




已閱讀5頁,還剩79頁未讀, 繼續(xù)免費閱讀
版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
目錄目錄 JAVA 線程 概念與原理線程 概念與原理 2 JAVA 線程 創(chuàng)建與啟動線程 創(chuàng)建與啟動 3 JAVA 線程 線程棧模型與線程的變量線程 線程棧模型與線程的變量 7 JAVA 線程 線程狀態(tài)線程 線程狀態(tài)的的轉換轉換 8 JAVA 線程 線程的同步與鎖線程 線程的同步與鎖 16 JAVA 線程 線程的交互線程 線程的交互 22 JAVA 線程 線程的調度線程 線程的調度 休眠休眠 27 JAVA 線程 線程的調度線程 線程的調度 優(yōu)先級優(yōu)先級 28 JAVA 線程 線程的調度線程 線程的調度 讓步讓步 30 JAVA 線程 線程的調度線程 線程的調度 合并合并 31 JAVA 線程 線程的調度線程 線程的調度 守護線程守護線程 33 JAVA 線程 線程的同步線程 線程的同步 同步方法同步方法 35 JAVA 線程 線程的同步線程 線程的同步 同步塊同步塊 39 JAVA 線程 并發(fā)協(xié)作線程 并發(fā)協(xié)作 生產者消費者模型生產者消費者模型 41 JAVA 線程 并發(fā)協(xié)作線程 并發(fā)協(xié)作 死鎖死鎖 45 JAVA 線程 新特征線程 新特征 線程池線程池 48 JAVA 線程 新特征線程 新特征 有返回值的線程有返回值的線程 54 JAVA 線程 新特征線程 新特征 鎖 上 鎖 上 56 JAVA 線程 新特征線程 新特征 鎖 下 鎖 下 59 JAVA 線程 新特征線程 新特征 信號量信號量 62 JAVA 線程 新特征線程 新特征 阻塞隊列阻塞隊列 65 JAVA 線程 新特征線程 新特征 阻塞棧阻塞棧 66 JAVA 線程 新特征線程 新特征 條件變量條件變量 68 JAVA 線程 新特征線程 新特征 原子量原子量 78 JAVA 線程 新特征線程 新特征 障礙器障礙器 81 JAVA 線程 大總結線程 大總結 83 Java 線程 概念與原理線程 概念與原理 一 操作系統(tǒng)中線程和進程的概念一 操作系統(tǒng)中線程和進程的概念 現(xiàn)在的操作系統(tǒng)是多任務操作系統(tǒng) 多線程是實現(xiàn)多任務的一種方式 進程是指一個內存中運行的應用程序 每個進程都有自己獨立的一塊內存空間 一個進程中可 以啟動多個線程 比如在 Windows 系統(tǒng)中 一個運行的 exe 就是一個進程 線程是指進程中的一個執(zhí)行流程 一個進程中可以運行多個線程 比如 java exe 進程中可以 運行很多線程 線程總是屬于某個進程 進程中的多個線程共享進程的內存 同時 執(zhí)行是人的感覺 在線程之間實際上輪換執(zhí)行 二 二 Java 中的線程中的線程 在 Java 中 線程 指兩件不同的事情 1 java lang Thread 類的一個實例 2 線程的執(zhí)行 使用 java lang Thread 類或者 java lang Runnable 接口編寫代碼來定義 實例化和啟動新 線程 一個 Thread 類實例只是一個對象 像 Java 中的任何其他對象一樣 具有變量和方法 生死 于堆上 Java 中 每個線程都有一個調用棧 即使不在程序中創(chuàng)建任何新的線程 線程也在后臺運行著 一個 Java 應用總是從 main 方法開始運行 mian 方法運行在一個線程內 它被稱為主線 程 一旦創(chuàng)建一個新的線程 就產生一個新的調用棧 線程總體分兩類 用戶線程和守候線程 當所有用戶線程執(zhí)行完畢的時候 JVM 自動關閉 但是守候線程卻不獨立于 JVM 守候線程一 般是由操作系統(tǒng)或者用戶自己創(chuàng)建的 Java 線程 創(chuàng)建與啟動 一 定義線程一 定義線程 1 擴展 java lang Thread 類 此類中有個 run 方法 應該注意其用法 public void run 如果該線程是使用獨立的 Runnable 運行對象構造的 則調用該 Runnable 對象的 run 方法 否則 該方法不執(zhí)行任何操作并返回 Thread 的子類應該重寫該方法 2 實現(xiàn) java lang Runnable 接口 void run 使用實現(xiàn)接口 Runnable 的對象創(chuàng)建一個線程時 啟動該線程將導致在獨立執(zhí)行的線 程中調用對象的 run 方法 方法 run 的常規(guī)協(xié)定是 它可能執(zhí)行任何所需的操作 二 實例化線程二 實例化線程 1 如果是擴展 java lang Thread 類的線程 則直接 new 即可 2 如果是實現(xiàn)了 java lang Runnable 接口的類 則用 Thread 的構造方法 Thread Runnable target Thread Runnable target String name Thread ThreadGroup group Runnable target Thread ThreadGroup group Runnable target String name Thread ThreadGroup group Runnable target String name long stackSize 三 啟動線程三 啟動線程 在線程的 Thread 對象上調用 start 方法 而不是 run 或者別的方法 在調用 start 方法之前 線程處于新狀態(tài)中 新狀態(tài)指有一個 Thread 對象 但還沒有一個真 正的線程 在調用 start 方法之后 發(fā)生了一系列復雜的事情 啟動新的執(zhí)行線程 具有新的調用棧 該線程從新狀態(tài)轉移到可運行狀態(tài) 當該線程獲得機會執(zhí)行時 其目標 run 方法將運行 注意 對 Java 來說 run 方法沒有任何特別之處 像 main 方法一樣 它只是新線程知道 調用的方法名稱 和簽名 因此 在 Runnable 上或者 Thread 上調用 run 方法是合法的 但 并不啟動新的線程 四 例子四 例子 1 實現(xiàn) Runnable 接口的多線程例子 實現(xiàn) Runnable 接口的類 author leizhimin 2008 9 13 18 12 10 public class DoSomething implements Runnable private String name public DoSomething String name this name name public void run for int i 0 i 5 i for long k 0 k 100000000 k System out println name i 測試 Runnable 類實現(xiàn)的多線程程序 author leizhimin 2008 9 13 18 15 02 public class TestRunnable public static void main String args DoSomething ds1 new DoSomething 阿三 DoSomething ds2 new DoSomething 李四 Thread t1 new Thread ds1 Thread t2 new Thread ds2 t1 start t2 start 執(zhí)行結果 李四 0 阿三 0 李四 1 阿三 1 李四 2 李四 3 阿三 2 李四 4 阿三 3 阿三 4 Process finished with exit code 0 2 擴展 Thread 類實現(xiàn)的多線程例子 測試擴展 Thread 類實現(xiàn)的多線程程序 author leizhimin 2008 9 13 18 22 13 public class TestThread extends Thread public TestThread String name super name public void run for int i 0 i 5 i for long k 0 k 100000000 k System out println this getName i public static void main String args Thread t1 new TestThread 阿三 Thread t2 new TestThread 李四 t1 start t2 start 執(zhí)行結果 阿三 0 李四 0 阿三 1 李四 1 阿三 2 李四 2 阿三 3 阿三 4 李四 3 李四 4 Process finished with exit code 0 對于上面的多線程程序代碼來說 輸出的結果是不確定的 其中的一條語句 for long k 0 k 100000000 k 是用來模擬一個非常耗時的操作的 五 一些常見問題五 一些常見問題 1 線程的名字 一個運行中的線程總是有名字的 名字有兩個來源 一個是虛擬機自己給的 名字 一個是你自己的定的名字 在沒有指定線程名字的情況下 虛擬機總會為線程指定名字 并且主線程的名字總是 mian 非主線程的名字不確定 2 線程都可以設置名字 也可以獲取線程的名字 連主線程也不例外 3 獲取當前線程的對象的方法是 Thread currentThread 4 在上面的代碼中 只能保證 每個線程都將啟動 每個線程都將運行直到完成 一系列線 程以某種順序啟動并不意味著將按該順序執(zhí)行 對于任何一組啟動的線程來說 調度程序不能 保證其執(zhí)行次序 持續(xù)時間也無法保證 5 當線程目標 run 方法結束時該線程完成 6 一旦線程啟動 它就永遠不能再重新啟動 只有一個新的線程可以被啟動 并且只能一次 一個可運行的線程或死線程可以被重新啟動 7 線程的調度是 JVM 的一部分 在一個 CPU 的機器上上 實際上一次只能運行一個線程 一次只有一個線程棧執(zhí)行 JVM 線程調度程序決定實際運行哪個處于可運行狀態(tài)的線程 眾多可運行線程中的某一個會被選中做為當前線程 可運行線程被選擇運行的順序是沒有保障 的 8 盡管通常采用隊列形式 但這是沒有保障的 隊列形式是指當一個線程完成 一輪 時 它 移到可運行隊列的尾部等待 直到它最終排隊到該隊列的前端為止 它才能被再次選中 事實 上 我們把它稱為可運行池而不是一個可運行隊列 目的是幫助認識線程并不都是以某種有保 障的順序排列唱呢個一個隊列的事實 9 盡管我們沒有無法控制線程調度程序 但可以通過別的方式來影響線程調度的方式 Java 線程 線程棧模型與線程的變量 要理解線程調度的原理 以及線程執(zhí)行過程 必須理解線程棧模型 線程棧是指某時刻時內存中線程調度的棧信息 當前調用的方法總是位于棧頂 線程棧的內容 是隨著程序的運行動態(tài)變化的 因此研究線程棧必須選擇一個運行的時刻 實際上指代碼運行 到什么地方 下面通過一個示例性的代碼說明線程 調用 棧的變化過程 這幅圖描述在代碼執(zhí)行到兩個不同時刻 1 2 時候 虛擬機線程調用棧示意圖 當程序執(zhí)行到 t start 時候 程序多出一個分支 增加了一個調用棧 B 這樣 棧 A 棧 B 并行執(zhí)行 從這里就可以看出方法調用和線程啟動的區(qū)別了 Java 線程 線程狀態(tài)的轉換線程 線程狀態(tài)的轉換 一 線程狀態(tài) 線程的狀態(tài)轉換是線程控制的基礎 線程狀態(tài)總的可分為五大狀態(tài) 分別是生 死 可運行 運行 等待 阻塞 用一個圖來描述如下 1 新狀態(tài) 線程對象已經創(chuàng)建 還沒有在其上調用 start 方法 2 可運行狀態(tài) 當線程有資格運行 但調度程序還沒有把它選定為運行線程時線程所處的狀 態(tài) 當 start 方法調用時 線程首先進入可運行狀態(tài) 在線程運行之后或者從阻塞 等待或睡 眠狀態(tài)回來后 也返回到可運行狀態(tài) 3 運行狀態(tài) 線程調度程序從可運行池中選擇一個線程作為當前線程時線程所處的狀態(tài) 這 也是線程進入運行狀態(tài)的唯一一種方式 4 等待 阻塞 睡眠狀態(tài) 這是線程有資格運行時它所處的狀態(tài) 實際上這個三狀態(tài)組合為一 種 其共同點是 線程仍舊是活的 但是當前沒有條件運行 換句話說 它是可運行的 但是 如果某件事件出現(xiàn) 他可能返回到可運行狀態(tài) 5 死亡態(tài) 當線程的 run 方法完成時就認為它死去 這個線程對象也許是活的 但是 它已 經不是一個單獨執(zhí)行的線程 線程一旦死亡 就不能復生 如果在一個死去的線程上調用 sta rt 方法 會拋出 java lang IllegalThreadStateException 異常 有關詳細狀態(tài)轉換圖可以參看本人的 Java 多線程編程總結 中的圖 二 阻止線程執(zhí)行 對于線程的阻止 考慮一下三個方面 不考慮 IO 阻塞的情況 睡眠 等待 因為需要一個對象的鎖定而被阻塞 1 睡眠 Thread sleep long millis 和 Thread sleep long millis int nanos 靜態(tài)方法強制當前正 在執(zhí)行的線程休眠 暫停執(zhí)行 以 減慢線程 當線程睡眠時 它入睡在某個地方 在蘇醒 之前不會返回到可運行狀態(tài) 當睡眠時間到期 則返回到可運行狀態(tài) 線程睡眠的原因 線程執(zhí)行太快 或者需要強制進入下一輪 因為 Java 規(guī)范不保證合理的輪 換 睡眠的實現(xiàn) 調用靜態(tài)方法 try Thread sleep 123 catch InterruptedException e e printStackTrace 睡眠的位置 為了讓其他線程有機會執(zhí)行 可以將 Thread sleep 的調用放線程 run 之內 這樣才能保證該線程執(zhí)行過程中會睡眠 例如 在前面的例子中 將一個耗時的操作改為睡眠 以減慢線程的執(zhí)行 可以這么寫 public void run for int i 0 i 5 i 很耗時的操作 用來減慢線程的執(zhí)行 for long k 0 k 100000000 k try Thread sleep 3 catch InterruptedException e e printStackTrace System out println this getName i 運行結果 阿三 0 李四 0 阿三 1 阿三 2 阿三 3 李四 1 李四 2 阿三 4 李四 3 李四 4 Process finished with exit code 0 這樣 線程在每次執(zhí)行過程中 總會睡眠 3 毫秒 睡眠了 其他的線程就有機會執(zhí)行了 注意 1 線程睡眠是幫助所有線程獲得運行機會的最好方法 2 線程睡眠到期自動蘇醒 并返回到可運行狀態(tài) 不是運行狀態(tài) sleep 中指定的時間是線 程不會運行的最短時間 因此 sleep 方法不能保證該線程睡眠到期后就開始執(zhí)行 3 sleep 是靜態(tài)方法 只能控制當前正在運行的線程 下面給個例子 一個計數(shù)器 計數(shù)到 100 在每個數(shù)字之間暫停 1 秒 每隔 10 個數(shù)字輸出一個字符串 author leizhimin 2008 9 14 9 53 49 public class MyThread extends Thread public void run for int i 0 i 100 i if i 10 0 System out println i System out print i try Thread sleep 1 System out print 線程睡眠 1 毫秒 n catch InterruptedException e e printStackTrace public static void main String args new MyThread start 0 0 線程睡眠 1 毫秒 1 線程睡眠 1 毫秒 2 線程睡眠 1 毫秒 3 線程睡眠 1 毫秒 4 線程睡眠 1 毫秒 5 線程睡眠 1 毫秒 6 線程睡眠 1 毫秒 7 線程睡眠 1 毫秒 8 線程睡眠 1 毫秒 9 線程睡眠 1 毫秒 10 10 線程睡眠 1 毫秒 11 線程睡眠 1 毫秒 12 線程睡眠 1 毫秒 13 線程睡眠 1 毫秒 14 線程睡眠 1 毫秒 15 線程睡眠 1 毫秒 16 線程睡眠 1 毫秒 17 線程睡眠 1 毫秒 18 線程睡眠 1 毫秒 19 線程睡眠 1 毫秒 20 20 線程睡眠 1 毫秒 21 線程睡眠 1 毫秒 22 線程睡眠 1 毫秒 23 線程睡眠 1 毫秒 24 線程睡眠 1 毫秒 25 線程睡眠 1 毫秒 26 線程睡眠 1 毫秒 27 線程睡眠 1 毫秒 28 線程睡眠 1 毫秒 29 線程睡眠 1 毫秒 30 30 線程睡眠 1 毫秒 31 線程睡眠 1 毫秒 32 線程睡眠 1 毫秒 33 線程睡眠 1 毫秒 34 線程睡眠 1 毫秒 35 線程睡眠 1 毫秒 36 線程睡眠 1 毫秒 37 線程睡眠 1 毫秒 38 線程睡眠 1 毫秒 39 線程睡眠 1 毫秒 40 40 線程睡眠 1 毫秒 41 線程睡眠 1 毫秒 42 線程睡眠 1 毫秒 43 線程睡眠 1 毫秒 44 線程睡眠 1 毫秒 45 線程睡眠 1 毫秒 46 線程睡眠 1 毫秒 47 線程睡眠 1 毫秒 48 線程睡眠 1 毫秒 49 線程睡眠 1 毫秒 50 50 線程睡眠 1 毫秒 51 線程睡眠 1 毫秒 52 線程睡眠 1 毫秒 53 線程睡眠 1 毫秒 54 線程睡眠 1 毫秒 55 線程睡眠 1 毫秒 56 線程睡眠 1 毫秒 57 線程睡眠 1 毫秒 58 線程睡眠 1 毫秒 59 線程睡眠 1 毫秒 60 60 線程睡眠 1 毫秒 61 線程睡眠 1 毫秒 62 線程睡眠 1 毫秒 63 線程睡眠 1 毫秒 64 線程睡眠 1 毫秒 65 線程睡眠 1 毫秒 66 線程睡眠 1 毫秒 67 線程睡眠 1 毫秒 68 線程睡眠 1 毫秒 69 線程睡眠 1 毫秒 70 70 線程睡眠 1 毫秒 71 線程睡眠 1 毫秒 72 線程睡眠 1 毫秒 73 線程睡眠 1 毫秒 74 線程睡眠 1 毫秒 75 線程睡眠 1 毫秒 76 線程睡眠 1 毫秒 77 線程睡眠 1 毫秒 78 線程睡眠 1 毫秒 79 線程睡眠 1 毫秒 80 80 線程睡眠 1 毫秒 81 線程睡眠 1 毫秒 82 線程睡眠 1 毫秒 83 線程睡眠 1 毫秒 84 線程睡眠 1 毫秒 85 線程睡眠 1 毫秒 86 線程睡眠 1 毫秒 87 線程睡眠 1 毫秒 88 線程睡眠 1 毫秒 89 線程睡眠 1 毫秒 90 90 線程睡眠 1 毫秒 91 線程睡眠 1 毫秒 92 線程睡眠 1 毫秒 93 線程睡眠 1 毫秒 94 線程睡眠 1 毫秒 95 線程睡眠 1 毫秒 96 線程睡眠 1 毫秒 97 線程睡眠 1 毫秒 98 線程睡眠 1 毫秒 99 線程睡眠 1 毫秒 Process finished with exit code 0 2 線程的優(yōu)先級和線程讓步 yield 線程的讓步是通過 Thread yield 來實現(xiàn)的 yield 方法的作用是 暫停當前正在執(zhí)行的線 程對象 并執(zhí)行其他線程 要理解 yield 必須了解線程的優(yōu)先級的概念 線程總是存在優(yōu)先級 優(yōu)先級范圍在 1 10 之間 JVM 線程調度程序是基于優(yōu)先級的搶先調度機制 在大多數(shù)情況下 當前運行的線程優(yōu) 先級將大于或等于線程池中任何線程的優(yōu)先級 但這僅僅是大多數(shù)情況 注意 當設計多線程應用程序的時候 一定不要依賴于線程的優(yōu)先級 因為線程調度優(yōu)先級操 作是沒有保障的 只能把線程優(yōu)先級作用作為一種提高程序效率的方法 但是要保證程序不依 賴這種操作 當線程池中線程都具有相同的優(yōu)先級 調度程序的 JVM 實現(xiàn)自由選擇它喜歡的線程 這時候調 度程序的操作有兩種可能 一是選擇一個線程運行 直到它阻塞或者運行完成為止 二是時間 分片 為池內的每個線程提供均等的運行機會 設置線程的優(yōu)先級 線程默認的優(yōu)先級是創(chuàng)建它的執(zhí)行線程的優(yōu)先級 可以通過 setPriority i nt newPriority 更改線程的優(yōu)先級 例如 Thread t new MyThread t setPriority 8 t start 線程優(yōu)先級為 1 10 之間的正整數(shù) JVM 從不會改變一個線程的優(yōu)先級 然而 1 10 之間的 值是沒有保證的 一些 JVM 可能不能識別 10 個不同的值 而將這些優(yōu)先級進行每兩個或多個 合并 變成少于 10 個的優(yōu)先級 則兩個或多個優(yōu)先級的線程可能被映射為一個優(yōu)先級 線程默認優(yōu)先級是 5 Thread 類中有三個常量 定義線程優(yōu)先級范圍 static int MAX PRIORITY 線程可以具有的最高優(yōu)先級 static int MIN PRIORITY 線程可以具有的最低優(yōu)先級 static int NORM PRIORITY 分配給線程的默認優(yōu)先級 3 Thread yield 方法 Thread yield 方法作用是 暫停當前正在執(zhí)行的線程對象 并執(zhí)行其他線程 yield 應該做的是讓當前運行線程回到可運行狀態(tài) 以允許具有相同優(yōu)先級的其他線程獲得運 行機會 因此 使用 yield 的目的是讓相同優(yōu)先級的線程之間能適當?shù)妮嗈D執(zhí)行 但是 實際 中無法保證 yield 達到讓步目的 因為讓步的線程還有可能被線程調度程序再次選中 結論 yield 從未導致線程轉到等待 睡眠 阻塞狀態(tài) 在大多數(shù)情況下 yield 將導致線程從 運行狀態(tài)轉到可運行狀態(tài) 但有可能沒有效果 4 join 方法 Thread 的非靜態(tài)方法 join 讓一個線程 B 加入 到另外一個線程 A 的尾部 在 A 執(zhí)行完畢之 前 B 不能工作 例如 Thread t new MyThread t start t join 另外 join 方法還有帶超時限制的重載版本 例如 t join 5000 則讓線程等待 5000 毫秒 如果超過這個時間 則停止等待 變?yōu)榭蛇\行狀態(tài) 線程的加入 join 對線程棧導致的結果是線程棧發(fā)生了變化 當然這些變化都是瞬時的 下面 給示意圖 小結 到目前位置 介紹了線程離開運行狀態(tài)的 3 種方法 1 調用 Thread sleep 使當前線程睡眠至少多少毫秒 盡管它可能在指定的時間之前被中 斷 2 調用 Thread yield 不能保障太多事情 盡管通常它會讓當前運行線程回到可運行性狀 態(tài) 使得有相同優(yōu)先級的線程有機會執(zhí)行 3 調用 join 方法 保證當前線程停止執(zhí)行 直到該線程所加入的線程完成為止 然而 如 果它加入的線程沒有存活 則當前線程不需要停止 除了以上三種方式外 還有下面幾種特殊情況可能使線程離開運行狀態(tài) 1 線程的 run 方法完成 2 在對象上調用 wait 方法 不是在線程上調用 3 線程不能在對象上獲得鎖定 它正試圖運行該對象的方法代碼 4 線程調度程序可以決定將當前運行狀態(tài)移動到可運行狀態(tài) 以便讓另一個線程獲得運行機 會 而不需要任何理由 Java 線程 線程的同步與鎖線程 線程的同步與鎖 一 同步問題提出一 同步問題提出 線程的同步是為了防止多個線程訪問一個數(shù)據(jù)對象時 對數(shù)據(jù)造成的破壞 例如 兩個線程 ThreadA ThreadB 都操作同一個對象 Foo 對象 并修改 Foo 對象上的數(shù)據(jù) public class Foo private int x 100 public int getX return x public int fix int y x x y return x public class MyRunnable implements Runnable private Foo foo new Foo public static void main String args MyRunnable r new MyRunnable Thread ta new Thread r Thread A Thread tb new Thread r Thread B ta start tb start public void run for int i 0 i 0 return String nameList remove 0 else return null public class Test public static void main String args final NameList nl new NameList nl add aaa class NameDropper extends Thread public void run String name nl removeFirst System out println name Thread t1 new NameDropper Thread t2 new NameDropper t1 start t2 start 雖然集合對象 private List nameList Collections synchronizedList new LinkedList 是同步的 但是程序還不是線程安全的 出現(xiàn)這種事件的原因是 上例中一個線程操作列表過程中無法阻止另外一個線程對列表的其他 操作 解決上面問題的辦法是 在操作集合對象的 NameList 上面做一個同步 改寫后的代碼如下 public class NameList private List nameList Collections synchronizedList new LinkedList public synchronized void add String name nameList add name public synchronized String removeFirst if nameList size 0 return String nameList remove 0 else return null 這樣 當一個線程訪問其中一個同步方法時 其他線程只有等待 七 線程死鎖七 線程死鎖 死鎖對 Java 程序來說 是很復雜的 也很難發(fā)現(xiàn)問題 當兩個線程被阻塞 每個線程在等待 另一個線程時就發(fā)生死鎖 還是看一個比較直觀的死鎖例子 public class DeadlockRisk private static class Resource public int value private Resource resourceA new Resource private Resource resourceB new Resource public int read synchronized resourceA synchronized resourceB return resourceB value resourceA value public void write int a int b synchronized resourceB synchronized resourceA resourceA value a resourceB value b 假設 read 方法由一個線程啟動 write 方法由另外一個線程啟動 讀線程將擁有 resource A 鎖 寫線程將擁有 resourceB 鎖 兩者都堅持等待的話就出現(xiàn)死鎖 實際上 上面這個例子發(fā)生死鎖的概率很小 因為在代碼內的某個點 CPU 必須從讀線程切換 到寫線程 所以 死鎖基本上不能發(fā)生 但是 無論代碼中發(fā)生死鎖的概率有多小 一旦發(fā)生死鎖 程序就死掉 有一些設計方法能幫 助避免死鎖 包括始終按照預定義的順序獲取鎖這一策略 已經超出 SCJP 的考試范圍 八 線程同步小結八 線程同步小結 1 線程同步的目的是為了保護多個線程反問一個資源時對資源的破壞 2 線程同步方法是通過鎖來實現(xiàn) 每個對象都有切僅有一個鎖 這個鎖與一個特定的對象關 聯(lián) 線程一旦獲取了對象鎖 其他訪問該對象的線程就無法再訪問該對象的其他同步方法 3 對于靜態(tài)同步方法 鎖是針對這個類的 鎖對象是該類的 Class 對象 靜態(tài)和非靜態(tài)方法 的鎖互不干預 一個線程獲得鎖 當在一個同步方法中訪問另外對象上的同步方法時 會獲取 這兩個對象鎖 4 對于同步 要時刻清醒在哪個對象上同步 這是關鍵 5 編寫線程安全的類 需要時刻注意對多個線程競爭訪問資源的邏輯和安全做出正確的判斷 對 原子 操作做出分析 并保證原子操作期間別的線程無法訪問競爭資源 6 當多個線程等待一個對象鎖時 沒有獲取到鎖的線程將發(fā)生阻塞 7 死鎖是線程間相互等待鎖鎖造成的 在實際中發(fā)生的概率非常的小 真讓你寫個死鎖程序 不一定好使 呵呵 但是 一旦程序發(fā)生死鎖 程序將死掉 Java 線程 線程的交互線程 線程的交互 線程交互是比較復雜的問題 SCJP 要求不很基礎 給定一個場景 編寫代碼來恰當使用等待 通知和通知所有線程 一 線程交互的基礎知識 SCJP 所要求的線程交互知識點需要從 java lang Object 的類的三個方法來學習 void notify 喚醒在此對象監(jiān)視器上等待的單個線程 void notifyAll 喚醒在此對象監(jiān)視器上等待的所有線程 void wait 導致當前的線程等待 直到其他線程調用此對象的 notify 方法或 notifyAll 方法 當然 wait 還有另外兩個重載方法 void wait long timeout 導致當前的線程等待 直到其他線程調用此對象的 notify 方法或 notifyAll 方法 或者超過指定的時間量 void wait long timeout int nanos 導致當前的線程等待 直到其他線程調用此對象的 notify 方法或 notifyAll 方法 或者其他某個線程中斷當前線程 或者已超過某個實際時間量 以上這些方法是幫助線程傳遞線程關心的時間狀態(tài) 關于等待 通知 要記住的關鍵點是 必須從同步環(huán)境內調用 wait notify notifyAll 方法 線程不能調用對象上等待或通知 的方法 除非它擁有那個對象的鎖 wait notify notifyAll 都是 Object 的實例方法 與每個對象具有鎖一樣 每個對象可 以有一個線程列表 他們等待來自該信號 通知 線程通過執(zhí)行對象上的 wait 方法獲得這 個等待列表 從那時候起 它不再執(zhí)行任何其他指令 直到調用對象的 notify 方法為止 如 果多個線程在同一個對象上等待 則將只選擇一個線程 不保證以何種順序 繼續(xù)執(zhí)行 如果 沒有線程等待 則不采取任何特殊操作 下面看個例子就明白了 計算輸出其他線程鎖計算的數(shù)據(jù) author leizhimin 2008 9 15 13 20 38 public class ThreadA public static void main String args ThreadB b new ThreadB 啟動計算線程 b start 線程 A 擁有 b 對象上的鎖 線程為了調用 wait 或 notify 方法 該線程必須是那個 對象鎖的擁有者 synchronized b try System out println 等待對象 b 完成計算 當前線程 A 等待 b wait catch InterruptedException e e printStackTrace System out println b 對象計算的總和是 b total 計算 1 2 3 100 的和 author leizhimin 2008 9 15 13 20 49 public class ThreadB extends Thread int total public void run synchronized this for int i 0 i 101 i total i 完成計算了 喚醒在此對象監(jiān)視器上等待的單個線程 在本例中線程 A 被喚醒 notify 等待對象 b 完成計算 b 對象計算的總和是 5050 Process finished with exit code 0 千萬注意 當在對象上調用 wait 方法時 執(zhí)行該代碼的線程立即放棄它在對象上的鎖 然而調用 notify 時 并不意味著這時線程會放棄其鎖 如果線程榮然在完成同步代碼 則線程在移出之前不 會放棄鎖 因此 只要調用 notify 并不意味著這時該鎖變得可用 二 多個線程在等待一個對象鎖時候使用 notifyAll 在多數(shù)情況下 最好通知等待某個對象的所有線程 如果這樣做 可以在對象上使用 notifyAll 讓所有在此對象上等待的線程沖出等待區(qū) 返回到可運行狀態(tài) 下面給個例子 計算線程 author leizhimin 2008 9 20 11 15 46 public class Calculator extends Thread int total public void run synchronized this for int i 0 i 101 i total i 通知所有在此對象上等待的線程 notifyAll 獲取計算結果并輸出 author leizhimin 2008 9 20 11 15 22 public class ReaderResult extends Thread Calculator c public ReaderResult Calculator c this c c public void run synchronized c try System out println Thread currentThread 等待計算結果 c wait catch InterruptedException e e printStackTrace System out println Thread currentThread 計算結果為 c t otal public static void main String args Calculator calculator new Calculator 啟動三個線程 分別獲取計算結果 new ReaderResult calculator start new ReaderResult calculator start new ReaderResult calculator start 啟動計算線程 calculator start 運行結果 Thread Thread 1 5 main 等待計算結果 Thread Thread 2 5 main 等待計算結果 Thread Thread 3 5 main 等待計算結果 Exception in thread Thread 0 java lang IllegalMonitorStateException current t hread not owner at java lang Object notifyAll Native Method at threadtest Calculator run Calculator java 18 Thread Thread 1 5 main 計算結果為 5050 Thread Thread 2 5 main 計算結果為 5050 Thread Thread 3 5 main 計算結果為 5050 Process finished with exit code 0 運行結果表明 程序中有異常 并且多次運行結果可能有多種輸出結果 這就是說明 這個多 線程的交互程序還存在問題 究竟是出了什么問題 需要深入的分析和思考 下面將做具體分 析 實際上 上面這個代碼中 我們期望的是讀取結果的線程在計算線程調用 notifyAll 之前等待 即可 但是 如果計算線程先執(zhí)行 并在讀取結果線程等待之前調用了 notify 方法 那么又 會發(fā)生什么呢 這種情況是可能發(fā)生的 因為無法保證線程的不同部分將按照什么順序來執(zhí)行 幸運的是當讀取線程運行時 它只能馬上進入等待狀態(tài) 它沒有做任何事情來檢查等待的 事件是否已經發(fā)生 因此 如果計算線程已經調用了 notifyAll 方法 那么它就不會再 次調用 notifyAll 并且等待的讀取線程將永遠保持等待 這當然是開發(fā)者所不愿意看到 的問題 因此 當?shù)却氖录l(fā)生時 需要能夠檢查 notifyAll 通知事件是否已經發(fā)生 通常 解決上面問題的最佳方式是將 Java 線程 線程的調度線程 線程的調度 休眠休眠 Java 線程調度是 Java 多線程的核心 只有良好的調度 才能充分發(fā)揮系統(tǒng)的性能 提高程序 的執(zhí)行效率 這里要明確的一點 不管程序員怎么編寫調度 只能最大限度的影響線程執(zhí)行的次序 而不能 做到精準控制 線程休眠的目的是使線程讓出 CPU 的最簡單的做法之一 線程休眠時候 會將 CPU 資源交給 其他線程 以便能輪換執(zhí)行 當休眠一定時間后 線程會蘇醒 進入準備狀態(tài)等待執(zhí)行 線程休眠的方法是 Thread sleep long millis 和 Thread sleep long millis int nanos 均為靜態(tài)方法 那調用 sleep 休眠的哪個線程呢 簡單說 哪個線程調用 sleep 就休眠哪 個線程 Java 線程 線程的調度 休眠 author leizhimin 2009 11 4 9 02 40 public class Test public static void main String args Thread t1 new MyThread1 Thread t2 new Thread new MyRunnable t1 start t2 start class MyThread1 extends Thread public void run for int i 0 i 3 i System out println 線程 1 第 i 次執(zhí)行 try Thread sleep 50 catch InterruptedException e e printStackTrace class MyRunnable implements Runnable public void run for int i 0 i 3 i System out println 線程 2 第 i 次執(zhí)行 try Thread sleep 50 catch InterruptedException e e printStackTrace 線程 2 第 0 次執(zhí)行 線程 1 第 0 次執(zhí)行 線程 1 第 1 次執(zhí)行 線程 2 第 1 次執(zhí)行 線程 1 第 2 次執(zhí)行 線程 2 第 2 次執(zhí)行 Process finished with exit code 0 從上面的結果輸出可以看出 無法精準保證線程執(zhí)行次序 Java 線程 線程的調度線程 線程的調度 優(yōu)先級優(yōu)先級 與線程休眠類似 線程的優(yōu)先級仍然無法保障線程的執(zhí)行次序 只不過 優(yōu)先級高的線程獲取 CPU 資源的概率較大 優(yōu)先級低的并非沒機會執(zhí)行 線程的優(yōu)先級用 1 10 之間的整數(shù)表示 數(shù)值越大優(yōu)先級越高 默認的優(yōu)先級為 5 在一個線程中開啟另外一個新線程 則新開線程稱為該線程的子線程 子線程初始優(yōu)先級與父 線程相同 Java 線程 線程的調度 優(yōu)先級 author leizhimin 2009 11 4 9 02 40 public class Test public static void main String args Thread t1 new MyThread1 Thread t2 new Thread new MyRunnable t1 setPriority 10 t2 setPriority 1 t2 start t1 start class MyThread1 extends Thread public void run for int i 0 i 10 i System out println 線程 1 第 i 次執(zhí)行 try Thread sleep 100 catch InterruptedException e e printStackTrace class MyRunnable implements Runnable public void run for int i 0 i 10 i System out println 線程 2 第 i 次執(zhí)行 try Thread sleep 100 catch InterruptedException e e printStackTrace 線程 1 第 0 次執(zhí)行 線程 2 第 0 次執(zhí)行 線程 2 第 1 次執(zhí)行 線程 1 第 1 次執(zhí)行 線程 2 第 2 次執(zhí)行 線程 1 第 2 次執(zhí)行 線程 1 第 3 次執(zhí)行 線程 2 第 3 次執(zhí)行 線程 2 第 4 次執(zhí)行 線程 1 第 4 次執(zhí)行 線程 1 第 5 次執(zhí)行 線程 2 第 5 次執(zhí)行 線程 1 第 6 次執(zhí)行 線程 2 第 6 次執(zhí)行 線程 1 第 7 次執(zhí)行 線程 2 第 7 次執(zhí)行 線程 1 第 8 次執(zhí)行 線程 2 第 8 次執(zhí)行 線程 1 第 9 次執(zhí)行 線程 2 第 9 次執(zhí)行 Process finished with exit code 0 Java 線程 線程的調度線程 線程的調度 讓步讓步 線程的讓步含義就是使當前運行著線程讓出 CPU 資源 但是然給誰不知道 僅僅是讓出 線 程狀態(tài)回到可運行狀態(tài) 線程的讓步使用 Thread yield 方法 yield 為靜態(tài)方法 功能是暫停當前正在執(zhí)行的線程 對象 并執(zhí)行其他線程 Java 線程 線程的調度 讓步 author leizhimin 2009 11 4 9 02 40 public class Test public static void main String args Thread t1 new MyThread1 Thread t2 new Thread new MyRunnable t2 start t1 start class MyThread1 extends Thread public void run for int i 0 i 10 i System out println 線程 1 第 i 次執(zhí)行 class MyRunnable implements Runnable public void run for int i 0 i 10 i System out println 線程 2 第 i 次執(zhí)行 Thread yield 線程 2 第 0 次執(zhí)行 線程 2 第 1 次執(zhí)行 線程 2 第 2 次執(zhí)行 線程 2 第 3 次執(zhí)行 線程 1 第 0 次執(zhí)行 線程 1 第 1 次執(zhí)行 線程 1 第 2 次執(zhí)行 線程 1 第 3 次執(zhí)行 線程 1 第 4 次執(zhí)行 線程 1 第 5 次執(zhí)行 線程 1 第 6 次執(zhí)行 線程 1 第 7 次執(zhí)行 線程 1 第 8 次執(zhí)行 線程 1 第 9 次執(zhí)行 線程 2 第 4 次執(zhí)行 線程 2 第 5 次執(zhí)行 線程 2 第 6 次執(zhí)行 線程 2 第 7 次執(zhí)行 線程 2 第 8 次執(zhí)行 線程 2 第 9 次執(zhí)行 Process finished with exit code 0 Java 線程 線程的調度線程 線程的調度 合并合并 線程的合并的含義就是將幾個并行線程的線程合并為一個單線程執(zhí)行 應用場景是當一個線程 必須等待另一個線程執(zhí)行完畢才能執(zhí)行時可以使用 join 方法 join 為非靜態(tài)方法 定義如下 void join 等待該線程終止 void join long millis 等待該線程終止的時間最長為 millis 毫秒 void join long
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年汽車制造財務經理工作計劃
- 餐飲業(yè)餐廳員工勞動合同簽訂與保密協(xié)議
- 幼兒園安全硬件設施維護管理培訓心得體會
- 城市綠化帶拆遷補償協(xié)議示范文本
- 供應鏈企業(yè)廠長供應鏈風險管理聘用合同
- 休閑餐飲品牌招商加盟服務合同
- 2025至2030年中國針織高彈手套市場分析及競爭策略研究報告
- 三基三嚴品牌建設培訓計劃
- 初中生心理健康輔導計劃
- 2025至2030年中國防水數(shù)碼地板市場分析及競爭策略研究報告
- 航行通告教學課件
- 2023年護理考試-外科護理(副高)歷年考試真題試卷摘選答案
- 2022年廣東高考成績一分一段表重磅出爐
- 新版病人搬運(輪椅)操作評分標準
- 重癥監(jiān)護ICU護理實習生出科考試試題及答案
- GB/Z 22074-2008塑料外殼式斷路器可靠性試驗方法
- GB/T 32360-2015超濾膜測試方法
- 中藥學全套(完整版)課件
- 工程施工停止點檢查表
- 國開專科《外國文學》十年期末考試題庫及答案
- 《滅火器維修》GA95-2015(全文)
評論
0/150
提交評論