談談JDK線程的偽喚醒


  在JDK的官方的wait()方法的注釋中明確表示線程可能被“虛假喚醒“,JDK也明確推薦使用while來判斷狀態信息。那么這種情況的發生的可能性有多大呢?

  使用生產者消費者模型來說明,偽喚醒造成的后果是本來未被喚醒的線程被喚醒了,那么就破壞了生產者消費者中的判斷條件,也就是例子中的while條件number == 0或者number == 1。最終導致的結果就死0和1不能交替出現。

  JDK的兩種同步方案均可能出現這種偽喚醒的問題(API說明明確表示會出現這種現象),這兩種組合是synchronized+wait+notify和ReentrantLock+await+signal。下面的例子中,如果把while換成if,那么就0、1就不能交替出現,反制則會,例子中是100個線程進行增加,100個線程進行減少。

  在Java並發編程書上面引用了Thinking In Java的一句話說,大概意思是:任何並發編程都是通過加鎖來解決。其實JDK並發編程也是通過加鎖解決,每個對象都有一個對象鎖,並且有一個與這個鎖相關的隊列,來實現並發編程。區別在於加鎖的粒度問題,讀讀可以並發(在讀遠大於寫的場景下比較合適),其他三種情況不能並發。而關系型數據庫也是利用這一思想,只不過做得更加徹底,除了寫寫不能並發,其他三種情況都能並發,這得益於MVCC模型。

public class Resource {

    public int number = 0;

    public synchronized void add() {
        while (number == 1) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number++;
        System.err.println(number + "-" + Thread.currentThread().getId());
        notifyAll();
    }

    public synchronized void minus() {
        while (number == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number--;
        System.err.println(number + "-" + Thread.currentThread().getId());
        notifyAll();
    }

    public static class AddThread implements Runnable {

        private Resource resource;

        public AddThread(Resource resource) {
            this.resource = resource;
        }

        @Override
        public void run() {
            for (;;) resource.add();
        }
    }

    public static class MinusThread implements Runnable {
        private Resource resource;

        public MinusThread(Resource resource) {
            this.resource = resource;
        }

        @Override
        public void run() {
            for (;;) resource.minus();
        }
    }

    public static void main(String[] args) {
        Resource resource = new Resource();
        for (int i = 0; i < 100; i++) {
            new Thread(new AddThread(resource)).start();
            new Thread(new MinusThread(resource)).start();
        }
    }
}
public class ResourceLock {

    private int number = 0;

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public int getNumber() {
        return this.number;
    }

    public void increase() {
        lock.lock();
        try {
            while (number == 1) {
                condition.await();
            }
            number++;
            System.err.println(number + "-" + Thread.currentThread().getId());
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    public void decrease() {
        lock.lock();
        try {
            while (number == 0) {
                condition.await();
            }
            number--;
            System.err.println(number + "-" + Thread.currentThread().getId());
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    static class IncreaseThread implements Runnable {
        private ResourceLock resource;

        public IncreaseThread(ResourceLock resource) {
            this.resource = resource;
        }

        @Override
        public void run() {
            for (;;) resource.increase();
        }
    }

    static class DecreaseThread implements Runnable {
        private ResourceLock resource;

        public DecreaseThread(ResourceLock resource) {
            this.resource = resource;
        }

        @Override
        public void run() {
            for (;;) resource.decrease();
        }
    }

    public static void main(String[] args) throws Exception {
        ResourceLock resource = new ResourceLock();
        ExecutorService es = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 100; i++) {
            es.submit(new IncreaseThread(resource));
            es.submit(new DecreaseThread(resource));
        }
    }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM