多线程wait()与notify()和notifyAll()方法的总结

多线程wait()与notify()和notifyAll()方法的总结

wait和sleep的区别

  • 在调用wait方法时,线程必须持有调用对象的锁,当调用wait方法后,线程就会释放掉改对象的锁(monitor)。

  • 在调用Thread类的sleep方法时,线程是不会释放掉对象的锁的。

wait()与notify()和notifyAll()方法的总结

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

wait()与notify()的使用

两个线程去操作counter,打印出10101010…

package com.learn.thread.wait;

class Counter {

    private int counter;

    public synchronized void increase() {
        if (counter != 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        counter++;
        System.out.print(counter);
        // 随机唤醒了一个
        notify();
    }

    public synchronized void decrease() {
        if (counter == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        counter--;
        System.out.print(counter);
        notify();
    }
}

class IncreaseThread extends Thread {

    private Counter counter;

    public IncreaseThread(Counter counter) {
        this.counter = counter;
    }

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

class DecreaseThread implements Runnable {

    private Counter counter;

    public DecreaseThread(Counter counter) {
        this.counter = counter;
    }

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


public class MyCounter {

    public static void main(String[] args) {
        Counter counter = new Counter();

        Thread increase = new IncreaseThread(counter);
        DecreaseThread decrease = new DecreaseThread(counter);

        increase.start();
        new Thread(decrease).start();
    }
}

程序输出
再增加两个线程,一个增加一个减少

public class MyCounter {

    public static void main(String[] args) {
        Counter counter = new Counter();

        Thread increase = new IncreaseThread(counter);
        DecreaseThread decrease = new DecreaseThread(counter);

        increase.start();
        new Thread(decrease).start();
        new IncreaseThread(counter).start();
        new Thread(new DecreaseThread(counter)).start();
    }
}

程序输出
显然,程序输出有问题。为什么会出现这种问题呢?

  • 分析:
    在初始时刻,counter=0,增加的线程进入到increase方法,程序直接counter++,counter值变为1,控制台打印出1,调用notify()方法(唤醒某个MyCounter对象上的wait的线程)。假设此时没有wait的线程,则该线程退出。
    两个减少线程其中之一执行了decrease方法,判断counter是否等于0,此时counter=1,执行counter–,counter=0,控制台打印出0,调用notify()方法。
    接下来又有一个减少线程来执行decrease,判断counter是否等于0,此时counter=0,线程wait(),释放对象的锁,其他三个线程(一个减少,两个增加)开始竞争对象锁,假设减少线程获取到这把锁,线程来执行decrease,判断counter是否等于0,此时counter=0,线程wait(),释放对象的锁。现在有两个减少线程T1,T2在等待。一个增加线程获取到这把锁,判断counter是否等于0,此时counter=0,程序执行counter++,counter值变为1,控制台打印出1,调用notify()方法,唤醒等待集合中的一个线程,一个减少线程T1被唤醒,线程继续向下执行,counter–,控制台打印出0,调用notify()方法,恰好此时等待线程T2被唤醒,线程继续向下执行,counter–,控制台打印出-1,调用notify()方法。程序输出已经错误。
  • 修改:将wait方法用循环包起来,wait的判断条件应该用循环
    使用循环,当程序被唤醒后,继续进行判断,是否满足条件。
    public synchronized void decrease() {
        while (counter == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        counter--;
        System.out.print(counter);
        notify();
    }
}
package com.learn.thread.wait;

class Counter {

    private int counter;

    // wait()一定是在synchronized代码块或synchronized方法中调用
    public synchronized void increase() {
        while (counter != 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        counter++;
        System.out.print(counter);
        // 随机唤醒了一个
        notify();
    }

    // wait()一般放在while循环中,线程下次醒过来后会继续进行循环,判断条件是否满足,
    // 满足就重新等待。防止程序直接向下执行。
    public synchronized void decrease() {
        while (counter == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        counter--;
        System.out.print(counter);
        notify();
    }
}

class IncreaseThread extends Thread {

    private Counter counter;

    public IncreaseThread(Counter counter) {
        this.counter = counter;
    }

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

class DecreaseThread implements Runnable {

    private Counter counter;

    public DecreaseThread(Counter counter) {
        this.counter = counter;
    }

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


public class MyCounter {

    public static void main(String[] args) {
        Counter counter = new Counter();

        Thread increase = new IncreaseThread(counter);
        DecreaseThread decrease = new DecreaseThread(counter);

        increase.start();
        new Thread(decrease).start();
        new IncreaseThread(counter).start();
        new Thread(new DecreaseThread(counter)).start();
    }
}

程序结果

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值