1. 程式人生 > >Java併發程式設計實戰————Condition

Java併發程式設計實戰————Condition

引言

使用Condition實現生產者-消費者模型,並與wait和notify實現的效果相對比。

wait/notify模擬生產者-消費者

面試題:寫一個固定容量同步容器,擁有put和get方法,以及getCount方法能夠支援2個生產執行緒以及10個消費者執行緒的阻塞呼叫。

在《Effective Java》一書中提到:wait()方法()絕大多數情況下都是和while一起使用的。這是因為,當wait()執行完成後,會立刻釋放當前鎖,如果這時其他執行緒立刻獲得鎖並對變數進行操作,會出現資料不一致的情況,使用while可以確保資料不會出現不一致的情況。

public class ProducerConsumer1<T> {
    final private LinkedList<T> lists = new LinkedList<>();
    final private int MAX = 10;// 最多10個元素
    private int count = 0;

    public synchronized void put(T t) {
        while (lists.size() == this.MAX) {// 想想為什麼用while而不是if
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        lists.add(t);
        count++;
        this.notifyAll();// 通知消費者執行緒進行消費
    }

    public synchronized T get() {
        T t = null;

        while (lists.size() == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        t = lists.removeFirst();
        count--;
        this.notifyAll();// 通知生產者進行生產
        return t;
    }

    public static void main(String[] args) {
        ProducerConsumer1<String> c = new ProducerConsumer1<>();
        // 啟動消費者執行緒
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 5; j++) {
                    System.out.println(c.get());
                }
            }, "c" + i).start();
        }

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                for (int j = 0; j < 25; j++) {
                    c.put(Thread.currentThread().getName() + " " + j);
                }
            }).start();
        }
    }
}

Condition模擬生產者-消費者

使用Lock和Condition來實現類似需求時,可以更加精確的指定哪些執行緒被喚醒,這比notifyAll()效率更高一些。

將上面的程式程式碼進行改寫:

public class ProducerConsumer2<T> {
    final private LinkedList<T> lists = new LinkedList<>();
    final private int MAX = 10;
    private int count = 0;

    private Lock lock = new ReentrantLock();
    private Condition producer = lock.newCondition();
    private Condition consumer = lock.newCondition();

    public void put(T t) {
        try {
            lock.lock();
            while (lists.size() == MAX) {
                producer.await();
            }

            lists.add(t);

            ++count;
            consumer.signalAll(); // 通知消費者執行緒進行消費
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public T get() {
        T t = null;
        try {
            lock.lock();
            while (lists.size() == 0) {
                consumer.await();
            }

            t = lists.removeFirst();
            count--;
            producer.signalAll();// 通知生產者進行生產
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return t;
    }

    public static void main(String[] args) {
        ProducerConsumer2<String> c = new ProducerConsumer2<>();
        // 啟動消費者執行緒
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 5; j++) {
                    System.out.println(c.get());
                }
            }, "c" + i).start();
        }

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                for (int j = 0; j < 25; j++) {
                    c.put(Thread.currentThread()
                                .getName()
                            + " " + j);
                }
            }).start();
        }
    }
}

鳴謝