Java学习个人备忘录之线程间的通信
多個線程在處理同一資源,但是任務卻不同.? 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) //保證兩個線程用同一個鎖 { 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) //保證兩個線程用同一個鎖 { System.out.println(r.name+"....."+r.sex); } } } } class ResourceDemo { public static void main(String[] args) { //創建資源 Resource r = new Resource(); //創建任務 Input in = new Input(r); Output out = new Output(r); //創建線程 Thread t1 = new Thread(in); Thread t2 = new Thread(out); //開啟線程 t1.start(); t2.start(); } }
但是這樣會造成大量的才重復, 沒有交替性。
?
等待喚醒機制
涉及的方法:
1. wait(): 讓線程處于凍結狀態, 被wait的線程會被存儲到線程池中.
2. notify(): 喚醒線程池中一個線程(任意)
3. notifyAll(): 喚醒線程池中的所有線程.
這些方法都必須定義在同步中,
因為這些方法都是用于操做線程狀態的方法.
必須要明確到底操做的是哪個鎖上的線程.
為什么操做線程的方法wait notify notifyAll定義在了Object類中.
因為這些方法時監視器的方法, 堅持其其實就是鎖.
鎖可以是任意的對象,任意的對象調用的方式一定定義在Object類中的.?
上面代碼的優化
class Resource { private String name; //這里要私有化 private String sex; boolean flag = false; public synchronized void set(String name,String sex) //對數據要可控化 { 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) { //創建資源 Resource r = new Resource(); //創建任務 Input in = new Input(r); Output out = new Output(r); //創建線程 Thread t1 = new Thread(in); Thread t2 = new Thread(out); //開啟線程 t1.start(); t2.start(); } }?
多生產者多消費者問題
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()+".....生產者....."+this.name); flag = true; notify(); } public synchronized void out() { if (!flag) try{this.wait();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+".....消費者....."+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(); } }但是這樣會出現安全隱患, 從這4個線程上看, 一共分了兩組, t0和t1一組, t2和t3一組, 當t1 t2 t3 睡眠時, t0出來后再次喚醒t1, 這時t1是不用判斷的if條件的,直接向下繼續執行. 這樣就又進行了"生產烤鴨", 所以出現了安全隱患. 解決辦法: 將兩個if 換成 while, 這樣在t1醒來的時候會繼續判斷flag是否為真. 但是這樣又會出現死鎖現象, 因為t1判斷flag時, flag為真, 這時t1會再次等待,這時4個線程都進入等待狀態---死鎖!!
?
解決辦法1
將notify換成notifyAll, 這樣就一定會喚醒對方的線程,同時自己方的線程因為while循環出不去.?
if判斷標記只有一次, 會導致不該運行的線程運行了, 出現了數據錯誤的情況.?while判斷標記, 解決了線程獲取執行權后, 是否要運行。
notify: 只能喚醒一個線程, 如果本方喚醒了本方, 就沒有意義, 而且while判斷標記notify會導致死鎖.?notifyAll解決了, 本方線程一定會喚醒對方線程.?
解決辦法2:
JDK1.5新特征的解決辦法--Lock
可以看出來, 上面的解決方法會造成多次無用的判斷, 這會降低效率,可以用這面的方法解決.?
jdk1.5以后將同步和鎖封裝成了對象.
并將操作鎖的隱式方法定義到了該對象中,
將隱式動作變成了顯示動作.
但是如果執行的代碼拋出了異常, 這樣代碼就會一直持有鎖,不釋放,所以要如下
?
import java.util.concurrent.locks.* class Resource { private String name; private int count = 1; private boolean flag = false; Lock l = new ReentrantLock();//因為Lock是java.util.concurrent.locks包中的類, 所以要先導入包. 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()+".....生產者....."+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()+".....消費者....."+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在底層上是這樣實現的:
interface Condition
{
await();
signal();
signalAll();
}
所以要這樣實現, 如下:
Lock l = new ReectrantLock();
Condition c1 = l.newCondition();
Condition c2 = l.newCondition();
其實解決辦法3和解決辦法2沒有太大的區別.并沒有真的運用了1.5的新特征。
解決辦法4
這個解決辦法才真正的運用到了1.5的新特征。
?
轉載于:https://www.cnblogs.com/y-zr/p/7906007.html
總結
以上是生活随笔為你收集整理的Java学习个人备忘录之线程间的通信的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最小生成树之Kruskal算法
- 下一篇: 红黑树(一)之 原理和算法详细介绍---