使用wait和notify进行多线程协调

多线程协调

synchronized解决了多线程竞争的问题。例如,对于一个任务管理器,多个线程同时往队列中添加任务,可以用synchronized加锁:

class TaskQueue {
    Queue<String> queue = new LinkedList<>();

    public synchronized void addTask(String s) {
        this.queue.add(s);
    }
}

但是无法解决多线程协调的问题,多线程协调问题也就是多个线程执行不同的方法的问题,如果处理不当可能造成线程长期占用无法释放,造成资源浪费、JVM进程无限等待的后果。

class TaskQueue {
    Queue<String> queue = new LinkedList<>();

    public synchronized void addTask(String s) {
        this.queue.add(s);
    }

    public synchronized String getTask() {
        while (queue.isEmpty()) {
        }
        return queue.remove();
    }
}

上面的代码,执行getTask时,持有的锁是this锁,如果队列为空则一直等待,一直无法释放锁,由于addTask在等待this锁,一直无法增加任务,则该队列一直为空。因此陷入了循环等待。

解决办法

使用wait和notify(其中notifyAll更安全)

  • 必须在已获得的锁对象上调用wait()方法;
  • 在synchronized内部可以调用notify()或notifyAll()唤醒其他等待线程;
  • 必须在已获得的锁对象上调用notify()或notifyAll()方法;
  • 已唤醒的线程还需要重新获得锁后才能继续执行。
    下面代码如果不加wait和notifyAll则无法正常结束
package org.meituan.javalearn.thread;

import lombok.SneakyThrows;

import java.util.LinkedList;

/**
 * @projectName: codebetter
 * @package: org.meituan.javalearn.thread
 * @className: WaitNotifyThread
 * @author: fangjiayueyuan
 * @description: TODO
 * @date: 2023/5/4 上午11:11
 * @version: 1.0
 */
public class WaitNotifyThread {
    public static void main(String[] args) {
        LinkedList<Thread> threadPools = new LinkedList<Thread>();
        final TaskQueue taskQueue = new TaskQueue();
        for(int i=0;i<5;i++){
            threadPools.add(new Thread(new Runnable() {
                @SneakyThrows
                @Override
                public void run() {
                    System.out.println(taskQueue.getTask());
                }
            }));
        }
        Thread addThread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    taskQueue.addTask("The"+i+"th task to be closed");
                }
            }
        });

        for(Thread thread:threadPools){
            thread.start();
        }
        addThread.start();
    }
}
class TaskQueue{
    LinkedList<String> queue = new LinkedList<String>();
    public synchronized void addTask(String task){
        queue.add(task);
        this.notifyAll();
    }
    public synchronized String getTask() throws InterruptedException {
        while (queue.isEmpty()){
            this.wait();
        }
        return queue.remove();

    }
}

上面代码的运行结果:在这里插入图片描述

解决办法2

使用condition解决多线程协调问题

  • 使用Condition时,引用的Condition对象必须从Lock实例的newCondition()返回,这样才能获得一个绑定了Lock实例的Condition实例。
  • await()会释放当前锁,进入等待状态;
  • signal()会唤醒某个等待线程;
  • signalAll()会唤醒所有等待线程;
  • 唤醒线程从await()返回后需要重新获得锁。
    使用Condition进行多线程协调的代码如下,实现和上面的代码一样功能的任务队列。
class ConditionTaskQueue{
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    LinkedList<String> queue = new LinkedList<String>();
    public void addTask(String task){
        lock.lock();
        try{
            queue.add(task);
            condition.signalAll();
        }finally {
            lock.unlock();
        }

    }
    public String getTask() throws InterruptedException {
        lock.lock();
        try{
            while (queue.isEmpty()){
                condition.await();
            }
            return queue.remove();
        }finally {
            lock.unlock();
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值