黑马程序员——java基础---多线程(二)
------Java培訓(xùn)、Android培訓(xùn)、iOS培訓(xùn)、.Net培訓(xùn)、期待與您交流! -------
?線程間的通信:簡(jiǎn)單來(lái)說(shuō),就是多個(gè)線程在操作同一資源,但操作的動(dòng)作不同。
試想一下,對(duì)于同一個(gè)資源做不同的操作,這勢(shì)必會(huì)在操作的過(guò)程中產(chǎn)生矛盾。為了避免這種情況的發(fā)生,就需要用的synchronized來(lái)保證,每次對(duì)共享資源的操作,只能是一條線程在進(jìn)行。在用到同步的時(shí)候,就會(huì)因需求問(wèn)題用到wait()、notify()、notifyAll()這三個(gè)方法。
wait()方法:作用是使調(diào)用線程自動(dòng)釋放CPU使用權(quán),從而使線程本身進(jìn)入線程池繼續(xù)等待。
notify()方法:由運(yùn)行的線程喚醒線程池中排在最前面的等待線程,使該線程具有使用CPU的資格。
notifyAll()方法:由運(yùn)行的線程喚醒線程池中的所有等待線程。
因?yàn)槿齻€(gè)方法都需以對(duì)持有(鎖)監(jiān)視器的對(duì)象操作,也就使他們只能在同步中使用,因?yàn)橹挥型街胁艜?huì)出現(xiàn)鎖這個(gè)概念。而通過(guò)查閱API,我們很容易就發(fā)現(xiàn)這三個(gè)方法都被定義在Object類當(dāng)中,那么這些操作鎖的方法為什么會(huì)被定義在Object類中呢?我們可以這樣理解,同步中對(duì)于鎖的標(biāo)志位可以是任意的特有對(duì)象,而這三個(gè)方法都是操作鎖的,也就是說(shuō)他們可以操作任何對(duì)象,Java中能操作任何對(duì)象的類也只有Object類了。
雖然有這些方法來(lái)使同步機(jī)制更加完善,但是對(duì)于某些情況同步機(jī)制并不能完美的完成相應(yīng)的功能。
針對(duì)以下代碼:
1 * 2 * 生產(chǎn)者消費(fèi)者問(wèn)題 3 */ 4 public class ProducerConsumerDemo{ 5 public static void main(String[] args){ 6 Resource r = new Resource(); 7 Producer pro = new Producer(r); 8 Consumer con = new Consumer(r); 9 10 Thread t1 = new Thread(pro); 11 Thread t3 = new Thread(pro); 12 Thread t2 = new Thread(con); 13 Thread t4 = new Thread(con); 14 15 t1.start(); 16 t2.start(); 17 t3.start(); 18 t4.start(); 19 } 20 } 21 //定義共享的資源類 22 class Resource{ 23 private String name; 24 private int count = 1; 25 private boolean flag = false; 26 27 public synchronized void set(String name){ 28 while(flag) 29 try{ 30 this.wait(); 31 }catch(Exception e){ 32 33 } 34 this.name = name+"--"+count++; 35 System.out.println(Thread.currentThread().getName()+"---生產(chǎn)者---"+this.name); 36 flag = true; 37 this.notifyAll(); 38 } 39 public synchronized void out(){ 40 while(!flag) 41 try{ 42 wait(); 43 }catch(Exception e){ 44 } 45 System.out.println(Thread.currentThread().getName()+"-----消費(fèi)者-----"+this.name); 46 flag = false; 47 this.notifyAll(); 48 } 49 } 50 //定義生產(chǎn)者類 51 class Producer implements Runnable{ 52 private Resource res; 53 Producer(Resource res){ 54 this.res = res; 55 } 56 public void run(){ 57 while(true){ 58 res.set("+商品+"); 59 } 60 } 61 } 62 //定義消費(fèi)者類 63 class Consumer implements Runnable{ 64 private Resource res; 65 Consumer(Resource res){ 66 this.res = res; 67 } 68 public void run(){ 69 while(true){ 70 res.out(); 71 } 72 } 73 }在這個(gè)類中雖然可以初步滿足生產(chǎn)者消費(fèi)者這一功能需求,但我們可以發(fā)現(xiàn)在代碼中我們用了notifyAll()這個(gè)方法,這個(gè)方法每運(yùn)行一次將會(huì)把所有的等待狀態(tài)的線程全部喚醒,這會(huì)給計(jì)算機(jī)帶來(lái)很大的耗費(fèi),這種情況顯然并不是我們所期望的。
?針對(duì)上述問(wèn)題,官方在JDK1.5版本中進(jìn)行了升級(jí):
* 引進(jìn)了Lock接口從而替代了synchronized * 用Condition 替代了 Object監(jiān)視器方法這樣做的好處就是:一定程度上簡(jiǎn)化了同步代碼的編寫;一個(gè)Condition對(duì)象可以對(duì)不同鎖進(jìn)行操作,而且可以根據(jù)需求對(duì)特定的線程進(jìn)行喚醒。而同時(shí)也要注意:一定要在對(duì)應(yīng)的程序代碼尾部執(zhí)行釋放鎖的操作。下面是通過(guò)運(yùn)用Lock和Condition改進(jìn)后的代碼:
1 import java.util.concurrent.locks.Condition; 2 import java.util.concurrent.locks.ReentrantLock; 3 4 /* 5 * 生產(chǎn)者消費(fèi)者問(wèn)題 6 */ 7 public class ProducerConsumerDemo2{ 8 public static void main(String[] args){ 9 Resource2 r = new Resource2(); 10 Producer2 pro = new Producer2(r); 11 Consumer2 con = new Consumer2(r); 12 13 Thread t1 = new Thread(pro); 14 Thread t3 = new Thread(pro); 15 Thread t2 = new Thread(con); 16 Thread t4 = new Thread(con); 17 18 t1.start(); 19 t2.start(); 20 t3.start(); 21 t4.start(); 22 } 23 } 24 //定義共有的資源 25 class Resource2{ 26 private String name; 27 private int count = 1; 28 private boolean flag = false; 29 30 private ReentrantLock lock = new ReentrantLock(); 31 Condition c_pro = lock.newCondition(); 32 Condition c_con = lock.newCondition(); 33 34 public void set(String name) throws InterruptedException{ 35 lock.lock(); 36 try{ 37 while(flag) 38 c_pro.await(); 39 this.name = name+"--"+count++; 40 System.out.println(Thread.currentThread().getName()+"---生產(chǎn)者---"+this.name); 41 flag = true; 42 c_con.signal(); 43 }finally{ 44 lock.unlock(); 45 } 46 } 47 public void out() throws InterruptedException{ 48 lock.lock(); 49 try{ 50 while(!flag) 51 c_con.await(); 52 System.out.println(Thread.currentThread().getName()+"-----消費(fèi)者-----"+this.name); 53 flag = false; 54 c_pro.signal(); 55 }finally{ 56 lock.unlock(); 57 } 58 } 59 } 60 //定義生產(chǎn)者 61 class Producer2 implements Runnable{ 62 private Resource2 res; 63 Producer2(Resource2 r){ 64 this.res = r; 65 } 66 public void run(){ 67 while(true){ 68 try { 69 res.set("+商品+"); 70 } catch (InterruptedException e) { 71 // TODO Auto-generated catch block 72 e.printStackTrace(); 73 } 74 } 75 } 76 } 77 //定義消費(fèi)者 78 class Consumer2 implements Runnable{ 79 private Resource2 res; 80 Consumer2(Resource2 r){ 81 this.res = r; 82 } 83 public void run(){ 84 while(true){ 85 try { 86 res.out(); 87 } catch (InterruptedException e) { 88 e.printStackTrace(); 89 } 90 } 91 } 92 }
?
接下來(lái)是關(guān)于線程的停止:1、正常的情況是,等待線程運(yùn)行完,自動(dòng)釋放CPU占用權(quán)。這種方式也是最為安全的。
2、多線程中常用循環(huán)控制語(yǔ)句,我們可以通過(guò)設(shè)置標(biāo)志位,來(lái)使線程在不滿足條件的情況下自動(dòng)釋放CPU資源,停止運(yùn)行。
3、對(duì)于占有資源但卻因?yàn)槟撤N原因進(jìn)入凍結(jié)的線程,我們可以使用interrupt()方法來(lái)打斷其狀態(tài),進(jìn)而使之可以正常運(yùn)行釋放資源。
設(shè)置守護(hù)進(jìn)程的方法:通過(guò)調(diào)用SetDaemon()方法,可以把對(duì)應(yīng)進(jìn)程設(shè)置成守護(hù)進(jìn)程或者是用戶進(jìn)程。
join()方法:作用是長(zhǎng)期占有CPU使用權(quán)。原理是:當(dāng)A線程執(zhí)行到了B線程的join方法時(shí),A就會(huì)等待,等B線程執(zhí)行完,A才會(huì)執(zhí)行。Join可以用來(lái)臨時(shí)加入線程執(zhí)行。
代碼示例:
1 class Demo implements Runnable{ 2 public void run(){ 3 for(int x=0; x<20; x++){ 4 System.out.println(Thread.currentThread().getName()+"---"+x); 5 } 6 } 7 } 8 public class JoinTest { 9 public static void main(String[] args) throws InterruptedException { 10 Demo d = new Demo(); 11 Thread t1 = new Thread(d); 12 Thread t2 = new Thread(d); 13 14 t1.start(); 15 t1.join(); 16 t2.start(); 17 18 for(int x=0; x<30; x++){ 19 System.out.println("main--"+x); 20 } 21 System.out.println("over"); 22 } 23 }
?
? 執(zhí)行結(jié)果:1 Thread-0---0 2 Thread-0---1 3 Thread-0---2 4 Thread-0---3 5 Thread-0---4 6 Thread-0---5 7 Thread-0---6 8 Thread-0---7 9 Thread-0---8 10 Thread-0---9 11 Thread-0---10 12 Thread-0---11 13 Thread-0---12 14 Thread-0---13 15 Thread-0---14 16 Thread-0---15 17 Thread-0---16 18 Thread-0---17 19 Thread-0---18 20 Thread-0---19 21 main--0 22 Thread-1---0 23 main--1 24 Thread-1---1 25 Thread-1---2 26 Thread-1---3 27 Thread-1---4 28 Thread-1---5 29 Thread-1---6 30 Thread-1---7 31 main--2 32 Thread-1---8 33 Thread-1---9 34 main--3 35 Thread-1---10 36 main--4 37 Thread-1---11 38 Thread-1---12 39 Thread-1---13 40 Thread-1---14 41 Thread-1---15 42 Thread-1---16 43 main--5 44 main--6 45 main--7 46 main--8 47 main--9 48 main--10 49 main--11 50 main--12 51 main--13 52 main--14 53 main--15 54 main--16 55 main--17 56 Thread-1---17 57 main--18 58 Thread-1---18 59 main--19 60 Thread-1---19 61 main--20 62 main--21 63 main--22 64 main--23 65 main--24 66 main--25 67 main--26 68 main--27 69 main--28 70 main--29 71 over
我們可以發(fā)現(xiàn)只有當(dāng)線程t1執(zhí)行完以后,main線程才會(huì)和t2線程繼續(xù)強(qiáng)度CPU的使用權(quán),進(jìn)而相繼輸出。所以可以理解為join()方法是線程具有了長(zhǎng)期占有CPU的資格。
? 關(guān)于線程優(yōu)先級(jí)的知識(shí):
1、線程的優(yōu)先級(jí)設(shè)置范圍為(1-10);
? ?2、線程默認(rèn)的優(yōu)先級(jí)是5;
? ? ? ? ? ?3、我們可以通過(guò)SetPriority(1-10)來(lái)對(duì)線程的優(yōu)先級(jí)進(jìn)行設(shè)置。
4、因?yàn)橄噜彽膬?yōu)先級(jí)效果差別很小,說(shuō)以一般提倡對(duì)優(yōu)先級(jí)設(shè)置一下幾個(gè)值:Thread.MAX_PRIORITY 10 ;Thread.MIN_PRIORITY 1;Thread.NORM_PRIORITY 5 yield()方法:暫停當(dāng)前正在執(zhí)行的線程,轉(zhuǎn)而去執(zhí)行其它線程。 開發(fā)中應(yīng)用:保證以下三個(gè)代碼同時(shí)運(yùn)行。 案例代碼: 1 new Thread(){ 2 for(int x=0;x<100;x++){ 3 sop(Thread.currentThread().getName()) 4 } 5 }.start(); 6 7 for(int x=0;x<100;x++){ 8 sop(Thread.currentThread().getName()) 9 } 10 11 Runnable r=new Runnable(){ 12 public voud run(){ 13 for(int x=0;x<100;x++){ 14 sop(Thread.currentThread().getName()) 15 } 16 } 17 }; 18 19 new Thread(r).start();?
?
轉(zhuǎn)載于:https://www.cnblogs.com/shadowW-W/p/4647051.html
總結(jié)
以上是生活随笔為你收集整理的黑马程序员——java基础---多线程(二)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: windows系统路径环境变量
- 下一篇: Yii CDBCriteria常用方法