Java学习个人备忘录之线程间的通信
多個(gè)線程在處理同一資源,但是任務(wù)卻不同.? class Resource { String name; String sex; } //輸入 class Input implements Runnable { Resource r; Input(Resource r) { this.r = r; } public void run() { int i = 0; while(true) { synchronized(r) //保證兩個(gè)線程用同一個(gè)鎖 { if (i==0) { r.name = "mike"; r.sex = "nan"; } else { r.name = "麗麗"; r.name = "女女女女女女女女女"; } x = (x+1)%2; } } } } //輸出 class Output implements Runnable { Resource r; Output(Resource r) { this.r = r; } public void run() { while(true) { synchronized(r) //保證兩個(gè)線程用同一個(gè)鎖 { System.out.println(r.name+"....."+r.sex); } } } } class ResourceDemo { public static void main(String[] args) { //創(chuàng)建資源 Resource r = new Resource(); //創(chuàng)建任務(wù) Input in = new Input(r); Output out = new Output(r); //創(chuàng)建線程 Thread t1 = new Thread(in); Thread t2 = new Thread(out); //開啟線程 t1.start(); t2.start(); } }
但是這樣會(huì)造成大量的才重復(fù), 沒有交替性。
?
等待喚醒機(jī)制
涉及的方法:
1. wait(): 讓線程處于凍結(jié)狀態(tài), 被wait的線程會(huì)被存儲(chǔ)到線程池中.
2. notify(): 喚醒線程池中一個(gè)線程(任意)
3. notifyAll(): 喚醒線程池中的所有線程.
這些方法都必須定義在同步中,
因?yàn)檫@些方法都是用于操做線程狀態(tài)的方法.
必須要明確到底操做的是哪個(gè)鎖上的線程.
為什么操做線程的方法wait notify notifyAll定義在了Object類中.
因?yàn)檫@些方法時(shí)監(jiān)視器的方法, 堅(jiān)持其其實(shí)就是鎖.
鎖可以是任意的對(duì)象,任意的對(duì)象調(diào)用的方式一定定義在Object類中的.?
上面代碼的優(yōu)化
class Resource { private String name; //這里要私有化 private String sex; boolean flag = false; public synchronized void set(String name,String sex) //對(duì)數(shù)據(jù)要可控化 { if (this.flag) try{this.wait();}catch(InterruptedException e){} this.name = name; this.sex = sex; flag = true; this.notify(); } public synchronized void out() { if (this.flag) try{this.wait();}catch(InterruptedException e){} System.out.println(name+"....."+sex); flag = false; this.notify(); } } //輸入 class Input implements Runnable { Resource r; Input(Resource r) { this.r = r; } public void run() { int i = 0; while(true) { if (i==0) { r.set("mike","nan"); } else { r.set"麗麗","女女女女女女女女女"); } x = (x+1)%2; } } } //輸出 class Output implements Runnable { Resource r; Output(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ResourceDemo3 { public static void main(String[] args) { //創(chuàng)建資源 Resource r = new Resource(); //創(chuàng)建任務(wù) Input in = new Input(r); Output out = new Output(r); //創(chuàng)建線程 Thread t1 = new Thread(in); Thread t2 = new Thread(out); //開啟線程 t1.start(); t2.start(); } }?
多生產(chǎn)者多消費(fèi)者問題
class Resource { private String name; private int count = 1; private boolean flag = false; public synchronized void set(String name) { if (flag) try{this.wait();}catch(InterruptedException e){} this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+".....生產(chǎn)者....."+this.name); flag = true; notify(); } public synchronized void out() { if (!flag) try{this.wait();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+".....消費(fèi)者....."+this.name); flag = false; notify(); } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while (true) { r.set("烤鴨"); } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }但是這樣會(huì)出現(xiàn)安全隱患, 從這4個(gè)線程上看, 一共分了兩組, t0和t1一組, t2和t3一組, 當(dāng)t1 t2 t3 睡眠時(shí), t0出來后再次喚醒t1, 這時(shí)t1是不用判斷的if條件的,直接向下繼續(xù)執(zhí)行. 這樣就又進(jìn)行了"生產(chǎn)烤鴨", 所以出現(xiàn)了安全隱患. 解決辦法: 將兩個(gè)if 換成 while, 這樣在t1醒來的時(shí)候會(huì)繼續(xù)判斷flag是否為真. 但是這樣又會(huì)出現(xiàn)死鎖現(xiàn)象, 因?yàn)閠1判斷flag時(shí), flag為真, 這時(shí)t1會(huì)再次等待,這時(shí)4個(gè)線程都進(jìn)入等待狀態(tài)---死鎖!!
?
解決辦法1
將notify換成notifyAll, 這樣就一定會(huì)喚醒對(duì)方的線程,同時(shí)自己方的線程因?yàn)閣hile循環(huán)出不去.?
if判斷標(biāo)記只有一次, 會(huì)導(dǎo)致不該運(yùn)行的線程運(yùn)行了, 出現(xiàn)了數(shù)據(jù)錯(cuò)誤的情況.?while判斷標(biāo)記, 解決了線程獲取執(zhí)行權(quán)后, 是否要運(yùn)行。
notify: 只能喚醒一個(gè)線程, 如果本方喚醒了本方, 就沒有意義, 而且while判斷標(biāo)記notify會(huì)導(dǎo)致死鎖.?notifyAll解決了, 本方線程一定會(huì)喚醒對(duì)方線程.?
解決辦法2:
JDK1.5新特征的解決辦法--Lock
可以看出來, 上面的解決方法會(huì)造成多次無用的判斷, 這會(huì)降低效率,可以用這面的方法解決.?
jdk1.5以后將同步和鎖封裝成了對(duì)象.
并將操作鎖的隱式方法定義到了該對(duì)象中,
將隱式動(dòng)作變成了顯示動(dòng)作.
但是如果執(zhí)行的代碼拋出了異常, 這樣代碼就會(huì)一直持有鎖,不釋放,所以要如下
?
import java.util.concurrent.locks.* class Resource { private String name; private int count = 1; private boolean flag = false; Lock l = new ReentrantLock();//因?yàn)長(zhǎng)ock是java.util.concurrent.locks包中的類, 所以要先導(dǎo)入包. public void set(String name) //這里的同步就可以去掉了 { l.lock(); //在這里加上鎖 try { while (flag) try{this.wait();}catch(InterruptedException e){} this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+".....生產(chǎn)者....."+this.name); flag = true; notifyAll(); } finally { l.unlock(); } } public void out() { l.lock(); try { while (!flag) try{this.wait();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+".....消費(fèi)者....."+this.name); flag = false; notifyAll(); } finally { l.unlock(); } } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while (true) { r.set("烤鴨"); } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }解決辦法3:
JDK1.5新特征的解決辦法--Condition
Condition在底層上是這樣實(shí)現(xiàn)的:
interface Condition
{
await();
signal();
signalAll();
}
所以要這樣實(shí)現(xiàn), 如下:
Lock l = new ReectrantLock();
Condition c1 = l.newCondition();
Condition c2 = l.newCondition();
其實(shí)解決辦法3和解決辦法2沒有太大的區(qū)別.并沒有真的運(yùn)用了1.5的新特征。
解決辦法4
這個(gè)解決辦法才真正的運(yùn)用到了1.5的新特征。
?
轉(zhuǎn)載于:https://www.cnblogs.com/y-zr/p/7906007.html
總結(jié)
以上是生活随笔為你收集整理的Java学习个人备忘录之线程间的通信的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最小生成树之Kruskal算法
- 下一篇: Java实现单例模式