------Java培訓(xùn)、Android培訓(xùn)、iOS培訓(xùn)、.Net培訓(xùn)、期待與您交流! -------
一、進(jìn)程、線程、多進(jìn)程的概念
1、進(jìn)程和線程
進(jìn)程:正在進(jìn)行中的程序。其實(shí)進(jìn)程就是一個(gè)應(yīng)用程序運(yùn)行時(shí)的內(nèi)存分配空間。
線程:其實(shí)就是進(jìn)程中一個(gè)程序執(zhí)行控制單元,一條執(zhí)行路徑。進(jìn)程負(fù)責(zé)的是應(yīng)用程序的空間的標(biāo)示。線程負(fù)責(zé)的是應(yīng)用程序的執(zhí)行順序。
一個(gè)進(jìn)程至少有一個(gè)線程在運(yùn)行,當(dāng)一個(gè)進(jìn)程中出現(xiàn)多個(gè)線程時(shí),就稱這個(gè)應(yīng)用程序是多線程應(yīng)用程序,每個(gè)線程在棧區(qū)中都有自己的執(zhí)行空間,自己的方法區(qū)、自己的變量。
2、java虛擬機(jī)就是多線程程序
jvm在啟動(dòng)的時(shí),首先有一個(gè)主線程,負(fù)責(zé)程序的執(zhí)行,調(diào)用的是main函數(shù)。主線程執(zhí)行的代碼都在main方法中。
當(dāng)產(chǎn)生垃圾時(shí),收垃圾的動(dòng)作,是不需要主線程來完成,因?yàn)檫@樣,會(huì)出現(xiàn)主線程中的代碼執(zhí)行會(huì)停止,會(huì)去運(yùn)行垃圾回收器代碼,效率較低,所以由單獨(dú)一個(gè)線程來負(fù)責(zé)垃圾回收。
3、多線程的好壞之處
多線程的好處:解決了多部分代碼同時(shí)運(yùn)行的問題。 多線程的弊端:線程太多,會(huì)導(dǎo)致效率的降低。 其實(shí),多個(gè)應(yīng)用程序同時(shí)執(zhí)行都是CPU在做著快速的切換完成的。這個(gè)切換是隨機(jī)的。哪個(gè)線程搶到了cpu的執(zhí)行權(quán),哪個(gè)線程就執(zhí)行。CPU的切換是需要花費(fèi)時(shí)間的,從而導(dǎo)致了效率的降低。
創(chuàng)建線程的目的就是為了開啟一條執(zhí)行路徑,去運(yùn)行指定的代碼和其他代碼實(shí)現(xiàn)同時(shí)運(yùn)行,而運(yùn)行的指定代碼就是這個(gè)執(zhí)行路徑的任務(wù)。
二、創(chuàng)建線程的方式
1、獲取當(dāng)前線程名稱
Thread.currentThread().getName();
線程的名稱是由:Thread-編號(hào)定義的。編號(hào)從0開始。
線程要運(yùn)行的代碼都統(tǒng)一存放在了run方法中。
2、創(chuàng)建線程的方式
(1)、第一種方式:繼承Thread類,由子類復(fù)寫run方法。
1. 定義一個(gè)類繼承Thread類。2. 覆蓋Thread類中的run方法。3. 直接創(chuàng)建Thread的子類對(duì)象創(chuàng)建線程。4. 調(diào)用start方法開啟線程并調(diào)用線程的任務(wù)run方法執(zhí)行。
run方法就是封裝自定義線程運(yùn)行任務(wù)的函數(shù),run方法中定義的就是線程要運(yùn)行的任務(wù)代碼。記住:把需要開啟多線程的運(yùn)行代碼都寫到run方法中。
代碼演示:
1 //繼承Thread類,復(fù)寫run方法 2 class Demo extends Thread 3 { 4 public void run(){//run方法內(nèi)不能拋異常,只能try catch。因?yàn)樵摲椒ㄊ菑?fù)寫Thread類中的run方法。Thread類中的run方法沒有拋異常 5 for (int x=0;x<70 ;x++ ) 6 { 7 System.out.自定義線程名稱的方法
1 class Demo extends Thread 2 { 3 private String name; 4 Demo(String name){ 5 super(name); 6 } 7 public void run(){ 8 System.out.println(Thread.currentThread().getName()); 9 }10 }11 class ThreadDemo12 {13 publici static void main(Stirng[] args){14 new Demo("線程一").start();15 new Demo("線程二").start();16 }17 } (2)、創(chuàng)建線程的第二種方式:實(shí)現(xiàn)Runnable接口,復(fù)寫run方法。
1.定義類實(shí)現(xiàn)Runnable接口
2.覆蓋接口中的run方法(用于封裝線程要運(yùn)行的代碼)
3.通過Thread類創(chuàng)建線程對(duì)象
4.將實(shí)現(xiàn)了Runnable接口的子類對(duì)象作為實(shí)際參數(shù)傳遞給Thread類中的構(gòu)造函數(shù)。
為什么要傳遞呢?線程的任務(wù)都封裝在Runnable接口子類對(duì)象的run方法中,所以要在線程對(duì)象創(chuàng)建時(shí)就必須明確要運(yùn)行的任務(wù)。
5.調(diào)用Thread對(duì)象的start方法。開啟線程,并運(yùn)行Runnable接口子類中的run方法。
為什么要有Runnable接口的出現(xiàn)?
1.通過繼承Thread類的方式,可以完成多線程的建立。但是這種方式有一個(gè)局限性,如果一個(gè)類已經(jīng)有了自己的父類,就不可以繼承Thread類,因?yàn)閖ava單繼承的局限性。
可是該類中的還有部分代碼需要被多個(gè)線程同時(shí)執(zhí)行。這時(shí)怎么辦呢?
只有對(duì)該類進(jìn)行額外的功能擴(kuò)展,java就提供了一個(gè)接口Runnable。這個(gè)接口中定義了run方法,其實(shí)run方法的定義就是為了存儲(chǔ)多線程要運(yùn)行的代碼。
所以,通常創(chuàng)建線程都用第二種方式。因?yàn)閷?shí)現(xiàn)Runnable接口可以避免單繼承的局限性。
2.其實(shí)是將不同類中需要被多線程執(zhí)行的代碼進(jìn)行抽取。將多線程要運(yùn)行的代碼的位置單獨(dú)定義到接口中。為其他類進(jìn)行功能擴(kuò)展提供了前提。所以Thread類在描述線程時(shí),內(nèi)部定義的run方法,也來自于Runnable接口。
實(shí)現(xiàn)Runnable接口可以避免單繼承的局限性。而且,繼承Thread,是可以對(duì)Thread類中的方法,進(jìn)行子類復(fù)寫的。但是不需要做這個(gè)復(fù)寫動(dòng)作的話,只為定義線程代碼存放位置,實(shí)現(xiàn)Runnable接口更方便一些。所以Runnable接口將線程要執(zhí)行的任務(wù)封裝成了對(duì)象。
代碼演示:
1 //實(shí)現(xiàn)Runnable,復(fù)寫run方法 2 class Demo implements Runnable 3 { 4 public void run(){ 5 for (int x=0;x<60 ;x++ ) 6 { 7 System.out.println(Thread.currentThread().getName()+".."+x); 8 } 9 }10 }11 class ThreadDemo12 {13 public static void main(String[] args) 14 {15 new Thread(new Demo()).start();16 for (int x=0;x<60 ; x++)17 {18 System.out.println(Thread.currentThread().getName()+":::::"+x);19 }20 }21 }(3)、線程狀態(tài)
被創(chuàng)建:start()
運(yùn)行:具備了執(zhí)行資格,同時(shí)也具備執(zhí)行權(quán)
臨時(shí)阻塞狀態(tài):線程具備cpu的執(zhí)行資格,但是沒有cpu的執(zhí)行權(quán)
凍結(jié):sleep(time),wait()--notify(),失去執(zhí)行資格和執(zhí)行權(quán)。sleep方法時(shí)間到或者調(diào)用notify()方法時(shí),獲得執(zhí)行資格,變?yōu)榕R時(shí)狀態(tài)
消亡:stop()方法,或者run()方法結(jié)束

三、線程安全問題
1、多線程安全問題的原因
通過圖解:發(fā)現(xiàn)一個(gè)線程在執(zhí)行多條語句時(shí),并運(yùn)算同一個(gè)數(shù)據(jù)時(shí),在執(zhí)行過程中,其他線程參與進(jìn)來,并操作了這個(gè)數(shù)據(jù)。導(dǎo)致到了錯(cuò)誤數(shù)據(jù)的產(chǎn)生。
涉及到兩個(gè)因素:
1.多個(gè)線程在操作共享數(shù)據(jù)。
2.有多條語句對(duì)共享數(shù)據(jù)進(jìn)行運(yùn)算。
原因:這多條語句,在某一個(gè)時(shí)刻被一個(gè)線程執(zhí)行時(shí),還沒有執(zhí)行完,就被其他線程執(zhí)行了。
2、線程安全問題的解決方案
解決安全問題的原理:
只要將操作共享數(shù)據(jù)的語句在某一時(shí)段讓一個(gè)線程執(zhí)行完,在執(zhí)行過程中,其他線程不能進(jìn)來執(zhí)行就可以解決這個(gè)問題。必須要當(dāng)前線程把這些代碼都執(zhí)行完畢后,其他線程才可以參與運(yùn)算。
解決同步的方式:(1)、同步代碼塊的格式: synchronized(對(duì)象){// 任意對(duì)象都可以。這個(gè)對(duì)象就是鎖。 需要被同步的代碼; }同步的好處:解決了線程的安全問題。同步的弊端:當(dāng)線程相當(dāng)多時(shí),因?yàn)槊總€(gè)線程都會(huì)去判斷同步上的鎖,這是很耗費(fèi)資源的,無形中會(huì)降低程序的運(yùn)行效率。同步的前提:必須有多個(gè)線程并使用同一個(gè)鎖。
需求:模擬4個(gè)線程同時(shí)賣100張票。
1 //加上同步代碼塊后 2 class Ticket implements Runnable 3 { 4 private int ticket=100; 5 private Object obj=new Object(); 6 public void run(){ 7 while (true) 8 { 9 synchronized(obj){//加上同步代碼塊后,obj對(duì)象相當(dāng)于一把鎖,一個(gè)線程進(jìn)去后,其他線程無法進(jìn)去,只有當(dāng)這個(gè)線程執(zhí)行完,跳出同步后,另一個(gè)才能進(jìn)去10 if (ticket>0)//一個(gè)線程進(jìn)同步有兩個(gè)隱式操作,剛進(jìn)同步后:上鎖,跳出同步后:釋放鎖11 {12 try{Thread.sleep(20);}catch(Exception e){}//讓線程停止20毫秒,這時(shí)候出現(xiàn)了線程等于0、-1、-2的情況13 System.out.println(Thread.currentThread().getName()+"..."+ticket--);//出現(xiàn)問題的原因是:線程停止了20毫秒后,繼續(xù)運(yùn)行的時(shí)候沒有判斷if的條件。14 }15 }16 }17 18 } 19 }20 class TicketDemo 21 {22 public static void main(String[] args) 23 {24 Ticket t=new Ticket();25 new Thread(t).start();26 new Thread(t).start();27 new Thread(t).start();28 new Thread(t).start();29 }30 }需求:儲(chǔ)戶,兩個(gè),每個(gè)都到銀行存錢,每次存100,共存三次。
1 class Bank 2 { 3 private Object obj=new Object(); 4 private int sum; 5 public void add(int num){ 6 synchronized(obj){ 7 sum=sum+num; 8 try{Thread.sleep(10);}catch(Exception e){} 9 System.out.println("sum="+sum);10 }11 }12 }13 class Person implements Runnable14 {15 private Bank b=new Bank();16 public void run(){17 for(int x=0;x<3;x++){18 b.add(100);19 }20 }21 }22 class BankDemo 23 {24 public static void main(String[] args) 25 {26 Person p=new Person();27 new Thread(p).start();28 new Thread(p).start();29 }30 }(2)、同步函數(shù)
同步函數(shù):其實(shí)就是將同步關(guān)鍵字定義在函數(shù)上,讓函數(shù)具備了同步性。
上面的代碼可以用同步函數(shù)代替同步代碼塊。
1 class Bank2 {3 private int sum;4 public synchronized void add(int num){ 5 sum=sum+num;6 try{Thread.sleep(10);}catch(Exception e){}7 System.out.println("sum="+sum); 8 }9 }同步函數(shù)用的是哪一個(gè)鎖呢?函數(shù)需要被對(duì)象調(diào)用。那么函數(shù)都有一個(gè)所屬對(duì)象引用。就是this。所以同步函數(shù)使用的鎖是this。
同步函數(shù)和同步代碼塊的區(qū)別:1. 同步函數(shù)的鎖是固定的this。2. 同步代碼塊的鎖是任意的對(duì)象。建議使用同步代碼塊。
關(guān)于同步函數(shù)用的是this鎖,可以通過該程序進(jìn)行驗(yàn)證。使用兩個(gè)線程來買票。一個(gè)線程在同步代碼塊中。一個(gè)線程在同步函數(shù)中。都在執(zhí)行賣票動(dòng)作。
1 class Ticket implements Runnable 2 { 3 private int ticket=100; 4 boolean flag; 5 public void run(){ 6 if(flag){ 7 while (true) 8 { 9 synchronized(this){10 if(ticket>0){11 try{Thread.sleep(10);}catch(Exception e){}12 System.out.println(Thread.currentThread().getName()+"..obj."+ticket--);13 }14 } 15 } 16 }17 else18 while(true)19 show();20 }21 public synchronized void show(){ 22 if(ticket>0){23 try{Thread.sleep(10);}catch(Exception e){}24 System.out.println(Thread.currentThread().getName()+"::this::"+ticket--);25 }26 27 }28 }29 class ThisLockDemo 30 {31 public static void main(String[] args) 32 {33 Ticket t=new Ticket();34 new Thread(t).start();35 try{Thread.sleep(10);}catch(Exception e){}//讓線程一先sleep一會(huì),這時(shí)候主線程還獲取執(zhí)行權(quán),繼續(xù)向下執(zhí)行36 t.flag=true;//標(biāo)記為true,這時(shí)候會(huì)運(yùn)行show方法中的代碼37 new Thread(t).start();38 }39 }上面的代碼顯示兩個(gè)線程交替執(zhí)行而且把100張票賣完了,而且沒有出現(xiàn)0號(hào)和負(fù)數(shù)的票,說明兩個(gè)線程是安全的,同時(shí)也說明了同步函數(shù)用的鎖是this。
(3)、靜態(tài)同步函數(shù)
當(dāng)同步函數(shù)被static修飾時(shí),這時(shí)的同步用的是哪個(gè)鎖呢?
靜態(tài)函數(shù)在加載時(shí)所屬于類,這時(shí)有可能還沒有該類產(chǎn)生的對(duì)象,但是該類的字節(jié)碼文件加載進(jìn)內(nèi)存就已經(jīng)被封裝成了對(duì)象,這個(gè)對(duì)象就是該類的字節(jié)碼文件對(duì)象。
所以靜態(tài)加載時(shí),只有一個(gè)對(duì)象存在,那么靜態(tài)同步函數(shù)就使用的這個(gè)對(duì)象。
這個(gè)對(duì)象就是 類名.class。
還是用賣票的程序可以驗(yàn)證靜態(tài)同步函數(shù)的鎖是.class。
1 class Ticket implements Runnable 2 { 3 private static int ticket=100; 4 boolean flag; 5 public void run(){ 6 if(flag){ 7 while (true) 8 { 9 synchronized(Ticket.class){10 if(ticket>0){11 try{Thread.sleep(10);}catch(Exception e){}12 System.out.println(Thread.currentThread().getName()+"..obj."+ticket--);13 }14 } 15 } 16 }17 else18 while(true)19 show();20 }21 public static synchronized void show(){ 22 if(ticket>0){23 try{Thread.sleep(10);}catch(Exception e){}24 System.out.println(Thread.currentThread().getName()+"::this::"+ticket--);25 }26 27 }28 }29 class ThisLockDemo 30 {31 public static void main(String[] args) 32 {33 Ticket t=new Ticket();34 new Thread(t).start();35 try{Thread.sleep(10);}catch(Exception e){}//讓線程一先sleep一會(huì),這時(shí)候主線程還獲取執(zhí)行權(quán),繼續(xù)向下執(zhí)行36 t.flag=true;//標(biāo)記為true,這時(shí)候會(huì)運(yùn)行show方法中的代碼37 new Thread(t).start();38 }39 }在一個(gè)類中只有一個(gè)同步,可以使用同步函數(shù)。如果有多同步,必須使用同步代碼塊,來確定不同的鎖。所以同步代碼塊相對(duì)靈活一些。
四、多線程下的單例設(shè)計(jì)模式
餓漢式不存在線程安全問題,因?yàn)椴僮鞯拇a只有一句。
1 class Single 2 {3 private Single(){}4 private static final Single s=new Single();5 public static Single getInstance(){6 return s;7 }8 }懶漢式存在安全問題,可以使用同步函數(shù)解決。若直接使用同步函數(shù),則效率較低,因?yàn)槊看味夹枰袛唷?赏ㄟ^雙重判斷的方法解決效率問題。 1 class Single 2 { 3 private Single(){} 4 private static Single s=null; 5 public static Single getInstance(){//如果在函數(shù)上加上synchronized就出現(xiàn)了每次都判斷鎖 6 if(s==null){//雙重判斷是否為空可以解決低效問題。 7 synchronized(Single.class){ 8 if(s==null){ 9 s=new Single(); 10 }11 } 12 } 13 return s;14 }15 }五、死鎖
所謂的死鎖就是同步中嵌套同步,在開發(fā)過程中避免發(fā)生死鎖現(xiàn)象。演示:制造一個(gè)死鎖程序。 1 class DeadLock implements Runnable 2 { 3 private boolean flag; 4 DeadLock(boolean flag){ 5 this.flag=flag; 6 } 7 public void run(){ 8 if(flag){ 9 while (true)10 {11 synchronized(MyLock.locka){//同步代碼塊中嵌套同步代碼塊12 System.out.println("if....locka...");13 synchronized(MyLock.lockb){14 System.out.println("if....lockb...");15 }16 }17 } 18 }19 else{20 while (true)21 {22 synchronized(MyLock.lockb){23 System.out.println("else-----lockb--");24 synchronized(MyLock.locka){25 System.out.println("else-----locka---");26 }27 }28 } 29 }30 } 31 }32 class MyLock33 {34 public static MyLock locka=new MyLock();35 public static MyLocks lockb=new MyLock();36 }37 class DeadLockDemo 38 {39 public static void main(String[] args) 40 {41 new Thread(new DeadLock(true)).start();42 new Thread(new DeadLock(false)).start();43 44 }45 }六、線程間通信問題
線程間通信:思路:多個(gè)線程在操作同一個(gè)資源,但是操作的動(dòng)作卻不一樣。
1.將資源封裝成對(duì)象。
2.將線程執(zhí)行的任務(wù)(任務(wù)其實(shí)就是run方法。)也封裝成對(duì)象。
等待/喚醒機(jī)制涉及的方法:1. wait():讓線程處于凍結(jié)狀態(tài),被wait的線程會(huì)被存儲(chǔ)到線程池中。2. notify():?jiǎn)拘丫€程池中的一個(gè)線程(任何一個(gè)都有可能)。3. notifyAll():?jiǎn)拘丫€程池中的所有線程。
這些方法都使用在同步中,因?yàn)橐獙?duì)持有監(jiān)視器(鎖)的線程操作。所以要使用在同步中,因?yàn)橹挥型讲啪哂墟i。
為什么這些操作線程的方法要定義Object類中呢? 因?yàn)檫@些方法在操作同步中線程時(shí),都必須要標(biāo)識(shí)它們所操作線程持有的鎖,只有同一個(gè)鎖上的被等待線程,可以被同一個(gè)鎖上notify喚醒。不可以對(duì)不同鎖中的線程進(jìn)行喚醒。也就是說,等待和喚醒必須是同一個(gè)鎖。而鎖可以是任意對(duì)象,所以可以被任意對(duì)象調(diào)用的方法定義Object類中。
wait和sleep區(qū)別?1.wait可以指定時(shí)間也可以不指定。sleep必須指定時(shí)間。2.在同步中時(shí),對(duì)CPU的執(zhí)行權(quán)和鎖的處理不同。wait:釋放執(zhí)行權(quán),釋放鎖。sleep:釋放執(zhí)行權(quán),不釋放鎖。
(1)、一個(gè)生產(chǎn)者,一個(gè)消費(fèi)者。即: 一個(gè)線程負(fù)責(zé)生產(chǎn),一個(gè)線程負(fù)責(zé)消費(fèi)
代碼演示:
1 class Resource 2 { 3 private String name; 4 private String sex; 5 private boolean flag; 6 public synchronized void set(String name,String sex){ 7 if(flag)//如果為真,執(zhí)行下面wait方法,線程處于等待狀態(tài),釋放了執(zhí)行權(quán)并且放棄了鎖。 8 try{this.wait();}catch(Exception e){} 9 //如果標(biāo)記不為true,那么往里面存數(shù)據(jù)。10 this.name=name;11 this.sex=sex;12 flag=true;13 this.notify();//喚醒線程池中第一個(gè)等待的線程14 }15 public synchronized void out(){16 if(!flag)17 try{this.wait();}catch(Exception e){}18 System.out.println(name+"...."+sex);19 flag=false;20 this.notify();21 }22 }23 class Input implements Runnable24 {25 private Resource r;//為了保證操作的是同一資源,所以把資源對(duì)象傳參進(jìn)去26 Input(Resource r){27 this.r=r;28 }29 public void run(){30 int x=0;31 while (true)32 {33 if(x==0)34 r.set("mike","man"); 35 else36 r.set("麗麗","女女女女女女");37 x=(x+1)%2;//切換輸入內(nèi)容38 }39 }40 }41 class Output implements Runnable42 {43 private Resource r;44 Output(Resource r){45 this.r=r;46 }47 public void run(){48 while (true)49 {50 r.out();51 }52 } 53 }54 class ResourceDemo55 {56 public static void main(String[] args) 57 {58 Resource r=new Resource();//創(chuàng)建資源對(duì)象59 new Thread(new Input(r)).start();//開啟兩個(gè)線程60 new Thread(new Output(r)).start();61 }62 }(2)、多生產(chǎn)--多消費(fèi)問題
上面的例子是一個(gè)輸入一個(gè)輸出,現(xiàn)在是多個(gè)線程執(zhí)行輸入同時(shí)多條線程執(zhí)行輸出。即:多個(gè)線程操作生產(chǎn),多個(gè)線程操作消費(fèi)。這是開發(fā)中最常見的,希望能夠掌握。
1 class Resource 2 { 3 private String name; 4 private int count=1; 5 private boolean flag=false; 6 public synchronized void set(String name){ 7 while(flag)//循環(huán)判斷標(biāo)記來防止線程等待被喚醒后往下執(zhí)行,直接生產(chǎn)覆蓋上一個(gè)數(shù)據(jù)。 8 try{this.wait();}catch(Exception e){} 9 this.name=name+"....."+count++;10 System.out.println(Thread.currentThread().getName()+"..生產(chǎn)者.."+this.name);11 flag=true;12 this.notifyAll();//喚醒線程池中的全部線程13 }14 public synchronized void out(){15 while(!flag)16 try{this.wait();}catch(Exception e){}17 System.out.println(Thread.currentThread().getName()+"----消費(fèi)者-------"+this.name);18 flag=false;19 this.notifyAll();//喚醒線程池中的全部線程20 }21 }22 class Producer implements Runnable23 {24 private Resource r;25 Producer(Resource r){26 this.r=r;27 }28 public void run(){29 while (true)30 {31 r.set("商品");32 }33 }34 }35 class Consumer implements Runnable36 {37 private Resource r;38 Consumer(Resource r){39 this.r=r;40 }41 public void run(){42 while (true)43 {44 r.out();45 }46 }47 }48 class ProducerConsumerDemo49 {50 public static void main(String[] args) 51 {52 Resource r=new Resource();53 new Thread(new Producer(r)).start();54 new Thread(new Producer(r)).start();55 new Thread(new Consumer(r)).start();56 new Thread(new Consumer(r)).start();57 58 }59 }七、線程間通信-生產(chǎn)者消費(fèi)者JDK1.5升級(jí)版
上面的程序雖然能夠解決問題,但是發(fā)現(xiàn)每次都喚醒全部線程,這樣會(huì)導(dǎo)致線程都去搶奪cpu執(zhí)行權(quán),效率有點(diǎn)低,所以JDK在升級(jí)1.5的時(shí)候推出了一個(gè)接口Lock接口。它的出現(xiàn)就替代了同步代碼塊或者同步函數(shù),將同步的隱式操作變成顯示鎖操作。同時(shí)更為靈活,可以一個(gè)鎖上加上多組監(jiān)視器。。
在之前的版本中使用Object類中wait、notify、notifyAll的方法來完成的,那是因?yàn)橥街械逆i是任意對(duì)象,所以操作時(shí)的等待喚醒的方法是都是定義在Object類中。而現(xiàn)在鎖是指定對(duì)象Lock,所以查找等待喚醒機(jī)制方式需要通過Lock接口來完成.而Lock接口中并沒有直接操作等待喚醒的方法,而是將這些方法又單獨(dú)的封裝了一個(gè)對(duì)象中,這個(gè)對(duì)象就是Condition,將Object中的三個(gè)方法進(jìn)行了單獨(dú)的封裝,并提供了功能一致的方法await()、signal()、signalAll()。所以Condition接口出現(xiàn)替代了Object中的wait、notify、notifyAll方法。將這些監(jiān)視器方法單獨(dú)進(jìn)行了封裝,變成Condition監(jiān)視器對(duì)象,可以任意鎖進(jìn)行組合。
Lock接口
1 Lock lock=new ReentrantLock();//創(chuàng)建Lock對(duì)象2 void lock();//獲取鎖。3 void unlock();//釋放鎖,為了防止異常出現(xiàn),導(dǎo)致鎖無法被關(guān)閉,所以鎖的關(guān)閉動(dòng)作要放在finally中。4 Condition newCondition();//獲取Condition對(duì)象
Condition接口
1 void await();//對(duì)應(yīng)于Object中的wait方法2 void signal();//對(duì)應(yīng)于Object中的notify方法3 void signalAll();//對(duì)應(yīng)于Object中的notifyAll方法
JDK1.5多生產(chǎn)--多消費(fèi)問題代碼
1 import java.util.concurrent.locks .*; 2 class Resource 3 { 4 private boolean flag; 5 private Lock lock=new ReentrantLock();//創(chuàng)建Lock對(duì)象 6 private Condition procondition=lock.newCondition();//創(chuàng)建Condition對(duì)象 7 private Condition concondition=lock.newCondition(); 8 private int count=1; 9 private String name;10 public void set(String name){11 lock.lock();//顯示的加鎖12 try13 {14 while(flag)15 procondition.await();//await替換了wait;16 this.name=name+"..."+count++;17 System.out.println(Thread.currentThread().getName()+"..生產(chǎn)者.."+this.name);18 flag=true;19 concondition.signal();//signal替換了notify20 }21 catch (Exception e)22 {23 System.out.println(e);24 }25 finally{26 lock.unlock();//釋放鎖,釋放資源的動(dòng)作一定要放在finally塊中27 }28 29 }30 public void out(){31 lock.lock();32 try33 {34 while(!flag)35 concondition.await();36 System.out.println(Thread.currentThread().getName()+"------消費(fèi)者-----"+this.name);37 flag=false;38 procondition.signal();39 }40 catch (Exception e)41 {42 System.out.println(e);43 }44 finally{45 lock.unlock();46 }47 }48 }49 class Producer implements Runnable50 {51 private Resource r;52 Producer(Resource r){53 this.r=r;54 }55 public void run(){ 56 while (true)57 { 58 r.set("商品");59 }60 }61 }62 class Consumer implements Runnable63 {64 private Resource r;65 Consumer(Resource r){66 this.r=r;67 }68 public void run(){69 while (true)70 {71 r.out();72 }73 }74 }75 class ProducerConsumerDemo2 76 {77 public static void main(String[] args) 78 {79 Resource r=new Resource();80 new Thread(new Producer(r)).start();81 new Thread(new Producer(r)).start();82 new Thread(new Consumer(r)).start();83 new Thread(new Consumer(r)).start();84 }85 }八、Thread類中其他方法
1、停止線程
查看API發(fā)現(xiàn)線程停止方法stop方法已經(jīng)過時(shí)了,所以我們無法在使用。其實(shí)讓線程停止的原理就是:run方法結(jié)束。
開啟多線程運(yùn)行,運(yùn)行代碼通常是循環(huán)結(jié)構(gòu)。只要控制住循環(huán),就可以讓run方法結(jié)束,也就是線程結(jié)束。
所以run方法中通常定義循環(huán),指定控制住循環(huán)線程即可結(jié)束。
特殊情況:當(dāng)線程處于了凍結(jié)狀態(tài)。就不會(huì)讀取到標(biāo)記。那么線程就不會(huì)結(jié)束。當(dāng)沒有指定的方式讓凍結(jié)的線程恢復(fù)到運(yùn)行狀態(tài)是,這時(shí)需要對(duì)凍結(jié)進(jìn)行清除。強(qiáng)制讓線程恢復(fù)到運(yùn)行狀態(tài)中來。這樣就可以操作標(biāo)記讓線程結(jié)束。Thread類提供該方法 interrupt(),interrupt()方法將線程從凍結(jié)狀態(tài)強(qiáng)制恢復(fù)到運(yùn)行狀態(tài)中來,讓線程具備CPU的執(zhí)行資格。強(qiáng)制動(dòng)作會(huì)發(fā)生InterruptedException,一定要記得處理。
1 class StopThread implements Runnable{ 2 private boolean flag = true; 3 public void run(){ 4 while(flag ){ 5 System. out.println(Thread.currentThread().getName() + "..."); 6 } 7 } 8 public void setFlag(){ 9 flag = false ;10 }11 }12 13 class StopThreadDemo{14 public static void main(String[] args){15 StopThread st = new StopThread();16 new Thread(st).start();17 new Thread(st).start(); 18 19 int num = 0;20 while(true){21 if(num++ == 50){22 st.setFlag();23 break;24 }25 System. out.println(Thread.currentThread().getName()+"..."+ num);26 }27 System. out.println("over" );28 }29 } 1 class StopThread implements Runnable{ 2 private boolean flag = true; 3 public synchronized void run(){ 4 while(flag){ 5 try{ 6 wait(); 7 } catch(Exception e){ 8 System.out.println(e); 9 flag = false;10 }11 System.out.println(Thread.currentThread().getName() );12 }13 }14 public void setFlag(){15 flag = false;16 }17 }18 19 class StopThreadDemo{20 public static void main(String[] args){21 StopThread st = new StopThread();22 Thread t1 = new Thread(st).start();23 Thread t2 = new Thread(st).start(); 24 int num = 0;25 while(true){26 if(num++ == 50){27 t1.interrupt();28 t2.interrupt();29 break;30 }31 System.out.println( Thread.currentThread().getName()+"..." + num);32 }33 System.out.println( "over");34 }35 }2、守護(hù)線程
setDaemon,將該線程標(biāo)記為守護(hù)線程或用戶線程。正在運(yùn)行的線程都是守護(hù)線程時(shí),Java 虛擬機(jī)退出。該方法必須在啟動(dòng)線程前調(diào)用。setDaemon(boolean):將線程標(biāo)記為后臺(tái)線程,后臺(tái)線程和前臺(tái)線程一樣,開啟,一樣搶執(zhí)行權(quán)運(yùn)行,只有在結(jié)束時(shí),有區(qū)別,當(dāng)前臺(tái)線程都運(yùn)行結(jié)束后,后臺(tái)線程會(huì)自動(dòng)結(jié)束。
3、join方法
當(dāng)A線程執(zhí)行到了B線程的.join()方法時(shí),A就會(huì)等待。等B線程都執(zhí)行完,A才會(huì)執(zhí)行。join可以用來臨時(shí)加入線程執(zhí)行。
4、setPriority方法
用來設(shè)置優(yōu)先級(jí)
MAX_PRIORITY最高優(yōu)先級(jí)10
MIN_PRIORITY 最低優(yōu)先級(jí)1
NORM_PRIORITY分配給線程的默認(rèn)優(yōu)先級(jí)
5、yield方法
暫停當(dāng)前線程,讓其他線程執(zhí)行,可以讓線程釋放執(zhí)行權(quán)
6、toString方法
返回該線程的字符串表示形式,包括線程名稱、優(yōu)先級(jí)和線程組
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注