国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

黑馬程序員----java基礎(chǔ)--多線程

2019-11-15 01:12:43
字體:
供稿:網(wǎng)友
黑馬程序員----java基礎(chǔ)--多線程

------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í)和線程組


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 浠水县| 嘉兴市| 湖口县| 揭阳市| 清原| 乾安县| 高安市| 三门县| 荆门市| 漳浦县| 商水县| 建昌县| 松阳县| 鸡东县| 绥芬河市| 和林格尔县| 青龙| 棋牌| 秦安县| 沙坪坝区| 康定县| 长丰县| 郁南县| 拉萨市| 建湖县| 宜兰县| 大同市| 霍林郭勒市| 孝昌县| 册亨县| 黔东| 阳泉市| 尉犁县| 同德县| 基隆市| 驻马店市| 秦皇岛市| 耒阳市| 洪雅县| 新绛县| 无极县|