天天看點

實作兩個線程交替列印0~100的奇偶數

問題描述

有兩個線程,一個線程隻列印奇數,一個線程隻列印偶數,而且要按照順序列印,就是說要按照這樣的方式列印:

偶線程:0

奇線程:1

偶線程:2

奇線程:3

方案一:使用synchronized關鍵字

要點:

  • 建立兩個線程,一個線程處理偶數,一個線程處理奇數
  • 兩個線程之間通過synchronized進行同步,保證count++每次隻有一個線程進行操作
  • 為什麼兩個線程能交替執行,這裡很巧的是

    count從0123自增過程就是一個奇偶數交替的過程

    ,實際上兩個線程都是在不停的嘗試(while循環)進入synchronized代碼塊,如果滿足相對應的條件(偶數或是奇數)就列印輸出。
public class WaitNotifyPrintOddEvenSyn {

    private static int count;

    private static final Object lock = new Object();

    /**
     * 建立2個線程,第一個隻處理偶數,第二個隻處理奇數(用位運算);用synchronized來通信
     */
    public static void main(String[] args) {
        new Thread(() -> {
            while (count < 100) {
                synchronized (lock) {
                    if ((count & 1) == 0) {
                        System.out.println(Thread.currentThread().getName() + ":" + count++);
                    }
                }
            }
        }, "偶線程").start();

        new Thread(() -> {
            while (count < 100) {
                synchronized (lock) {
                    if ((count & 1) == 1) {
                        System.out.println(Thread.currentThread().getName() + ":" + count++);
                    }
                }
            }
        }, "奇線程").start();
    }
}
           

方案二:使用wait/notify關鍵字(推薦)

要點:

  • 這個相對于上面那個好了解很多,直接走流程,偶數線程拿到鎖列印輸出同時count++,然後進行休眠,因為

    wait()方法的特性,休眠的同時會釋放monitor鎖

    ,奇數線程就可以進來了,進來後列印輸出,同時notify喚醒偶數線程繼續下一輪,奇數線程往下執行wait方法休眠,就這樣,偶數線程喚醒奇數線程,奇數線程喚醒偶數線程,直到滿足count<100條件後,線程不再休眠,直接退出程式。
  • 這個要點一個在于

    wait/notify的等待喚醒機制

    ,一個在于wait()方法的特性,休眠後會釋放鎖。
  • 這種方式和上面那種方式不同點在于,這種方式是

    被動喚醒的機制

    ,而上面那個是線程不斷重試的機制(一直while重試,直到滿足條件就列印),很明顯這種方式優于上面那種!
public class WaitNotifyPrintOddEveWait {

    private static int count = 0;

    private static final Object lock = new Object();

    public static void main(String[] args) {
        new Thread(new TurningRunner(), "偶線程").start();
        new Thread(new TurningRunner(), "奇線程").start();
    }

    /**
     * 1. 拿到鎖,立刻列印
     * 2. 列印完,喚醒其他線程,自己就休眠
     */
    static class TurningRunner implements Runnable {

        @Override
        public void run() {
            while (count < 100) {
                synchronized (lock) {
                    //拿到鎖就列印
                    System.out.println(Thread.currentThread().getName() + ":" + count++);
                    lock.notify();
                    if (count < 100) {
                        try {
                            //如果任務還沒結束,就讓出目前的鎖,并休眠
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}
           

方案三:可以使用ReentrantLock,設定為公平鎖,即等待時間越長的線程先執行

筆記來源:慕課網悟空老師視訊《Java并發核心知識體系精講》

繼續閱讀