JUC-02 虚假唤醒&精准唤醒

虚假唤醒

public class Tick {
    public static void main(String[] args) {
        Data date = new Data();
        //建立4个线程,两个加,两个减
        new Thread(()->{
            try {
                for (int i=0;i<20;i++)
                    date.increment();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    date.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            try {
                for (int i=0;i<20;i++)
                    date.increment();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    date.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
class Data{
    private int number=0;
    public synchronized   void increment() throws InterruptedException {
        if (number!=0) //如果还有就等待
            this.wait();
        number++;
        System.out.println(Thread.currentThread().getName()+" incr->"+number);
        this.notify();
    }
    public synchronized void decrement()throws InterruptedException{
        if (number==0) //如果没有了就等待
            this.wait();
        number--;
        System.out.println(Thread.currentThread().getName()+" decr->"+number);
        this.notify();
    }
}

复制代码

运行结果:

。。。。。。省略上下文
C incr->1
A incr->2
C incr->3
。。。。。。
复制代码

原因:

  • 当我们在if中使用wait()时,如果保持着++,–的次序则不会出错。
  • 如果当线程A等待时,如果线程C获取了锁进行++操作后释放锁,A获得锁。然后A直接走wait之后的代码不进行条件判断,因而出错。

解决:

使用while 而不是 if 如

public synchronized void decrement()throws InterruptedException{
        while (number==0) //如果没有了就等待
            this.wait();
        number--;
        System.out.println(Thread.currentThread().getName()+" decr->"+number);
        this.notify();
    }
复制代码

精准唤醒 single

官方文档single的实施注意:

当调用此方法时,实现可能(通常是)要求当前线程保存与此Condition的锁
执行必须记录此前提条件,如果不保持锁定,则采取任何措施。 
通常情况下,一个异常如IllegalMonitorStateException将被抛出。 
复制代码
  1. 定义多个condition,创建多个等待队列
  2. 通过condition.single唤醒等待队列中等待最久的线程,由于多个等待队列中都只有一个线程在等待因此可以精准唤醒
public class TestCondistion {
    public static void main(String[] args) {
        Data3 date = new Data3();
        // 设置ABCD四个线程,分别调用printA,B,C,D
        new Thread(()->{
            try {
                for (int i=0;i<20;i++)
                    date.printA();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        //省略相似代码
    }
}
class Data3{
    private int number=0;
    private Lock lock=new ReentrantLock();
    //创建多个condition
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private Condition condition4 = lock.newCondition();

    public  void printA() throws InterruptedException {
        lock.lock();
        try{
            while (number!=0)
                condition1.await();
            number++;
            System.out.println(Thread.currentThread().getName()+" incr->"+number);
            //唤醒condition2等待队列中等待最久的那个一个,由于只有printB在condition2的等待队列中所以可以精准唤醒printB
            condition2.signal();
        }catch (Exception exception){
            System.err.println("incr wrong");;
        }finally {
            lock.unlock();
        }

    }
    public void printB()throws InterruptedException{
        lock.lock();
        try {
            while ( number==0)
                condition2.await();
            number--;
            System.out.println(Thread.currentThread().getName()+" decr->"+number);
            condition3.signal();
        }catch (Exception ex){
            System.err.println("decr wrong");
        }finally {
            lock.unlock();
        }

    }
    public void printC()throws InterruptedException{
        lock.lock();
        try {
            while ( number!=0)
                condition3.await();
            number++;
            System.out.println(Thread.currentThread().getName()+" incr->"+number);
            condition4.signal();
        }catch (Exception ex){
            System.err.println("incr wrong");
        }finally {
            lock.unlock();
        }

    }
    public void printD()throws InterruptedException{
        lock.lock();
        try {
            while ( number==0)
                condition4.await();
            number--;
            System.out.println(Thread.currentThread().getName()+" decr->"+number);
            condition1.signal();
        }catch (Exception ex){
            System.err.println("decr wrong");
        }finally {
            lock.unlock();
        }

    }
}
复制代码

运行结果:

A incr->1
B decr->0
C incr->1
D decr->0
A incr->1
B decr->0
C incr->1
D decr->0
A incr->1
B decr->0
C incr->1
D decr->0
A incr->1
B decr->0
C incr->1
D decr->0
A incr->1
B decr->0
C incr->1
D decr->0
A incr->1
B decr->0
C incr->1
D decr->0
A incr->1
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享