1. 程式人生 > 實用技巧 >JUC 併發程式設計--02,生產者和消費者 synchronized的寫法 , juc的寫法

JUC 併發程式設計--02,生產者和消費者 synchronized的寫法 , juc的寫法

synchronized的寫法

class PCdemo{
    public static void main(String[] args) {
        //多個執行緒操作同一資源
        Data data = new Data();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread-1").start();


        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread-2").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread-3").start();


        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread-4").start();
    }
}
//這是一個資源類,
class Data {
    private int num = 0;
    //加1
    public synchronized void increment() throws InterruptedException {
        while(num != 0){
            this.wait();
        }
        num++;
        System.out.println("當前執行緒名字:" + Thread.currentThread().getName() + "加1 操作, num為" + num);
        this.notifyAll();
    }
    //減1
    public synchronized void decrement() throws InterruptedException {
        while(num == 0){
            this.wait();
        }
        num--;
        System.out.println("當前執行緒名字:" + Thread.currentThread().getName() + "減1 操作, num為" + num);
        this.notifyAll();
    }
}

結果:

這裡需要注意一個概念: 虛假喚醒,就是說執行緒被喚醒了, 但不會被通知 如果把資源類Data中的 increment, decrement方法中的while 換為: if, 那麼執行的時候, 二個執行緒的結果是正常的, 如果二個以上就會出錯,結果為

JUC 版本的 生產者和消費者問題

public class JucPCdemo {
    public static void main(String[] args) {
        //JUC 版本的 就是來替代 synchronized版本的
        DataJ data = new DataJ();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread-1").start();


        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread-2").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread-3").start();


        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread-4").start();
    }

}

class DataJ{
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    private int num = 0;
    //加1
    public void increment() throws InterruptedException {
        //先加鎖
        lock.lock();
        try {
            while(num != 0){
                condition.await();//這個替代 this.wait()
            }
            num++;
            System.out.println("當前執行緒名字:" + Thread.currentThread().getName() + "加1 操作, num為" + num);
            condition.signalAll();// 這個來替代  this.notifyAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //釋放鎖
            lock.unlock();
        }
    }
    //減1
    public void decrement() throws InterruptedException {
        //先加鎖
        lock.lock();
        try {
            while(num == 0){
                condition.await();//這個替代 this.wait();
            }
            num--;
            System.out.println("當前執行緒名字:" + Thread.currentThread().getName() + "減1 操作, num為" + num);
            condition.signalAll();// 這個來替代  this.notifyAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //釋放鎖
            lock.unlock();
        }
    }
}

結果同樣是正確的

然而 Condition 更強大的是精確通知和精確喚醒, 之前的執行結果執行緒之間是隨機執行的,如果讓執行緒 1,2,3,4 依次迴圈有序執行, 就要用到Condition