多线程协作机制

JAVA架构师之路

本系列文章均是博主原创,意在记录学习上的知识,同时一起分享学习心得。
第一章
第一节 Java集合总结(架构师之路 )
第二节 Java多线程(架构师之路 )
第三节 理解synchronized(架构师之路 )



前言

多线程之间除了竞争关系之外,还存在互相协作的情况。本章介绍了多线程基本机制wait/notify。


一、协作场景

多线程之间的协作场景有很多,比如:

  • 生产者/消费者协作模式:生产者和消费者线程通过共享队列进行协作,当队列满时生产者需要等待,队列空时消费者需要等待。
  • 同时开始:要求多个程序需要同时开始。
  • 等待结束:通常用于主从协作模式,主线程将任务分解为多个子线程,主线程需要等待子线程都执行完毕,才执行其他任务。
  • 异步结果:将子线程的管理封装为异步调用,异步调用马上返回,但不是返回最终结果,而是一个称为Future的对象,通过它可以在随后获得最终的结果。
  • 集合点:在并行迭代计算中,每个线程负责一部分计算,然后在集合点等待其他线程完成,所有线程到齐后,交换数据和计算结果,再进行下一次迭代。

二、wait/notify

Java的根父类都是Object,其定义了一些线程协作的基本方法,大致分为两类:wait、notify。

1.wait

主要有两个方法:

  • wait()。表示无限期等待,实际调用的是wait(0)。
  • wait(long timeout)。表示等待timeout长时间,单位是毫秒。

具体过程:
1)把当前线程放入条件等待队列,释放对象锁,阻塞等待,线程状态变为WAITING或TIMED_WAITING。
2)等待时间到或被其他线程调用notify/notifyAll从条件队列中移除,这时要重新竞争对象锁:

  • 如果能够获得对象锁,线程状态变为RUNNABLE,并从wait()调用中返回。
  • 否则,该线程加入对象锁等待队列,线程状态变为BLOCKED,只有在获得锁后才会从wait调用中返回。

2.notify

调用notify会把在条件队列中等待的线程唤醒并从队列中移除,但它不会释放对象锁,也就是说,只有包含notify的synchronized代码块执行完后,等待的线程才会从wait调用中返回。
主要有两个方法:

  • notify()。表示从条件队列中选一个线程,将其从队列中移除并唤醒。
  • notifyAll()。表示移除条件队列中所有的线程并全部唤醒。

3、协作过程

每个对象其实都有一个锁、和一个锁的等待队列,除此之外,每个对象还有另外一个等待队列,叫条件队列,该队列用于线程间的协作。

  1. 调用wait()就会把当前线程放到条件队列上并阻塞,表示当前线程执行不下去了,它需要等待一个条件,改条件它自己改不了,需要其他线程改变。
  2. 当其他线程调用notify/notifyAll时,会改变条件,唤醒在等待队列中的线程。

示例代码:

public class WaitThread extends Thread{
   

    private volatile boolean fire = false;

    @Override
    public void run() {
   
        try {
   
            // 子线程A获取waitThread对象的锁
            synchronized (this) {
   
                while (!fire) {
   
                    wait();// 子线程释放waitThread对象的锁,并加入waitThread对象的条件队列
                }
            }
            System.out.println("fired");
        } catch (InterruptedException e) {
   

        }
    }

    public synchronized void fire() {
   
        this.fire = true;
        // 从waitThread对象的条件队列中唤醒其中一条子线程A,将其从条件队列中移除,
        // 并让子线程A竞争waitThread对象锁
        notify();
    }

    public static void main(String[] args) throws InterruptedException {
   
        WaitThread waitThread = new WaitThread();
        waitThread.start();//开启子线程A
        sleep(1000L);
        System.out.println("fire");
        waitThread.fire();// 主线程获取waitThread对象的锁,并执行fire()方法
    }
}

【注意】
1、wait/notify只能在synchronized代码块内被调用,如果调用wait/notify时,当前线程没有持有对象锁,会抛出java.lang.IllegalMonitor-StateException。
2、wait/notify它们被不同的线程调用,但共享相同的锁和条件等待队列。它们围绕一个共享的条件变量进行协作,这个条件变量时程序自身维护的。
3、在设计多线程协作时,需要想清楚协作的共享变量和条件是什么,这是协作的核心。

三、应用场景

3.1 生产者/消费者模式

在生产者/消费者协作模式中,其共享变量和条件如下:
共享变量:队列。
条件:

  • 生产者往队列添加数据时,如果队列满了需要wait;
  • 消费者从队列消费数据时,如果队列为空需要wait;

代码示例:

public class Test {
   
    public static void main(String[] args) {
   
        MyBlockingQueue<String> queue = new MyBlockingQueue<>(10);
        new Producer(queue).start();
        new Customer(queue).start();
    }
}
public class MyBlockingQueue<E> {
   

    private Queue<E> queue = null;
    private int limit;

    public MyBlockingQueue(int limit) {
   
        this.limit = limit;
        queue = new ArrayDeque<>(limit);
    }

    public synchronized void put(E e) throws InterruptedException {
   
        while (queue.size() == limit) {
   
            wait();
        }
        queue.add(e);
        notifyAll(
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值