Java多線程編程總結(jié).doc_第1頁
Java多線程編程總結(jié).doc_第2頁
Java多線程編程總結(jié).doc_第3頁
Java多線程編程總結(jié).doc_第4頁
Java多線程編程總結(jié).doc_第5頁
已閱讀5頁,還剩79頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

目錄目錄 JAVA 線程 概念與原理線程 概念與原理 2 JAVA 線程 創(chuàng)建與啟動(dòng)線程 創(chuàng)建與啟動(dò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)先級(jí)優(yōu)先級(jí) 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)者消費(fèi)者模型生產(chǎn)者消費(fèi)者模型 41 JAVA 線程 并發(fā)協(xié)作線程 并發(fā)協(xié)作 死鎖死鎖 45 JAVA 線程 新特征線程 新特征 線程池線程池 48 JAVA 線程 新特征線程 新特征 有返回值的線程有返回值的線程 54 JAVA 線程 新特征線程 新特征 鎖 上 鎖 上 56 JAVA 線程 新特征線程 新特征 鎖 下 鎖 下 59 JAVA 線程 新特征線程 新特征 信號(hào)量信號(hào)量 62 JAVA 線程 新特征線程 新特征 阻塞隊(duì)列阻塞隊(duì)列 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) 多線程是實(shí)現(xiàn)多任務(wù)的一種方式 進(jìn)程是指一個(gè)內(nèi)存中運(yùn)行的應(yīng)用程序 每個(gè)進(jìn)程都有自己獨(dú)立的一塊內(nèi)存空間 一個(gè)進(jìn)程中可 以啟動(dòng)多個(gè)線程 比如在 Windows 系統(tǒng)中 一個(gè)運(yùn)行的 exe 就是一個(gè)進(jìn)程 線程是指進(jìn)程中的一個(gè)執(zhí)行流程 一個(gè)進(jìn)程中可以運(yùn)行多個(gè)線程 比如 java exe 進(jìn)程中可以 運(yùn)行很多線程 線程總是屬于某個(gè)進(jìn)程 進(jìn)程中的多個(gè)線程共享進(jìn)程的內(nèi)存 同時(shí) 執(zhí)行是人的感覺 在線程之間實(shí)際上輪換執(zhí)行 二 二 Java 中的線程中的線程 在 Java 中 線程 指兩件不同的事情 1 java lang Thread 類的一個(gè)實(shí)例 2 線程的執(zhí)行 使用 java lang Thread 類或者 java lang Runnable 接口編寫代碼來定義 實(shí)例化和啟動(dòng)新 線程 一個(gè) Thread 類實(shí)例只是一個(gè)對(duì)象 像 Java 中的任何其他對(duì)象一樣 具有變量和方法 生死 于堆上 Java 中 每個(gè)線程都有一個(gè)調(diào)用棧 即使不在程序中創(chuàng)建任何新的線程 線程也在后臺(tái)運(yùn)行著 一個(gè) Java 應(yīng)用總是從 main 方法開始運(yùn)行 mian 方法運(yùn)行在一個(gè)線程內(nèi) 它被稱為主線 程 一旦創(chuàng)建一個(gè)新的線程 就產(chǎn)生一個(gè)新的調(diào)用棧 線程總體分兩類 用戶線程和守候線程 當(dāng)所有用戶線程執(zhí)行完畢的時(shí)候 JVM 自動(dòng)關(guān)閉 但是守候線程卻不獨(dú)立于 JVM 守候線程一 般是由操作系統(tǒng)或者用戶自己創(chuàng)建的 Java 線程 創(chuàng)建與啟動(dòng) 一 定義線程一 定義線程 1 擴(kuò)展 java lang Thread 類 此類中有個(gè) run 方法 應(yīng)該注意其用法 public void run 如果該線程是使用獨(dú)立的 Runnable 運(yùn)行對(duì)象構(gòu)造的 則調(diào)用該 Runnable 對(duì)象的 run 方法 否則 該方法不執(zhí)行任何操作并返回 Thread 的子類應(yīng)該重寫該方法 2 實(shí)現(xiàn) java lang Runnable 接口 void run 使用實(shí)現(xiàn)接口 Runnable 的對(duì)象創(chuàng)建一個(gè)線程時(shí) 啟動(dòng)該線程將導(dǎo)致在獨(dú)立執(zhí)行的線 程中調(diào)用對(duì)象的 run 方法 方法 run 的常規(guī)協(xié)定是 它可能執(zhí)行任何所需的操作 二 實(shí)例化線程二 實(shí)例化線程 1 如果是擴(kuò)展 java lang Thread 類的線程 則直接 new 即可 2 如果是實(shí)現(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 三 啟動(dòng)線程三 啟動(dòng)線程 在線程的 Thread 對(duì)象上調(diào)用 start 方法 而不是 run 或者別的方法 在調(diào)用 start 方法之前 線程處于新狀態(tài)中 新狀態(tài)指有一個(gè) Thread 對(duì)象 但還沒有一個(gè)真 正的線程 在調(diào)用 start 方法之后 發(fā)生了一系列復(fù)雜的事情 啟動(dòng)新的執(zhí)行線程 具有新的調(diào)用棧 該線程從新狀態(tài)轉(zhuǎn)移到可運(yùn)行狀態(tài) 當(dāng)該線程獲得機(jī)會(huì)執(zhí)行時(shí) 其目標(biāo) run 方法將運(yùn)行 注意 對(duì) Java 來說 run 方法沒有任何特別之處 像 main 方法一樣 它只是新線程知道 調(diào)用的方法名稱 和簽名 因此 在 Runnable 上或者 Thread 上調(diào)用 run 方法是合法的 但 并不啟動(dòng)新的線程 四 例子四 例子 1 實(shí)現(xiàn) Runnable 接口的多線程例子 實(shí)現(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 測(cè)試 Runnable 類實(shí)現(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 類實(shí)現(xiàn)的多線程例子 測(cè)試擴(kuò)展 Thread 類實(shí)現(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 對(duì)于上面的多線程程序代碼來說 輸出的結(jié)果是不確定的 其中的一條語句 for long k 0 k 100000000 k 是用來模擬一個(gè)非常耗時(shí)的操作的 五 一些常見問題五 一些常見問題 1 線程的名字 一個(gè)運(yùn)行中的線程總是有名字的 名字有兩個(gè)來源 一個(gè)是虛擬機(jī)自己給的 名字 一個(gè)是你自己的定的名字 在沒有指定線程名字的情況下 虛擬機(jī)總會(huì)為線程指定名字 并且主線程的名字總是 mian 非主線程的名字不確定 2 線程都可以設(shè)置名字 也可以獲取線程的名字 連主線程也不例外 3 獲取當(dāng)前線程的對(duì)象的方法是 Thread currentThread 4 在上面的代碼中 只能保證 每個(gè)線程都將啟動(dòng) 每個(gè)線程都將運(yùn)行直到完成 一系列線 程以某種順序啟動(dòng)并不意味著將按該順序執(zhí)行 對(duì)于任何一組啟動(dòng)的線程來說 調(diào)度程序不能 保證其執(zhí)行次序 持續(xù)時(shí)間也無法保證 5 當(dāng)線程目標(biāo) run 方法結(jié)束時(shí)該線程完成 6 一旦線程啟動(dòng) 它就永遠(yuǎn)不能再重新啟動(dòng) 只有一個(gè)新的線程可以被啟動(dòng) 并且只能一次 一個(gè)可運(yùn)行的線程或死線程可以被重新啟動(dòng) 7 線程的調(diào)度是 JVM 的一部分 在一個(gè) CPU 的機(jī)器上上 實(shí)際上一次只能運(yùn)行一個(gè)線程 一次只有一個(gè)線程棧執(zhí)行 JVM 線程調(diào)度程序決定實(shí)際運(yùn)行哪個(gè)處于可運(yùn)行狀態(tài)的線程 眾多可運(yùn)行線程中的某一個(gè)會(huì)被選中做為當(dāng)前線程 可運(yùn)行線程被選擇運(yùn)行的順序是沒有保障 的 8 盡管通常采用隊(duì)列形式 但這是沒有保障的 隊(duì)列形式是指當(dāng)一個(gè)線程完成 一輪 時(shí) 它 移到可運(yùn)行隊(duì)列的尾部等待 直到它最終排隊(duì)到該隊(duì)列的前端為止 它才能被再次選中 事實(shí) 上 我們把它稱為可運(yùn)行池而不是一個(gè)可運(yùn)行隊(duì)列 目的是幫助認(rèn)識(shí)線程并不都是以某種有保 障的順序排列唱呢個(gè)一個(gè)隊(duì)列的事實(shí) 9 盡管我們沒有無法控制線程調(diào)度程序 但可以通過別的方式來影響線程調(diào)度的方式 Java 線程 線程棧模型與線程的變量 要理解線程調(diào)度的原理 以及線程執(zhí)行過程 必須理解線程棧模型 線程棧是指某時(shí)刻時(shí)內(nèi)存中線程調(diào)度的棧信息 當(dāng)前調(diào)用的方法總是位于棧頂 線程棧的內(nèi)容 是隨著程序的運(yùn)行動(dòng)態(tài)變化的 因此研究線程棧必須選擇一個(gè)運(yùn)行的時(shí)刻 實(shí)際上指代碼運(yùn)行 到什么地方 下面通過一個(gè)示例性的代碼說明線程 調(diào)用 棧的變化過程 這幅圖描述在代碼執(zhí)行到兩個(gè)不同時(shí)刻 1 2 時(shí)候 虛擬機(jī)線程調(diào)用棧示意圖 當(dāng)程序執(zhí)行到 t start 時(shí)候 程序多出一個(gè)分支 增加了一個(gè)調(diào)用棧 B 這樣 棧 A 棧 B 并行執(zhí)行 從這里就可以看出方法調(diào)用和線程啟動(dòng)的區(qū)別了 Java 線程 線程狀態(tài)的轉(zhuǎn)換線程 線程狀態(tài)的轉(zhuǎn)換 一 線程狀態(tài) 線程的狀態(tài)轉(zhuǎn)換是線程控制的基礎(chǔ) 線程狀態(tài)總的可分為五大狀態(tài) 分別是生 死 可運(yùn)行 運(yùn)行 等待 阻塞 用一個(gè)圖來描述如下 1 新狀態(tài) 線程對(duì)象已經(jīng)創(chuàng)建 還沒有在其上調(diào)用 start 方法 2 可運(yùn)行狀態(tài) 當(dāng)線程有資格運(yùn)行 但調(diào)度程序還沒有把它選定為運(yùn)行線程時(shí)線程所處的狀 態(tài) 當(dāng) start 方法調(diào)用時(shí) 線程首先進(jìn)入可運(yùn)行狀態(tài) 在線程運(yùn)行之后或者從阻塞 等待或睡 眠狀態(tài)回來后 也返回到可運(yùn)行狀態(tài) 3 運(yùn)行狀態(tài) 線程調(diào)度程序從可運(yùn)行池中選擇一個(gè)線程作為當(dāng)前線程時(shí)線程所處的狀態(tài) 這 也是線程進(jìn)入運(yùn)行狀態(tài)的唯一一種方式 4 等待 阻塞 睡眠狀態(tài) 這是線程有資格運(yùn)行時(shí)它所處的狀態(tài) 實(shí)際上這個(gè)三狀態(tài)組合為一 種 其共同點(diǎn)是 線程仍舊是活的 但是當(dāng)前沒有條件運(yùn)行 換句話說 它是可運(yùn)行的 但是 如果某件事件出現(xiàn) 他可能返回到可運(yùn)行狀態(tài) 5 死亡態(tài) 當(dāng)線程的 run 方法完成時(shí)就認(rèn)為它死去 這個(gè)線程對(duì)象也許是活的 但是 它已 經(jīng)不是一個(gè)單獨(dú)執(zhí)行的線程 線程一旦死亡 就不能復(fù)生 如果在一個(gè)死去的線程上調(diào)用 sta rt 方法 會(huì)拋出 java lang IllegalThreadStateException 異常 有關(guān)詳細(xì)狀態(tài)轉(zhuǎn)換圖可以參看本人的 Java 多線程編程總結(jié) 中的圖 二 阻止線程執(zhí)行 對(duì)于線程的阻止 考慮一下三個(gè)方面 不考慮 IO 阻塞的情況 睡眠 等待 因?yàn)樾枰粋€(gè)對(duì)象的鎖定而被阻塞 1 睡眠 Thread sleep long millis 和 Thread sleep long millis int nanos 靜態(tài)方法強(qiáng)制當(dāng)前正 在執(zhí)行的線程休眠 暫停執(zhí)行 以 減慢線程 當(dāng)線程睡眠時(shí) 它入睡在某個(gè)地方 在蘇醒 之前不會(huì)返回到可運(yùn)行狀態(tài) 當(dāng)睡眠時(shí)間到期 則返回到可運(yùn)行狀態(tài) 線程睡眠的原因 線程執(zhí)行太快 或者需要強(qiáng)制進(jìn)入下一輪 因?yàn)?Java 規(guī)范不保證合理的輪 換 睡眠的實(shí)現(xiàn) 調(diào)用靜態(tài)方法 try Thread sleep 123 catch InterruptedException e e printStackTrace 睡眠的位置 為了讓其他線程有機(jī)會(huì)執(zhí)行 可以將 Thread sleep 的調(diào)用放線程 run 之內(nèi) 這樣才能保證該線程執(zhí)行過程中會(huì)睡眠 例如 在前面的例子中 將一個(gè)耗時(shí)的操作改為睡眠 以減慢線程的執(zhí)行 可以這么寫 public void run for int i 0 i 5 i 很耗時(shí)的操作 用來減慢線程的執(zhí)行 for long k 0 k 100000000 k try Thread sleep 3 catch InterruptedException e e printStackTrace System out println this getName i 運(yùn)行結(jié)果 阿三 0 李四 0 阿三 1 阿三 2 阿三 3 李四 1 李四 2 阿三 4 李四 3 李四 4 Process finished with exit code 0 這樣 線程在每次執(zhí)行過程中 總會(huì)睡眠 3 毫秒 睡眠了 其他的線程就有機(jī)會(huì)執(zhí)行了 注意 1 線程睡眠是幫助所有線程獲得運(yùn)行機(jī)會(huì)的最好方法 2 線程睡眠到期自動(dòng)蘇醒 并返回到可運(yùn)行狀態(tài) 不是運(yùn)行狀態(tài) sleep 中指定的時(shí)間是線 程不會(huì)運(yùn)行的最短時(shí)間 因此 sleep 方法不能保證該線程睡眠到期后就開始執(zhí)行 3 sleep 是靜態(tài)方法 只能控制當(dāng)前正在運(yùn)行的線程 下面給個(gè)例子 一個(gè)計(jì)數(shù)器 計(jì)數(shù)到 100 在每個(gè)數(shù)字之間暫停 1 秒 每隔 10 個(gè)數(shù)字輸出一個(gè)字符串 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)先級(jí)和線程讓步 yield 線程的讓步是通過 Thread yield 來實(shí)現(xiàn)的 yield 方法的作用是 暫停當(dāng)前正在執(zhí)行的線 程對(duì)象 并執(zhí)行其他線程 要理解 yield 必須了解線程的優(yōu)先級(jí)的概念 線程總是存在優(yōu)先級(jí) 優(yōu)先級(jí)范圍在 1 10 之間 JVM 線程調(diào)度程序是基于優(yōu)先級(jí)的搶先調(diào)度機(jī)制 在大多數(shù)情況下 當(dāng)前運(yùn)行的線程優(yōu) 先級(jí)將大于或等于線程池中任何線程的優(yōu)先級(jí) 但這僅僅是大多數(shù)情況 注意 當(dāng)設(shè)計(jì)多線程應(yīng)用程序的時(shí)候 一定不要依賴于線程的優(yōu)先級(jí) 因?yàn)榫€程調(diào)度優(yōu)先級(jí)操 作是沒有保障的 只能把線程優(yōu)先級(jí)作用作為一種提高程序效率的方法 但是要保證程序不依 賴這種操作 當(dāng)線程池中線程都具有相同的優(yōu)先級(jí) 調(diào)度程序的 JVM 實(shí)現(xiàn)自由選擇它喜歡的線程 這時(shí)候調(diào) 度程序的操作有兩種可能 一是選擇一個(gè)線程運(yùn)行 直到它阻塞或者運(yùn)行完成為止 二是時(shí)間 分片 為池內(nèi)的每個(gè)線程提供均等的運(yùn)行機(jī)會(huì) 設(shè)置線程的優(yōu)先級(jí) 線程默認(rèn)的優(yōu)先級(jí)是創(chuàng)建它的執(zhí)行線程的優(yōu)先級(jí) 可以通過 setPriority i nt newPriority 更改線程的優(yōu)先級(jí) 例如 Thread t new MyThread t setPriority 8 t start 線程優(yōu)先級(jí)為 1 10 之間的正整數(shù) JVM 從不會(huì)改變一個(gè)線程的優(yōu)先級(jí) 然而 1 10 之間的 值是沒有保證的 一些 JVM 可能不能識(shí)別 10 個(gè)不同的值 而將這些優(yōu)先級(jí)進(jìn)行每?jī)蓚€(gè)或多個(gè) 合并 變成少于 10 個(gè)的優(yōu)先級(jí) 則兩個(gè)或多個(gè)優(yōu)先級(jí)的線程可能被映射為一個(gè)優(yōu)先級(jí) 線程默認(rèn)優(yōu)先級(jí)是 5 Thread 類中有三個(gè)常量 定義線程優(yōu)先級(jí)范圍 static int MAX PRIORITY 線程可以具有的最高優(yōu)先級(jí) static int MIN PRIORITY 線程可以具有的最低優(yōu)先級(jí) static int NORM PRIORITY 分配給線程的默認(rèn)優(yōu)先級(jí) 3 Thread yield 方法 Thread yield 方法作用是 暫停當(dāng)前正在執(zhí)行的線程對(duì)象 并執(zhí)行其他線程 yield 應(yīng)該做的是讓當(dāng)前運(yùn)行線程回到可運(yùn)行狀態(tài) 以允許具有相同優(yōu)先級(jí)的其他線程獲得運(yùn) 行機(jī)會(huì) 因此 使用 yield 的目的是讓相同優(yōu)先級(jí)的線程之間能適當(dāng)?shù)妮嗈D(zhuǎn)執(zhí)行 但是 實(shí)際 中無法保證 yield 達(dá)到讓步目的 因?yàn)樽尣降木€程還有可能被線程調(diào)度程序再次選中 結(jié)論 yield 從未導(dǎo)致線程轉(zhuǎn)到等待 睡眠 阻塞狀態(tài) 在大多數(shù)情況下 yield 將導(dǎo)致線程從 運(yùn)行狀態(tài)轉(zhuǎn)到可運(yùn)行狀態(tài) 但有可能沒有效果 4 join 方法 Thread 的非靜態(tài)方法 join 讓一個(gè)線程 B 加入 到另外一個(gè)線程 A 的尾部 在 A 執(zhí)行完畢之 前 B 不能工作 例如 Thread t new MyThread t start t join 另外 join 方法還有帶超時(shí)限制的重載版本 例如 t join 5000 則讓線程等待 5000 毫秒 如果超過這個(gè)時(shí)間 則停止等待 變?yōu)榭蛇\(yùn)行狀態(tài) 線程的加入 join 對(duì)線程棧導(dǎo)致的結(jié)果是線程棧發(fā)生了變化 當(dāng)然這些變化都是瞬時(shí)的 下面 給示意圖 小結(jié) 到目前位置 介紹了線程離開運(yùn)行狀態(tài)的 3 種方法 1 調(diào)用 Thread sleep 使當(dāng)前線程睡眠至少多少毫秒 盡管它可能在指定的時(shí)間之前被中 斷 2 調(diào)用 Thread yield 不能保障太多事情 盡管通常它會(huì)讓當(dāng)前運(yùn)行線程回到可運(yùn)行性狀 態(tài) 使得有相同優(yōu)先級(jí)的線程有機(jī)會(huì)執(zhí)行 3 調(diào)用 join 方法 保證當(dāng)前線程停止執(zhí)行 直到該線程所加入的線程完成為止 然而 如 果它加入的線程沒有存活 則當(dāng)前線程不需要停止 除了以上三種方式外 還有下面幾種特殊情況可能使線程離開運(yùn)行狀態(tài) 1 線程的 run 方法完成 2 在對(duì)象上調(diào)用 wait 方法 不是在線程上調(diào)用 3 線程不能在對(duì)象上獲得鎖定 它正試圖運(yùn)行該對(duì)象的方法代碼 4 線程調(diào)度程序可以決定將當(dāng)前運(yùn)行狀態(tài)移動(dòng)到可運(yùn)行狀態(tài) 以便讓另一個(gè)線程獲得運(yùn)行機(jī) 會(huì) 而不需要任何理由 Java 線程 線程的同步與鎖線程 線程的同步與鎖 一 同步問題提出一 同步問題提出 線程的同步是為了防止多個(gè)線程訪問一個(gè)數(shù)據(jù)對(duì)象時(shí) 對(duì)數(shù)據(jù)造成的破壞 例如 兩個(gè)線程 ThreadA ThreadB 都操作同一個(gè)對(duì)象 Foo 對(duì)象 并修改 Foo 對(duì)象上的數(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 雖然集合對(duì)象 private List nameList Collections synchronizedList new LinkedList 是同步的 但是程序還不是線程安全的 出現(xiàn)這種事件的原因是 上例中一個(gè)線程操作列表過程中無法阻止另外一個(gè)線程對(duì)列表的其他 操作 解決上面問題的辦法是 在操作集合對(duì)象的 NameList 上面做一個(gè)同步 改寫后的代碼如下 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)一個(gè)線程訪問其中一個(gè)同步方法時(shí) 其他線程只有等待 七 線程死鎖七 線程死鎖 死鎖對(duì) Java 程序來說 是很復(fù)雜的 也很難發(fā)現(xiàn)問題 當(dāng)兩個(gè)線程被阻塞 每個(gè)線程在等待 另一個(gè)線程時(shí)就發(fā)生死鎖 還是看一個(gè)比較直觀的死鎖例子 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 方法由一個(gè)線程啟動(dòng) write 方法由另外一個(gè)線程啟動(dòng) 讀線程將擁有 resource A 鎖 寫線程將擁有 resourceB 鎖 兩者都堅(jiān)持等待的話就出現(xiàn)死鎖 實(shí)際上 上面這個(gè)例子發(fā)生死鎖的概率很小 因?yàn)樵诖a內(nèi)的某個(gè)點(diǎn) CPU 必須從讀線程切換 到寫線程 所以 死鎖基本上不能發(fā)生 但是 無論代碼中發(fā)生死鎖的概率有多小 一旦發(fā)生死鎖 程序就死掉 有一些設(shè)計(jì)方法能幫 助避免死鎖 包括始終按照預(yù)定義的順序獲取鎖這一策略 已經(jīng)超出 SCJP 的考試范圍 八 線程同步小結(jié)八 線程同步小結(jié) 1 線程同步的目的是為了保護(hù)多個(gè)線程反問一個(gè)資源時(shí)對(duì)資源的破壞 2 線程同步方法是通過鎖來實(shí)現(xiàn) 每個(gè)對(duì)象都有切僅有一個(gè)鎖 這個(gè)鎖與一個(gè)特定的對(duì)象關(guān) 聯(lián) 線程一旦獲取了對(duì)象鎖 其他訪問該對(duì)象的線程就無法再訪問該對(duì)象的其他同步方法 3 對(duì)于靜態(tài)同步方法 鎖是針對(duì)這個(gè)類的 鎖對(duì)象是該類的 Class 對(duì)象 靜態(tài)和非靜態(tài)方法 的鎖互不干預(yù) 一個(gè)線程獲得鎖 當(dāng)在一個(gè)同步方法中訪問另外對(duì)象上的同步方法時(shí) 會(huì)獲取 這兩個(gè)對(duì)象鎖 4 對(duì)于同步 要時(shí)刻清醒在哪個(gè)對(duì)象上同步 這是關(guān)鍵 5 編寫線程安全的類 需要時(shí)刻注意對(duì)多個(gè)線程競(jìng)爭(zhēng)訪問資源的邏輯和安全做出正確的判斷 對(duì) 原子 操作做出分析 并保證原子操作期間別的線程無法訪問競(jìng)爭(zhēng)資源 6 當(dāng)多個(gè)線程等待一個(gè)對(duì)象鎖時(shí) 沒有獲取到鎖的線程將發(fā)生阻塞 7 死鎖是線程間相互等待鎖鎖造成的 在實(shí)際中發(fā)生的概率非常的小 真讓你寫個(gè)死鎖程序 不一定好使 呵呵 但是 一旦程序發(fā)生死鎖 程序?qū)⑺赖?Java 線程 線程的交互線程 線程的交互 線程交互是比較復(fù)雜的問題 SCJP 要求不很基礎(chǔ) 給定一個(gè)場(chǎng)景 編寫代碼來恰當(dāng)使用等待 通知和通知所有線程 一 線程交互的基礎(chǔ)知識(shí) SCJP 所要求的線程交互知識(shí)點(diǎn)需要從 java lang Object 的類的三個(gè)方法來學(xué)習(xí) void notify 喚醒在此對(duì)象監(jiān)視器上等待的單個(gè)線程 void notifyAll 喚醒在此對(duì)象監(jiān)視器上等待的所有線程 void wait 導(dǎo)致當(dāng)前的線程等待 直到其他線程調(diào)用此對(duì)象的 notify 方法或 notifyAll 方法 當(dāng)然 wait 還有另外兩個(gè)重載方法 void wait long timeout 導(dǎo)致當(dāng)前的線程等待 直到其他線程調(diào)用此對(duì)象的 notify 方法或 notifyAll 方法 或者超過指定的時(shí)間量 void wait long timeout int nanos 導(dǎo)致當(dāng)前的線程等待 直到其他線程調(diào)用此對(duì)象的 notify 方法或 notifyAll 方法 或者其他某個(gè)線程中斷當(dāng)前線程 或者已超過某個(gè)實(shí)際時(shí)間量 以上這些方法是幫助線程傳遞線程關(guān)心的時(shí)間狀態(tài) 關(guān)于等待 通知 要記住的關(guān)鍵點(diǎn)是 必須從同步環(huán)境內(nèi)調(diào)用 wait notify notifyAll 方法 線程不能調(diào)用對(duì)象上等待或通知 的方法 除非它擁有那個(gè)對(duì)象的鎖 wait notify notifyAll 都是 Object 的實(shí)例方法 與每個(gè)對(duì)象具有鎖一樣 每個(gè)對(duì)象可 以有一個(gè)線程列表 他們等待來自該信號(hào) 通知 線程通過執(zhí)行對(duì)象上的 wait 方法獲得這 個(gè)等待列表 從那時(shí)候起 它不再執(zhí)行任何其他指令 直到調(diào)用對(duì)象的 notify 方法為止 如 果多個(gè)線程在同一個(gè)對(duì)象上等待 則將只選擇一個(gè)線程 不保證以何種順序 繼續(xù)執(zhí)行 如果 沒有線程等待 則不采取任何特殊操作 下面看個(gè)例子就明白了 計(jì)算輸出其他線程鎖計(jì)算的數(shù)據(jù) author leizhimin 2008 9 15 13 20 38 public class ThreadA public static void main String args ThreadB b new ThreadB 啟動(dòng)計(jì)算線程 b start 線程 A 擁有 b 對(duì)象上的鎖 線程為了調(diào)用 wait 或 notify 方法 該線程必須是那個(gè) 對(duì)象鎖的擁有者 synchronized b try System out println 等待對(duì)象 b 完成計(jì)算 當(dāng)前線程 A 等待 b wait catch InterruptedException e e printStackTrace System out println b 對(duì)象計(jì)算的總和是 b total 計(jì)算 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 完成計(jì)算了 喚醒在此對(duì)象監(jiān)視器上等待的單個(gè)線程 在本例中線程 A 被喚醒 notify 等待對(duì)象 b 完成計(jì)算 b 對(duì)象計(jì)算的總和是 5050 Process finished with exit code 0 千萬注意 當(dāng)在對(duì)象上調(diào)用 wait 方法時(shí) 執(zhí)行該代碼的線程立即放棄它在對(duì)象上的鎖 然而調(diào)用 notify 時(shí) 并不意味著這時(shí)線程會(huì)放棄其鎖 如果線程榮然在完成同步代碼 則線程在移出之前不 會(huì)放棄鎖 因此 只要調(diào)用 notify 并不意味著這時(shí)該鎖變得可用 二 多個(gè)線程在等待一個(gè)對(duì)象鎖時(shí)候使用 notifyAll 在多數(shù)情況下 最好通知等待某個(gè)對(duì)象的所有線程 如果這樣做 可以在對(duì)象上使用 notifyAll 讓所有在此對(duì)象上等待的線程沖出等待區(qū) 返回到可運(yùn)行狀態(tài) 下面給個(gè)例子 計(jì)算線程 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 通知所有在此對(duì)象上等待的線程 notifyAll 獲取計(jì)算結(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 等待計(jì)算結(jié)果 c wait catch InterruptedException e e printStackTrace System out println Thread currentThread 計(jì)算結(jié)果為 c t otal public static void main String args Calculator calculator new Calculator 啟動(dòng)三個(gè)線程 分別獲取計(jì)算結(jié)果 new ReaderResult calculator start new ReaderResult calculator start new ReaderResult calculator start 啟動(dòng)計(jì)算線程 calculator start 運(yùn)行結(jié)果 Thread Thread 1 5 main 等待計(jì)算結(jié)果 Thread Thread 2 5 main 等待計(jì)算結(jié)果 Thread Thread 3 5 main 等待計(jì)算結(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 計(jì)算結(jié)果為 5050 Thread Thread 2 5 main 計(jì)算結(jié)果為 5050 Thread Thread 3 5 main 計(jì)算結(jié)果為 5050 Process finished with exit code 0 運(yùn)行結(jié)果表明 程序中有異常 并且多次運(yùn)行結(jié)果可能有多種輸出結(jié)果 這就是說明 這個(gè)多 線程的交互程序還存在問題 究竟是出了什么問題 需要深入的分析和思考 下面將做具體分 析 實(shí)際上 上面這個(gè)代碼中 我們期望的是讀取結(jié)果的線程在計(jì)算線程調(diào)用 notifyAll 之前等待 即可 但是 如果計(jì)算線程先執(zhí)行 并在讀取結(jié)果線程等待之前調(diào)用了 notify 方法 那么又 會(huì)發(fā)生什么呢 這種情況是可能發(fā)生的 因?yàn)闊o法保證線程的不同部分將按照什么順序來執(zhí)行 幸運(yùn)的是當(dāng)讀取線程運(yùn)行時(shí) 它只能馬上進(jìn)入等待狀態(tài) 它沒有做任何事情來檢查等待的 事件是否已經(jīng)發(fā)生 因此 如果計(jì)算線程已經(jīng)調(diào)用了 notifyAll 方法 那么它就不會(huì)再 次調(diào)用 notifyAll 并且等待的讀取線程將永遠(yuǎn)保持等待 這當(dāng)然是開發(fā)者所不愿意看到 的問題 因此 當(dāng)?shù)却氖录l(fā)生時(shí) 需要能夠檢查 notifyAll 通知事件是否已經(jīng)發(fā)生 通常 解決上面問題的最佳方式是將 Java 線程 線程的調(diào)度線程 線程的調(diào)度 休眠休眠 Java 線程調(diào)度是 Java 多線程的核心 只有良好的調(diào)度 才能充分發(fā)揮系統(tǒng)的性能 提高程序 的執(zhí)行效率 這里要明確的一點(diǎn) 不管程序員怎么編寫調(diào)度 只能最大限度的影響線程執(zhí)行的次序 而不能 做到精準(zhǔn)控制 線程休眠的目的是使線程讓出 CPU 的最簡(jiǎn)單的做法之一 線程休眠時(shí)候 會(huì)將 CPU 資源交給 其他線程 以便能輪換執(zhí)行 當(dāng)休眠一定時(shí)間后 線程會(huì)蘇醒 進(jìn)入準(zhǔn)備狀態(tài)等待執(zhí)行 線程休眠的方法是 Thread sleep long millis 和 Thread sleep long millis int nanos 均為靜態(tài)方法 那調(diào)用 sleep 休眠的哪個(gè)線程呢 簡(jiǎn)單說 哪個(gè)線程調(diào)用 sleep 就休眠哪 個(gè)線程 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)先級(jí)優(yōu)先級(jí) 與線程休眠類似 線程的優(yōu)先級(jí)仍然無法保障線程的執(zhí)行次序 只不過 優(yōu)先級(jí)高的線程獲取 CPU 資源的概率較大 優(yōu)先級(jí)低的并非沒機(jī)會(huì)執(zhí)行 線程的優(yōu)先級(jí)用 1 10 之間的整數(shù)表示 數(shù)值越大優(yōu)先級(jí)越高 默認(rèn)的優(yōu)先級(jí)為 5 在一個(gè)線程中開啟另外一個(gè)新線程 則新開線程稱為該線程的子線程 子線程初始優(yōu)先級(jí)與父 線程相同 Java 線程 線程的調(diào)度 優(yōu)先級(jí) 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)前運(yùn)行著線程讓出 CPU 資源 但是然給誰不知道 僅僅是讓出 線 程狀態(tài)回到可運(yùn)行狀態(tài) 線程的讓步使用 Thread yield 方法 yield 為靜態(tài)方法 功能是暫停當(dāng)前正在執(zhí)行的線程 對(duì)象 并執(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)度 合并合并 線程的合并的含義就是將幾個(gè)并行線程的線程合并為一個(gè)單線程執(zhí)行 應(yīng)用場(chǎng)景是當(dāng)一個(gè)線程 必須等待另一個(gè)線程執(zhí)行完畢才能執(zhí)行時(shí)可以使用 join 方法 join 為非靜態(tài)方法 定義如下 void join 等待該線程終止 void join long millis 等待該線程終止的時(shí)間最長(zhǎng)為 millis 毫秒 void join long

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論