廖雪峰Java11多线程编程-2线程同步-4wait和notify

wait和notify

synchronized解决了多线程竞争的问题

  • 我们可以在synchronized块中安全的对一个变量进行修改,但是它没有解决多线程协调的问题。
  • 例如设计一个TaskQueue,预期效果:线程1通过addTask()不断往队列中添加任务,而线程2可以调用getTask()从队列中获取任务
    1418970-20190612125830111-890703385.png
class TaskQueue{
    Queue<String> queue = new LinkedList<>();
    public synchronized void addTask(String s){    //线程1不能执行,因为操作对象已经被线程2锁住了
        this.queue.add(s);
    }
    public synchronized String getTask(){
        while(queue.isEmpty()){   } //线程2执行getTask()时,如果队列为空,陷入死循环
        return queue.remove();
    }
}

上诉代码会出现死循环。
如果队列为空,则getTask()应该等待,直到队列中至少有一个任务再执行getTask()。
因此我们需要一种多线程协调的机制:当条件不满足时,线程进入等待状态
1418970-20190612134746827-794839366.jpg
wait()方法的执行机制:

  • 首先,wait()不是一个普通的Java方法,而是定义在Object类上面的一个native方法,即是由JVM虚拟机的C代码实现的
  • 其次,必须在synchronized代码块中才能调用wait()方法,因为wait()方法调用的时候,线程会释放它获得的锁,wait()方法返回后,线程又回重新获得锁。我们只能在锁对象调用wait()方法。此处我们使用synchronized修饰方法,所以我们获得对象是this对象。因此只能在this对象上调用wait()方法
  • 正是因为wait()方法会释放锁,所以其他的线程才能获得锁,并且进入addTask()方法。在addTask()方法内,我们向队列添加一个元素以后,就可以调用this.notify()来唤醒正在this对象上等待的线程,这样在wait()方法上等待的线程就可以被唤醒,然后从wait()方法返回以后继续执行。
import java.util.LinkedList;
import java.util.Queue;

class TaskQueue{
    final Queue<String> queue = new LinkedList<>(); //定义一个LinkedList作为queue
    public synchronized String getTask() throws InterruptedException{
        System.out.println("开始执行");
        while(this.queue.isEmpty()){ //判断queue是否是空,如果空,就释放对象锁,进入等待状态。
            this.wait();
            System.out.println("继续执行");
        }
        return queue.remove();//删除queue最上面的值
    }
    public synchronized void addTask(String name){
        this.queue.add(name); //queue添加一个任务
        this.notifyAll(); //唤醒所有正在等待的线程
        System.out.println("唤醒线程");
    }
}
class WorkerThread extends Thread {
    TaskQueue taskQueue;
    public WorkerThread(TaskQueue taskqueue){
        this.taskQueue = taskqueue;
    }
    public void run(){
        while(!isInterrupted()){ //不断的执行getTask,获取queue中的任务,一旦获取到,就打印hello name
            String name;
            try{
                name = taskQueue.getTask();
            }catch (InterruptedException e){
                break;
            }
            String result = "Hello, "+name+"!";
            System.out.println(result);
        }
    }
}
public class Main{
    public static void main(String[] args) throws Exception{
        TaskQueue taskqueue = new TaskQueue();
        WorkerThread worker = new WorkerThread(taskqueue);
        worker.start();
        taskqueue.addTask("Bob");
        Thread.sleep(1000);
        taskqueue.addTask("Alice");
        Thread.sleep(1000);
        taskqueue.addTask("Tim");
        Thread.sleep(1000);
        worker.interrupt();
        worker.join();
        System.out.println("End");
    }
}

1418970-20190612132249952-1061571916.png
为什么在while循环wait,而不是一个if语句中wait ?
如果存在2个或者更多的线程在wait(),唤醒当前线程的不一定是执行添加队列的线程1。当非添加队列的线程1唤醒获取队列的线程2时:

  • 使用while可以判断当前队列是否为空,
  • 使用if继续执行,如果队列为空,执行remove方法会报错
    1418970-20190612164041180-736702952.jpg

总结

  • 在synchronized内部可以调用wait()是线程进入等待状态
  • 必须在已获得的锁对象上调用wait()方法
  • 在synchronized内部可以调用notify/notifyAll()唤醒其他等待线程
  • 必须在已获得的锁对象上调用notify()/notifyAll()方法

转载于:https://www.cnblogs.com/csj2018/p/11005544.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值