日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

线程之间的协作

發布時間:2024/9/30 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 线程之间的协作 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

當我們使用線程來同時運行多個任務時,可以通過使用鎖(互斥)來同步兩個任務的行為,從而使得一個任務不會干涉另一個任務的資源。也就是說如果兩個任務交替著進入某項共享資源,你可以使用互斥來使得此時此刻只有一個任務訪問這項資源。有的時候我們并不是要使線程之間互斥,而是希望它們之間能夠彼此協作,使得多個任務之間可以一起工作去解決問題。

當任務協作時,關鍵的問題就是這些任務之間的握手。為了實現這種握手,必須要利用線程之間的基礎特性:互斥。在這種情況下,互斥可以確保只有一個任務響應某個信號,這樣就可以解決它們之間的競爭關系。

然而如果只是互斥并不能完成任務之間的協作,在互斥的基礎上添加了一種新的途徑,使得它們可以使自身掛起,直至某些外部條件發生變化,表示這個任務是時候繼續執行了。這種握手可以通過Object 的方法wait() 、notify() 和notifyAll() 來安全的實現。在Java SE5 并發庫中還提供了await() 和 signal() 的Condition 對象。

wait() 與notifyAll()

wait() 會在等待外部條件改變時將任務掛起,并且只有在notify() 與notifyAll() 發生時,這個任務就會被喚醒去檢查所發生的變化。因此wait() 提供了一種在任務之間對活動進行同步的方式。

這里需要注意的是:當我們調用sleep() 的時候鎖并沒有被釋放,調用yield() 方法時也是這種情況,這一點很重要,當一個任務中調用了wait() 的時候,線程的執行將會被掛起,該對象上的鎖將被釋放,也就是說wait() 操作會釋放鎖。這意味著其他的任務將會獲得這個鎖,去執行它的任務。

wait() 有兩種方式,第一種接收毫秒數作為參數,含義與sleep() 方法類似,都是指在制定的時間內“暫停執行”。但是與sleep() 不同的是對于wait() 而言:

[1]:在執行wait() 前進鎖是釋放的
[2]:你可以通過notify() 與notifyAll() ,或者時間到期,可以從wait() 中恢復過來

另一種方式是wait() 不接受任何的參數。這種等待將會一直持續下去直至接收到notify() 與notifyAll() 的消息。

其中wait() 、notify() 與notifyAll() 有一個特殊的地方就是:它們并不是Thread 的一部分,這些方法屬于基類Object。這么做是有道理的(一會你就可以體會到這么做的好處),因為這些方法操作的鎖是所有對象的一部分,你可以把wait() 放在任何同步控制的方法里,而不需要考慮這個類是否繼承了Thread 類或者實現了 Runnable 接口。

notify() 與notifyAll()

使用notify() 時,是指眾多等待同一個鎖的任務中只有一個被喚醒,如果你使用notify() 那么必須要保證被喚醒的是恰當的任務。另一方面為了使用notify() ,所有的任務都必須等待相同的條件,如果你有多個任務在等待多個不同的條件,那么你就不知道是否喚醒了恰當的任務。notifyAll() 會喚醒所有等待這個鎖的任務

案例

在這里用三個具體的案例去實現線程之間的相互協作,第一個案例就是我們在main 方法中實現子方法執行輸出三次后主方法程執行輸出三次,總共循環三次這樣的步驟。在main() 方法中有兩個線程執行,它們之間共同使用model 對象這把鎖。

public class MainThread {public static void main(String[] args) {ThreadModel model = new ThreadModel();new Thread(new Runnable() {@Overridepublic void run() {for(int i=0;i<3;i++)model.subMethod(i+1);}}){}.start();for(int i=0;i<3;i++){model.mainMethod(i+1);}} } class ThreadModel{private boolean b = true; //檢查標志public synchronized void subMethod(int count){while (!b){ //當b 為 false 的時候子方法掛起,并釋放鎖,此時主方法將會獲得該鎖try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//b 為true 的時候子方法獲得鎖后開始進行輸出,將標志置為false 并將主方法的任務喚醒for(int i=0;i<3;i++){System.out.println("子線程執行第"+(i+1)+"次"+"...."+count);}b = false;this.notify();}public synchronized void mainMethod(int count){while (b){ //當b 為true 的時候主方法將掛起,釋放鎖,此時子方法獲得該鎖try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//b 為false 的時候主方法獲得鎖后開始進行輸出,將標志置為true 并將子方法的任務喚醒for(int i = 0;i <3;i++){System.out.println("主線程執行第"+(i+1)+"次"+"...."+count);}b = true; this.notify();} }

輸出:
子線程執行第1次….1
子線程執行第2次….1
子線程執行第3次….1
主線程執行第1次….1
主線程執行第2次….1
主線程執行第3次….1
子線程執行第1次….2
子線程執行第2次….2
子線程執行第3次….2
主線程執行第1次….2
主線程執行第2次….2
主線程執行第3次….2
子線程執行第1次….3
子線程執行第2次….3
子線程執行第3次….3
主線程執行第1次….3
主線程執行第2次….3
主線程執行第3次….3

下面這個例子使用線程池演示了兩個過程:一個是將蠟涂到Car 上,一個是拋光它。拋光任務在涂蠟任務完成之前,是不能執行工作的,涂蠟任務在涂另一層蠟之前必須等待拋光任務完成。

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;class Car{private boolean waxOn = false;public synchronized void waxed(){ //打蠟waxOn = true;notifyAll();}public synchronized void buffed() { //拋光waxOn = false;notifyAll();}public synchronized void waitForWaxing() throws InterruptedException {while (!waxOn)wait();}public synchronized void waitForBuffing() throws InterruptedException {while (waxOn)wait();} }class WaxOn implements Runnable{private Car car;public WaxOn(Car car){this.car = car;}@Overridepublic void run() {try{while (true){System.out.print("Wax On!"); //開始打蠟Thread.sleep(200);car.waxed(); //將標志置為true ,喚醒正在等待的WaxOff中 的任務car.waitForBuffing(); //此時打蠟完成后將掛起等待拋光后被喚醒}} catch (InterruptedException e) {System.out.println("Exiting via interrupt");}System.out.println("Ending Wax On task");} }class WaxOff implements Runnable{private Car car;public WaxOff(Car car){this.car = car;}@Overridepublic void run() {try{while (true){car.waitForWaxing(); //如果waxOn 為false 時掛起,釋放鎖,下面任務不執行,當waxOn時被喚醒System.out.print("Wax Off!"); //此時打蠟任務完成Thread.sleep(200);car.buffed(); //將waxOn 置為false 喚醒WaxOn 中的任務}} catch (InterruptedException e) {System.out.println("Exiting via interrupt");}System.out.println("Ending Wax Off task");} }public class WaxOMatic {public static void main(String[] args) throws InterruptedException {Car car = new Car();ExecutorService exec = Executors.newCachedThreadPool();exec.execute(new WaxOff(car));exec.execute(new WaxOn(car));Thread.sleep(1000); exec.shutdownNow(); //1s 之后終止這兩個線程,調用exec.shutdownNow()時會調用控制線程的interrupt()} }

輸出
Wax On!Wax Off!Wax On!Wax Off!Wax On!Exiting via interrupt
Ending Wax Off task
Exiting via interrupt
Ending Wax On task

下面這個示例是一個生產者與消費者的問題,只有在生產完成后才可以被消費,當消費完成后再執行生產任務,并且每次只生產與消費一個資源。

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;class ReSource{private String name;private int count = 1;private boolean flag = false;public synchronized void set(String name){while(true){while(flag){ //循環判斷標記,被喚醒后仍要判斷標記,避免兩個生產者任務掛起時產生數據錯亂try {wait();} catch (InterruptedException e) {System.out.println("生產完成");}}this.name = name + count;count++;System.out.println(Thread.currentThread().getName()+ "...生產者...." + this.name);flag = true;notifyAll(); //喚醒所有的線程,意味著消費者的線程也被喚醒,避免所有線程都處于等待狀態}}public synchronized void out(){while(true){while(!flag){try {wait();} catch (InterruptedException e) {System.out.println("消費完成");}}System.out.println(Thread.currentThread().getName()+"...消費者...." + this.name);flag = false;notifyAll();}} }class Producer implements Runnable{ReSource rs;Producer(ReSource rs){this.rs = rs;}public void run(){rs.set("烤鴨");} }class Consumer implements Runnable{ReSource rs;Consumer(ReSource rs){this.rs = rs;}public void run(){rs.out();}}public class Restaurant {public static void main(String args []) throws InterruptedException {ReSource rs = new ReSource();Producer producer = new Producer(rs); //多個生產者與消費者的案例Consumer consumer = new Consumer(rs);ExecutorService service = Executors.newCachedThreadPool();service.execute(new Thread(producer));service.execute(new Thread(producer));service.execute(new Thread(consumer));service.execute(new Thread(consumer));Thread.sleep(10);service.shutdownNow();System.exit(0);} }

輸出
pool-1-thread-1…生產者….烤鴨1
pool-1-thread-4…消費者….烤鴨1
pool-1-thread-2…生產者….烤鴨2
pool-1-thread-4…消費者….烤鴨2
pool-1-thread-1…生產者….烤鴨3
pool-1-thread-4…消費者….烤鴨3
pool-1-thread-2…生產者….烤鴨4
pool-1-thread-4…消費者….烤鴨4
……………………….

上面三個示例中在判斷標志讓線程掛起的時候我們都是使用了while() 循環,使用while() 循環的好處有很多:

  • 你可能有多個任務出于不同的原因在等待同一個鎖,而第一個喚醒任務可能會改變這種狀況(即使你沒有這么做,但是可能有人繼承你的類去這么做)。如果出現這種情況,那么這個任務應該被再次掛起,直至其感興趣的條件發生變化。
  • 在這個任務從其wait() 被喚醒的時刻,有可能會在某個其他的任務中做出了改變,從而使得這個任務不能執行,或者執行其他的操作顯得無關緊要。此時應該再次調用wait() 將其掛起。
  • 也有可能某些任務出于不同的原因在等待你對象上的鎖。在這種情況下,你需要檢查是否已經由正確的原因喚醒,如果不是就再次掛起。

在示例三中如果不用while() 循環判斷標記,當兩個生產者線程都處于掛起狀態時,如果一個消費者此時完成了消費任務,那么這兩個生產者 都會被喚醒,如果一個生產者得到了資源并完成了生產任務,此時另一個生產者線程得到了執行權,在if() 條件下由于不對標記進行判斷另一個生產者在原來掛起的基礎上得到執行權它也會去執行生成的任務,但是它本來是應該被掛起的。此時就會出現問題,while() 循環則不會,在線程被喚醒后它還會循環區判斷標記,這時候的標記將會引導它執行還是再度掛起。

參考書籍:
《Java 編程思想》Bruce Eckel 著 陳昊鵬 譯

總結

以上是生活随笔為你收集整理的线程之间的协作的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。