AA BB CC AA執行緒列印 ,CC執行緒列印

vbcjnmkk發表於2024-08-31

Lock實現執行緒間定製化通訊
合集 - JUC基礎(13)
1.
JUC前置知識
2023-10-23
2.
Synchronized和Lock介面
2023-10-25
3.
執行緒間通訊
2023-10-28
4.
Lock實現執行緒間定製化通訊
2023-10-30
5.
常用集合執行緒安全分析
2023-11-02
6.
多執行緒鎖
2023-11-12
7.
Callable介面和Future介面
2023-11-20
8.
JUC的強大輔助類
2023-11-22
9.
ReentrantReadWriteLock讀寫鎖
2023-11-24
10.
BlockingQueue阻塞佇列
2023-11-26
11.
執行緒池
2023-11-28
12.
Fork/Join
2023-12-01
13.
CompletableFuture非同步回撥
2023-12-01
收起
Lock實現執行緒間定製化通訊
案例
要求
三個執行緒,AA BB CC AA執行緒列印5次,BB執行緒列印10次,CC執行緒列印15次

程式碼實現
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**

  • @author 長名06

  • @version 1.0

  • 執行緒定製化通訊
    */
    //第一步,建立共享資源,和操作方法
    class ShareFlag {

    private Lock lock = new ReentrantLock();
    private int flag = 1;

    private Condition c1 = lock.newCondition();//一個Condition物件,只能喚醒由該物件阻塞的執行緒
    private Condition c2 = lock.newCondition();
    private Condition c3 = lock.newCondition();

    public void print5(int loop) throws InterruptedException {
    lock.lock();

     try {
         //第四步,避免虛假喚醒現象
         while (flag != 1) {//第2.1步 判斷是否滿足執行緒工作條件,阻塞
             c1.await();
         }
         //第2.2步 具體執行
         for (int i = 0; i < 5; i++) {
             System.out.println(Thread.currentThread().getName() + "\t" + i +"次輸出"
                     + "flag=" + flag + "輪數" + loop);
         }
         flag = 2;
         //通知(喚醒等待執行緒)
         c2.signal();
     } finally {
         lock.unlock();
     }
    

    }

    public void print10(int loop) throws InterruptedException {
    lock.lock();

     try {
    
         while (flag != 2) {
             c2.await();
         }
    
         for (int i = 0; i < 10; i++) {
             System.out.println(Thread.currentThread().getName() + "\t" + i +"次輸出"
                     + "flag=" + flag + "輪數" + loop);
         }
         flag = 3;
         //通知(喚醒等待執行緒)
         c3.signal();
     } finally {
         lock.unlock();
     }
    

    }

    public void print15(int loop) throws InterruptedException {
    lock.lock();

     try {
    
         while (flag != 3) {
             c3.await();
         }
    
         for (int i = 0; i < 15; i++) {
             System.out.println(Thread.currentThread().getName() + "\t" + i +"次輸出"
                     + "flag=" + flag + "輪數" + loop);
         }
         flag = 1;
         //通知(喚醒等待執行緒)
    

//程式碼效果參考:https://www.weibow.com/sitemap/post.xml
c1.signal();
} finally {
lock.unlock();
}

}

}

public class ThreadDemoByCust {
public static void main(String[] args) {
ShareFlag shareFlag = new ShareFlag();

    new Thread(() -> {
        for (int i = 1; i <= 10; i++) {
            try {
                shareFlag.print5(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }, "AA").start();

    new Thread(() -> {
        for (int i = 1; i <= 10; i++) {
            try {
                shareFlag.print10(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }, "BB").start();

    new Thread(() -> {
        for (int i = 1; i <= 10; i++) {
            try {
                shareFlag.print15(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }, "CC").start();
}

}
學習時,關於程式碼的疑問
不理解,為什麼一定是,AA執行緒執行完後,,BB執行緒執行完後,是c3是c2去執行喚醒一個等待執行緒操作去執行喚醒一個等待執行緒的操作,CC執行緒執行完後,是c1執行喚醒一個等待執行緒的操作。先往後面看,後續回來解答這個問題。

解答
是為了滿足要求中的順序,最開始三個AA,BB,CC執行緒,並行執行,因為flag初始值是1,所以AA先執行第一次迴圈,執行print5,然後迴圈輸出5次後,flag = 2了(AA被c1阻塞),為了保證要求,再BB輸出10次,則需要使用c2去喚醒BB執行緒,為什麼是c2,因為BB執行緒在,最開始flag = 1時,是由c2去阻塞的,這裡如果是c3物件阻塞的,則必須要用c3物件喚醒,因為Condition物件,只能喚醒由該物件阻塞的執行緒。後面BB執行緒切換CC執行緒,CC執行緒切換AA執行緒也是同理。

只是為了記錄自己的學習歷程,且本人水平有限,不對之處,請指正。

相關文章