




已閱讀5頁,還剩79頁未讀, 繼續(xù)免費閱讀
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
目錄目錄 JAVA 線程 概念與原理線程 概念與原理 2 JAVA 線程 創(chuàng)建與啟動線程 創(chuàng)建與啟動 3 JAVA 線程 線程棧模型與線程的變量線程 線程棧模型與線程的變量 7 JAVA 線程 線程狀態(tài)線程 線程狀態(tài)的的轉(zhuǎn)換轉(zhuǎn)換 8 JAVA 線程 線程的同步與鎖線程 線程的同步與鎖 16 JAVA 線程 線程的交互線程 線程的交互 22 JAVA 線程 線程的調(diào)度線程 線程的調(diào)度 休眠休眠 27 JAVA 線程 線程的調(diào)度線程 線程的調(diào)度 優(yōu)先級優(yōu)先級 28 JAVA 線程 線程的調(diào)度線程 線程的調(diào)度 讓步讓步 30 JAVA 線程 線程的調(diào)度線程 線程的調(diào)度 合并合并 31 JAVA 線程 線程的調(diào)度線程 線程的調(diào)度 守護(hù)線程守護(hù)線程 33 JAVA 線程 線程的同步線程 線程的同步 同步方法同步方法 35 JAVA 線程 線程的同步線程 線程的同步 同步塊同步塊 39 JAVA 線程 并發(fā)協(xié)作線程 并發(fā)協(xié)作 生產(chǎn)者消費者模型生產(chǎn)者消費者模型 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 線程 大總結(jié)線程 大總結(jié) 83 Java 線程 概念與原理線程 概念與原理 一 操作系統(tǒng)中線程和進(jìn)程的概念一 操作系統(tǒng)中線程和進(jìn)程的概念 現(xiàn)在的操作系統(tǒng)是多任務(wù)操作系統(tǒng) 多線程是實現(xiàn)多任務(wù)的一種方式 進(jìn)程是指一個內(nèi)存中運行的應(yīng)用程序 每個進(jìn)程都有自己獨立的一塊內(nèi)存空間 一個進(jìn)程中可 以啟動多個線程 比如在 Windows 系統(tǒng)中 一個運行的 exe 就是一個進(jìn)程 線程是指進(jìn)程中的一個執(zhí)行流程 一個進(jìn)程中可以運行多個線程 比如 java exe 進(jìn)程中可以 運行很多線程 線程總是屬于某個進(jìn)程 進(jìn)程中的多個線程共享進(jìn)程的內(nèi)存 同時 執(zhí)行是人的感覺 在線程之間實際上輪換執(zhí)行 二 二 Java 中的線程中的線程 在 Java 中 線程 指兩件不同的事情 1 java lang Thread 類的一個實例 2 線程的執(zhí)行 使用 java lang Thread 類或者 java lang Runnable 接口編寫代碼來定義 實例化和啟動新 線程 一個 Thread 類實例只是一個對象 像 Java 中的任何其他對象一樣 具有變量和方法 生死 于堆上 Java 中 每個線程都有一個調(diào)用棧 即使不在程序中創(chuàng)建任何新的線程 線程也在后臺運行著 一個 Java 應(yīng)用總是從 main 方法開始運行 mian 方法運行在一個線程內(nèi) 它被稱為主線 程 一旦創(chuàng)建一個新的線程 就產(chǎn)生一個新的調(diào)用棧 線程總體分兩類 用戶線程和守候線程 當(dāng)所有用戶線程執(zhí)行完畢的時候 JVM 自動關(guān)閉 但是守候線程卻不獨立于 JVM 守候線程一 般是由操作系統(tǒng)或者用戶自己創(chuàng)建的 Java 線程 創(chuàng)建與啟動 一 定義線程一 定義線程 1 擴(kuò)展 java lang Thread 類 此類中有個 run 方法 應(yīng)該注意其用法 public void run 如果該線程是使用獨立的 Runnable 運行對象構(gòu)造的 則調(diào)用該 Runnable 對象的 run 方法 否則 該方法不執(zhí)行任何操作并返回 Thread 的子類應(yīng)該重寫該方法 2 實現(xiàn) java lang Runnable 接口 void run 使用實現(xiàn)接口 Runnable 的對象創(chuàng)建一個線程時 啟動該線程將導(dǎo)致在獨立執(zhí)行的線 程中調(diào)用對象的 run 方法 方法 run 的常規(guī)協(xié)定是 它可能執(zhí)行任何所需的操作 二 實例化線程二 實例化線程 1 如果是擴(kuò)展 java lang Thread 類的線程 則直接 new 即可 2 如果是實現(xiàn)了 java lang Runnable 接口的類 則用 Thread 的構(gòu)造方法 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 對象上調(diào)用 start 方法 而不是 run 或者別的方法 在調(diào)用 start 方法之前 線程處于新狀態(tài)中 新狀態(tài)指有一個 Thread 對象 但還沒有一個真 正的線程 在調(diào)用 start 方法之后 發(fā)生了一系列復(fù)雜的事情 啟動新的執(zhí)行線程 具有新的調(diào)用棧 該線程從新狀態(tài)轉(zhuǎn)移到可運行狀態(tài) 當(dāng)該線程獲得機(jī)會執(zhí)行時 其目標(biāo) run 方法將運行 注意 對 Java 來說 run 方法沒有任何特別之處 像 main 方法一樣 它只是新線程知道 調(diào)用的方法名稱 和簽名 因此 在 Runnable 上或者 Thread 上調(diào)用 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í)行結(jié)果 李四 0 阿三 0 李四 1 阿三 1 李四 2 李四 3 阿三 2 李四 4 阿三 3 阿三 4 Process finished with exit code 0 2 擴(kuò)展 Thread 類實現(xiàn)的多線程例子 測試擴(kuò)展 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í)行結(jié)果 阿三 0 李四 0 阿三 1 李四 1 阿三 2 李四 2 阿三 3 阿三 4 李四 3 李四 4 Process finished with exit code 0 對于上面的多線程程序代碼來說 輸出的結(jié)果是不確定的 其中的一條語句 for long k 0 k 100000000 k 是用來模擬一個非常耗時的操作的 五 一些常見問題五 一些常見問題 1 線程的名字 一個運行中的線程總是有名字的 名字有兩個來源 一個是虛擬機(jī)自己給的 名字 一個是你自己的定的名字 在沒有指定線程名字的情況下 虛擬機(jī)總會為線程指定名字 并且主線程的名字總是 mian 非主線程的名字不確定 2 線程都可以設(shè)置名字 也可以獲取線程的名字 連主線程也不例外 3 獲取當(dāng)前線程的對象的方法是 Thread currentThread 4 在上面的代碼中 只能保證 每個線程都將啟動 每個線程都將運行直到完成 一系列線 程以某種順序啟動并不意味著將按該順序執(zhí)行 對于任何一組啟動的線程來說 調(diào)度程序不能 保證其執(zhí)行次序 持續(xù)時間也無法保證 5 當(dāng)線程目標(biāo) run 方法結(jié)束時該線程完成 6 一旦線程啟動 它就永遠(yuǎn)不能再重新啟動 只有一個新的線程可以被啟動 并且只能一次 一個可運行的線程或死線程可以被重新啟動 7 線程的調(diào)度是 JVM 的一部分 在一個 CPU 的機(jī)器上上 實際上一次只能運行一個線程 一次只有一個線程棧執(zhí)行 JVM 線程調(diào)度程序決定實際運行哪個處于可運行狀態(tài)的線程 眾多可運行線程中的某一個會被選中做為當(dāng)前線程 可運行線程被選擇運行的順序是沒有保障 的 8 盡管通常采用隊列形式 但這是沒有保障的 隊列形式是指當(dāng)一個線程完成 一輪 時 它 移到可運行隊列的尾部等待 直到它最終排隊到該隊列的前端為止 它才能被再次選中 事實 上 我們把它稱為可運行池而不是一個可運行隊列 目的是幫助認(rèn)識線程并不都是以某種有保 障的順序排列唱呢個一個隊列的事實 9 盡管我們沒有無法控制線程調(diào)度程序 但可以通過別的方式來影響線程調(diào)度的方式 Java 線程 線程棧模型與線程的變量 要理解線程調(diào)度的原理 以及線程執(zhí)行過程 必須理解線程棧模型 線程棧是指某時刻時內(nèi)存中線程調(diào)度的棧信息 當(dāng)前調(diào)用的方法總是位于棧頂 線程棧的內(nèi)容 是隨著程序的運行動態(tài)變化的 因此研究線程棧必須選擇一個運行的時刻 實際上指代碼運行 到什么地方 下面通過一個示例性的代碼說明線程 調(diào)用 棧的變化過程 這幅圖描述在代碼執(zhí)行到兩個不同時刻 1 2 時候 虛擬機(jī)線程調(diào)用棧示意圖 當(dāng)程序執(zhí)行到 t start 時候 程序多出一個分支 增加了一個調(diào)用棧 B 這樣 棧 A 棧 B 并行執(zhí)行 從這里就可以看出方法調(diào)用和線程啟動的區(qū)別了 Java 線程 線程狀態(tài)的轉(zhuǎn)換線程 線程狀態(tài)的轉(zhuǎn)換 一 線程狀態(tài) 線程的狀態(tài)轉(zhuǎn)換是線程控制的基礎(chǔ) 線程狀態(tài)總的可分為五大狀態(tài) 分別是生 死 可運行 運行 等待 阻塞 用一個圖來描述如下 1 新狀態(tài) 線程對象已經(jīng)創(chuàng)建 還沒有在其上調(diào)用 start 方法 2 可運行狀態(tài) 當(dāng)線程有資格運行 但調(diào)度程序還沒有把它選定為運行線程時線程所處的狀 態(tài) 當(dāng) start 方法調(diào)用時 線程首先進(jìn)入可運行狀態(tài) 在線程運行之后或者從阻塞 等待或睡 眠狀態(tài)回來后 也返回到可運行狀態(tài) 3 運行狀態(tài) 線程調(diào)度程序從可運行池中選擇一個線程作為當(dāng)前線程時線程所處的狀態(tài) 這 也是線程進(jìn)入運行狀態(tài)的唯一一種方式 4 等待 阻塞 睡眠狀態(tài) 這是線程有資格運行時它所處的狀態(tài) 實際上這個三狀態(tài)組合為一 種 其共同點是 線程仍舊是活的 但是當(dāng)前沒有條件運行 換句話說 它是可運行的 但是 如果某件事件出現(xiàn) 他可能返回到可運行狀態(tài) 5 死亡態(tài) 當(dāng)線程的 run 方法完成時就認(rèn)為它死去 這個線程對象也許是活的 但是 它已 經(jīng)不是一個單獨執(zhí)行的線程 線程一旦死亡 就不能復(fù)生 如果在一個死去的線程上調(diào)用 sta rt 方法 會拋出 java lang IllegalThreadStateException 異常 有關(guān)詳細(xì)狀態(tài)轉(zhuǎn)換圖可以參看本人的 Java 多線程編程總結(jié) 中的圖 二 阻止線程執(zhí)行 對于線程的阻止 考慮一下三個方面 不考慮 IO 阻塞的情況 睡眠 等待 因為需要一個對象的鎖定而被阻塞 1 睡眠 Thread sleep long millis 和 Thread sleep long millis int nanos 靜態(tài)方法強(qiáng)制當(dāng)前正 在執(zhí)行的線程休眠 暫停執(zhí)行 以 減慢線程 當(dāng)線程睡眠時 它入睡在某個地方 在蘇醒 之前不會返回到可運行狀態(tài) 當(dāng)睡眠時間到期 則返回到可運行狀態(tài) 線程睡眠的原因 線程執(zhí)行太快 或者需要強(qiáng)制進(jìn)入下一輪 因為 Java 規(guī)范不保證合理的輪 換 睡眠的實現(xiàn) 調(diào)用靜態(tài)方法 try Thread sleep 123 catch InterruptedException e e printStackTrace 睡眠的位置 為了讓其他線程有機(jī)會執(zhí)行 可以將 Thread sleep 的調(diào)用放線程 run 之內(nèi) 這樣才能保證該線程執(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 運行結(jié)果 阿三 0 李四 0 阿三 1 阿三 2 阿三 3 李四 1 李四 2 阿三 4 李四 3 李四 4 Process finished with exit code 0 這樣 線程在每次執(zhí)行過程中 總會睡眠 3 毫秒 睡眠了 其他的線程就有機(jī)會執(zhí)行了 注意 1 線程睡眠是幫助所有線程獲得運行機(jī)會的最好方法 2 線程睡眠到期自動蘇醒 并返回到可運行狀態(tài) 不是運行狀態(tài) sleep 中指定的時間是線 程不會運行的最短時間 因此 sleep 方法不能保證該線程睡眠到期后就開始執(zhí)行 3 sleep 是靜態(tài)方法 只能控制當(dāng)前正在運行的線程 下面給個例子 一個計數(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 方法的作用是 暫停當(dāng)前正在執(zhí)行的線 程對象 并執(zhí)行其他線程 要理解 yield 必須了解線程的優(yōu)先級的概念 線程總是存在優(yōu)先級 優(yōu)先級范圍在 1 10 之間 JVM 線程調(diào)度程序是基于優(yōu)先級的搶先調(diào)度機(jī)制 在大多數(shù)情況下 當(dāng)前運行的線程優(yōu) 先級將大于或等于線程池中任何線程的優(yōu)先級 但這僅僅是大多數(shù)情況 注意 當(dāng)設(shè)計多線程應(yīng)用程序的時候 一定不要依賴于線程的優(yōu)先級 因為線程調(diào)度優(yōu)先級操 作是沒有保障的 只能把線程優(yōu)先級作用作為一種提高程序效率的方法 但是要保證程序不依 賴這種操作 當(dāng)線程池中線程都具有相同的優(yōu)先級 調(diào)度程序的 JVM 實現(xiàn)自由選擇它喜歡的線程 這時候調(diào) 度程序的操作有兩種可能 一是選擇一個線程運行 直到它阻塞或者運行完成為止 二是時間 分片 為池內(nèi)的每個線程提供均等的運行機(jī)會 設(shè)置線程的優(yōu)先級 線程默認(rèn)的優(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)先級進(jìn)行每兩個或多個 合并 變成少于 10 個的優(yōu)先級 則兩個或多個優(yōu)先級的線程可能被映射為一個優(yōu)先級 線程默認(rèn)優(yōu)先級是 5 Thread 類中有三個常量 定義線程優(yōu)先級范圍 static int MAX PRIORITY 線程可以具有的最高優(yōu)先級 static int MIN PRIORITY 線程可以具有的最低優(yōu)先級 static int NORM PRIORITY 分配給線程的默認(rèn)優(yōu)先級 3 Thread yield 方法 Thread yield 方法作用是 暫停當(dāng)前正在執(zhí)行的線程對象 并執(zhí)行其他線程 yield 應(yīng)該做的是讓當(dāng)前運行線程回到可運行狀態(tài) 以允許具有相同優(yōu)先級的其他線程獲得運 行機(jī)會 因此 使用 yield 的目的是讓相同優(yōu)先級的線程之間能適當(dāng)?shù)妮嗈D(zhuǎn)執(zhí)行 但是 實際 中無法保證 yield 達(dá)到讓步目的 因為讓步的線程還有可能被線程調(diào)度程序再次選中 結(jié)論 yield 從未導(dǎo)致線程轉(zhuǎn)到等待 睡眠 阻塞狀態(tài) 在大多數(shù)情況下 yield 將導(dǎo)致線程從 運行狀態(tài)轉(zhuǎn)到可運行狀態(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 對線程棧導(dǎo)致的結(jié)果是線程棧發(fā)生了變化 當(dāng)然這些變化都是瞬時的 下面 給示意圖 小結(jié) 到目前位置 介紹了線程離開運行狀態(tài)的 3 種方法 1 調(diào)用 Thread sleep 使當(dāng)前線程睡眠至少多少毫秒 盡管它可能在指定的時間之前被中 斷 2 調(diào)用 Thread yield 不能保障太多事情 盡管通常它會讓當(dāng)前運行線程回到可運行性狀 態(tài) 使得有相同優(yōu)先級的線程有機(jī)會執(zhí)行 3 調(diào)用 join 方法 保證當(dāng)前線程停止執(zhí)行 直到該線程所加入的線程完成為止 然而 如 果它加入的線程沒有存活 則當(dāng)前線程不需要停止 除了以上三種方式外 還有下面幾種特殊情況可能使線程離開運行狀態(tài) 1 線程的 run 方法完成 2 在對象上調(diào)用 wait 方法 不是在線程上調(diào)用 3 線程不能在對象上獲得鎖定 它正試圖運行該對象的方法代碼 4 線程調(diào)度程序可以決定將當(dāng)前運行狀態(tài)移動到可運行狀態(tài) 以便讓另一個線程獲得運行機(jī) 會 而不需要任何理由 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 這樣 當(dāng)一個線程訪問其中一個同步方法時 其他線程只有等待 七 線程死鎖七 線程死鎖 死鎖對 Java 程序來說 是很復(fù)雜的 也很難發(fā)現(xiàn)問題 當(dāng)兩個線程被阻塞 每個線程在等待 另一個線程時就發(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 假設(shè) read 方法由一個線程啟動 write 方法由另外一個線程啟動 讀線程將擁有 resource A 鎖 寫線程將擁有 resourceB 鎖 兩者都堅持等待的話就出現(xiàn)死鎖 實際上 上面這個例子發(fā)生死鎖的概率很小 因為在代碼內(nèi)的某個點 CPU 必須從讀線程切換 到寫線程 所以 死鎖基本上不能發(fā)生 但是 無論代碼中發(fā)生死鎖的概率有多小 一旦發(fā)生死鎖 程序就死掉 有一些設(shè)計方法能幫 助避免死鎖 包括始終按照預(yù)定義的順序獲取鎖這一策略 已經(jīng)超出 SCJP 的考試范圍 八 線程同步小結(jié)八 線程同步小結(jié) 1 線程同步的目的是為了保護(hù)多個線程反問一個資源時對資源的破壞 2 線程同步方法是通過鎖來實現(xiàn) 每個對象都有切僅有一個鎖 這個鎖與一個特定的對象關(guān) 聯(lián) 線程一旦獲取了對象鎖 其他訪問該對象的線程就無法再訪問該對象的其他同步方法 3 對于靜態(tài)同步方法 鎖是針對這個類的 鎖對象是該類的 Class 對象 靜態(tài)和非靜態(tài)方法 的鎖互不干預(yù) 一個線程獲得鎖 當(dāng)在一個同步方法中訪問另外對象上的同步方法時 會獲取 這兩個對象鎖 4 對于同步 要時刻清醒在哪個對象上同步 這是關(guān)鍵 5 編寫線程安全的類 需要時刻注意對多個線程競爭訪問資源的邏輯和安全做出正確的判斷 對 原子 操作做出分析 并保證原子操作期間別的線程無法訪問競爭資源 6 當(dāng)多個線程等待一個對象鎖時 沒有獲取到鎖的線程將發(fā)生阻塞 7 死鎖是線程間相互等待鎖鎖造成的 在實際中發(fā)生的概率非常的小 真讓你寫個死鎖程序 不一定好使 呵呵 但是 一旦程序發(fā)生死鎖 程序?qū)⑺赖?Java 線程 線程的交互線程 線程的交互 線程交互是比較復(fù)雜的問題 SCJP 要求不很基礎(chǔ) 給定一個場景 編寫代碼來恰當(dāng)使用等待 通知和通知所有線程 一 線程交互的基礎(chǔ)知識 SCJP 所要求的線程交互知識點需要從 java lang Object 的類的三個方法來學(xué)習(xí) void notify 喚醒在此對象監(jiān)視器上等待的單個線程 void notifyAll 喚醒在此對象監(jiān)視器上等待的所有線程 void wait 導(dǎo)致當(dāng)前的線程等待 直到其他線程調(diào)用此對象的 notify 方法或 notifyAll 方法 當(dāng)然 wait 還有另外兩個重載方法 void wait long timeout 導(dǎo)致當(dāng)前的線程等待 直到其他線程調(diào)用此對象的 notify 方法或 notifyAll 方法 或者超過指定的時間量 void wait long timeout int nanos 導(dǎo)致當(dāng)前的線程等待 直到其他線程調(diào)用此對象的 notify 方法或 notifyAll 方法 或者其他某個線程中斷當(dāng)前線程 或者已超過某個實際時間量 以上這些方法是幫助線程傳遞線程關(guān)心的時間狀態(tài) 關(guān)于等待 通知 要記住的關(guān)鍵點是 必須從同步環(huán)境內(nèi)調(diào)用 wait notify notifyAll 方法 線程不能調(diào)用對象上等待或通知 的方法 除非它擁有那個對象的鎖 wait notify notifyAll 都是 Object 的實例方法 與每個對象具有鎖一樣 每個對象可 以有一個線程列表 他們等待來自該信號 通知 線程通過執(zhí)行對象上的 wait 方法獲得這 個等待列表 從那時候起 它不再執(zhí)行任何其他指令 直到調(diào)用對象的 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 對象上的鎖 線程為了調(diào)用 wait 或 notify 方法 該線程必須是那個 對象鎖的擁有者 synchronized b try System out println 等待對象 b 完成計算 當(dāng)前線程 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 千萬注意 當(dāng)在對象上調(diào)用 wait 方法時 執(zhí)行該代碼的線程立即放棄它在對象上的鎖 然而調(diào)用 notify 時 并不意味著這時線程會放棄其鎖 如果線程榮然在完成同步代碼 則線程在移出之前不 會放棄鎖 因此 只要調(diào)用 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 獲取計算結(jié)果并輸出 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 等待計算結(jié)果 c wait catch InterruptedException e e printStackTrace System out println Thread currentThread 計算結(jié)果為 c t otal public static void main String args Calculator calculator new Calculator 啟動三個線程 分別獲取計算結(jié)果 new ReaderResult calculator start new ReaderResult calculator start new ReaderResult calculator start 啟動計算線程 calculator start 運行結(jié)果 Thread Thread 1 5 main 等待計算結(jié)果 Thread Thread 2 5 main 等待計算結(jié)果 Thread Thread 3 5 main 等待計算結(jié)果 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 計算結(jié)果為 5050 Thread Thread 2 5 main 計算結(jié)果為 5050 Thread Thread 3 5 main 計算結(jié)果為 5050 Process finished with exit code 0 運行結(jié)果表明 程序中有異常 并且多次運行結(jié)果可能有多種輸出結(jié)果 這就是說明 這個多 線程的交互程序還存在問題 究竟是出了什么問題 需要深入的分析和思考 下面將做具體分 析 實際上 上面這個代碼中 我們期望的是讀取結(jié)果的線程在計算線程調(diào)用 notifyAll 之前等待 即可 但是 如果計算線程先執(zhí)行 并在讀取結(jié)果線程等待之前調(diào)用了 notify 方法 那么又 會發(fā)生什么呢 這種情況是可能發(fā)生的 因為無法保證線程的不同部分將按照什么順序來執(zhí)行 幸運的是當(dāng)讀取線程運行時 它只能馬上進(jìn)入等待狀態(tài) 它沒有做任何事情來檢查等待的 事件是否已經(jīng)發(fā)生 因此 如果計算線程已經(jīng)調(diào)用了 notifyAll 方法 那么它就不會再 次調(diào)用 notifyAll 并且等待的讀取線程將永遠(yuǎn)保持等待 這當(dāng)然是開發(fā)者所不愿意看到 的問題 因此 當(dāng)?shù)却氖录l(fā)生時 需要能夠檢查 notifyAll 通知事件是否已經(jīng)發(fā)生 通常 解決上面問題的最佳方式是將 Java 線程 線程的調(diào)度線程 線程的調(diào)度 休眠休眠 Java 線程調(diào)度是 Java 多線程的核心 只有良好的調(diào)度 才能充分發(fā)揮系統(tǒng)的性能 提高程序 的執(zhí)行效率 這里要明確的一點 不管程序員怎么編寫調(diào)度 只能最大限度的影響線程執(zhí)行的次序 而不能 做到精準(zhǔn)控制 線程休眠的目的是使線程讓出 CPU 的最簡單的做法之一 線程休眠時候 會將 CPU 資源交給 其他線程 以便能輪換執(zhí)行 當(dāng)休眠一定時間后 線程會蘇醒 進(jìn)入準(zhǔn)備狀態(tài)等待執(zhí)行 線程休眠的方法是 Thread sleep long millis 和 Thread sleep long millis int nanos 均為靜態(tài)方法 那調(diào)用 sleep 休眠的哪個線程呢 簡單說 哪個線程調(diào)用 sleep 就休眠哪 個線程 Java 線程 線程的調(diào)度 休眠 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 從上面的結(jié)果輸出可以看出 無法精準(zhǔn)保證線程執(zhí)行次序 Java 線程 線程的調(diào)度線程 線程的調(diào)度 優(yōu)先級優(yōu)先級 與線程休眠類似 線程的優(yōu)先級仍然無法保障線程的執(zhí)行次序 只不過 優(yōu)先級高的線程獲取 CPU 資源的概率較大 優(yōu)先級低的并非沒機(jī)會執(zhí)行 線程的優(yōu)先級用 1 10 之間的整數(shù)表示 數(shù)值越大優(yōu)先級越高 默認(rèn)的優(yōu)先級為 5 在一個線程中開啟另外一個新線程 則新開線程稱為該線程的子線程 子線程初始優(yōu)先級與父 線程相同 Java 線程 線程的調(diào)度 優(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 線程 線程的調(diào)度線程 線程的調(diào)度 讓步讓步 線程的讓步含義就是使當(dāng)前運行著線程讓出 CPU 資源 但是然給誰不知道 僅僅是讓出 線 程狀態(tài)回到可運行狀態(tài) 線程的讓步使用 Thread yield 方法 yield 為靜態(tài)方法 功能是暫停當(dāng)前正在執(zhí)行的線程 對象 并執(zhí)行其他線程 Java 線程 線程的調(diào)度 讓步 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 線程 線程的調(diào)度線程 線程的調(diào)度 合并合并 線程的合并的含義就是將幾個并行線程的線程合并為一個單線程執(zhí)行 應(yīng)用場景是當(dāng)一個線程 必須等待另一個線程執(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)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 公司聯(lián)歡慰問活動方案
- 公司組織油畫活動方案
- 公司月餅diy活動方案
- 公司組織踏青活動方案
- 公司蘇州兩日游活動方案
- 公司百日安全賽活動方案
- 公司網(wǎng)絡(luò)宣傳周活動方案
- 2025年戰(zhàn)略管理與籌資行業(yè)考研試題及答案
- 2025年植物學(xué)基礎(chǔ)知識及應(yīng)用考試卷及答案
- 拓展任務(wù)-火災(zāi)事故的基礎(chǔ)知識
- 智慧醫(yī)院建設(shè)項目實施方案
- 項目協(xié)作與溝通過程中的沖突管理試題及答案
- 2025年軌道車司機(jī)(中級)職業(yè)技能鑒定參考試題庫(含答案)
- 生物必修1教師用書
- 2024版壓力容器設(shè)計審核機(jī)考題庫-多選3-3
- 慢性阻塞性肺疾病急性加重期合并II型呼吸衰竭個案護(hù)理
- 路由與交換技術(shù)試題及答案
- (完整版)保安培訓(xùn)課件
- 2025屆上海市(春秋考)高考英語考綱詞匯對照表清單
- 《外匯交易基礎(chǔ)知識培訓(xùn)》詳解課件
- 汽油化學(xué)品安全技術(shù)說明書MSDS
評論
0/150
提交評論