关于wait与notify和notifyAll方法的总结

关于wait与notify和notifyAll方法的总结:

  1. 当调用wait时,首先要确保调用了wait方法的线程已经持有了对象的锁。
  2. 当调用了wait后,该线程就会释放掉这个对象的锁,然后进入到等待状态(wait set)
  3. 当线程调用了wait后进入到等待状态时,它就可以等待其他线程调用相同对象的notify或是notifyAll方法来使得自己被唤醒
  4. 一旦这个线程被其他线程唤醒后,该线程就会与其他线程一同开始竞争这个对象的锁(公平竞争);只有当线程获取到了这个对象的锁后,线程才会继续往下执行
  5. 调用wait方法的代码片段需要放在一个synchronized块或是synchronized方法中,这样才可以确保线程在调用wait方法前已经获取到了对象的锁
  6. 当调用对象的notify方法时,它会随机唤醒该对象等待集合(wait set)中的任意一个线程,当某个线程被唤醒后,它就会与其他线程一同竞争对象的锁
  7. 当调用对象的notifyAll方法时,它会唤醒该对象等待集合(wait set)中的所有线程,这些线程都会被唤醒后,又会开始竞争对象的锁
  8. 在某一时刻,只有唯一一个线程可以拥有对象的锁

写段代码验证下

MyObject类

public class MyObject {
    private int counter;
    public synchronized void increase() {
       //着重注意这里的if
        if (counter != 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        counter++;
        System.out.println(counter);
        notify();
    }

    public synchronized void decrease() {
      //着重注意这里的if
        if (counter == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        counter--;
        System.out.println(counter);
        notify();
    }
}

IncreaseThread类

public class IncreaseThread extends Thread {
    private MyObject myObject;

    public IncreaseThread(MyObject myObject) {
        this.myObject = myObject;
    }

    @Override
    public void run() {
        for (int i = 0; i < 30; i++) {
            try {
                Thread.sleep((long) Math.random() * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myObject.increase();
        }
    }
}

DecreaseThread类

public class DecreaseThread extends Thread {
    private MyObject myObject;
  
    public DecreaseThread(MyObject myObject) {
        this.myObject = myObject;
    }

    @Override
    public void run() {
        for (int i = 0; i < 30; i++) {
            try {
                Thread.sleep((long) Math.random() * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myObject.decrease();
        }
    }
}

client类,测试类

public class Client {
    public static void main(String[] args) {
        MyObject myObject = new MyObject();    
        Thread increaseThread = new IncreaseThread(myObject);       
        Thread decreaseThread = new DecreaseThread(myObject);

        increaseThread.start();      
        decreaseThread.start();        
    }
}

在测试类中,当IncreaseThread、DecreaseThread类的实例对象都只有一个时,控制台依次输出1、0、1、0。。。

但是,当我们对client测试类进行修改,增加两个对象后,控制台输出的值是不是和之前的一样呢?

测试类修改后代码

public class Client {
    public static void main(String[] args) {
        MyObject myObject = new MyObject();
        Thread increaseThread = new IncreaseThread(myObject);
        Thread increaseThread2 = new IncreaseThread(myObject);
        Thread decreaseThread = new DecreaseThread(myObject);
        Thread decreaseThread2 = new DecreaseThread(myObject);

        increaseThread.start();
        increaseThread2.start();
        decreaseThread.start();
        decreaseThread2.start();

    }
}

控制台除了输出1、0之外还会输出其他不可控制的值。

这是为什么呢?

这里主要看MyObject类中的increasedecrease方法的实现。

public class MyObject {
    private int counter;
    public synchronized void increase() {
       //着重注意这里的if
        if (counter != 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        counter++;
        System.out.println(counter);
        notify();
    }

    public synchronized void decrease() {
      //着重注意这里的if
        if (counter == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        counter--;
        System.out.println(counter);
        notify();
    }
}

这里使用A1、A2替代increaseThread、increaseThread2对象,用B1、B2替代decreaseThread、decreaseThread2。

四个线程A1、A2、B1、B2同时开始执行。假设一开始B1线程运行,此时counter == 0,执行wait,释放当前锁,假设下一个获得锁的是B2,则因为counter==0,再次执行wait;假设下一个获得锁的是A1,则因为counter等于0,执行counter++,那么counter等于1,然后执行notify,唤醒其他线程,假如唤醒的是B1,则B1线程往下先执行count–,此时counter等于0,然后再执行notify,(重点来了)假如此时唤醒的是B2,则继续往下执行counter–,此时counter等于-1。

经过上面的解释,相信你已经明白为什么会出现增加线程会出现不可控制的情况了。

那么,问题的原因已经找到,如何解决呢?

//将这里的if改为while即可,increase()和decrease()中的if都要改
if (counter == 0) {
    try {
      wait();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
}
//修改后
while (counter == 0) {
    try {
      wait();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
}

那么为什么这么修改就可以了呢?

我们从唤醒的假如是B2开始说。**(重点来了)**那么wait语句结束之后,会再次判断当前的counter是否等于0,如果不等于0,则执行counter,如果等于0,则继续执行wait。因为此时的counter是等于0的,所以执行执行wait。所以控制台输出还是1、0、1、0。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值