美文网首页
boy-learning-thread | 1.1.4 线程通信

boy-learning-thread | 1.1.4 线程通信

作者: BruceOuyang | 来源:发表于2019-10-19 11:40 被阅读0次

相关源码:boy-learning-thread
个人博客:http://bruce.bugmakers.club
内容来自《网易微专业 - 高性能编程章节》

线程通信

通信方式

要想实现多个线程之间的协同,如:线程执行先后顺序、获取某个线程的执行结果等待。
涉及到线程时间的相互通信,分为下面四类:

  1. 文件共享
  2. 网络共享
  3. 共享变量
  4. jdk 提供的线程协调 API,细分为:suspend/resume、wait/notify、park/unpark

1、文件共享

// 共享文件
File file = new File("a.txt");

thread1 写文件
thread2 读文件

2、变量共享

// 共享变量
public static String content = "init-txt";

thread1 写入内存变量
thread2 读取内存变量

3、网络共享

4、线程协作 - JDK API

JDK 中对于需要多线程协作完成某一任务的场景,提供了对应的API支持。
多线程协作的典型场景是:生产者 - 消费者模型。(线程阻塞、线程唤醒)

场景示例

线程1去买包子,没有包子,则不再执行。 线程2生产包子,通知线程1继续执行。

4.1、suspend/resume 机制

thread.suspend() // 挂起
thread.resume    // 激活

suspend/resume 这种组合已经被 jdk 遗弃掉了,原因是容易出现死锁。

死锁场景一:同步代码块中

Object baozi = null;
Thread consumer = new Thread(() -> {
    if (baozi == null) {
        System.out.println("来晚了,卖光了");
        synchronized(this) {
            Thread.currentThread().suspend();
        }
    }
    System.out.println("买到包子,回家");
});

// 包子制作中
Thread.sleep(3000L);

baozi = new Object();
System.out.println("新一笼的包子做好啦");

// 争取到锁后,在恢复 consumer
synchronized(this) {
    consumer.resume();
}

死锁场景二:先后顺序没有控制好

Object baozi = null;
Thread consumer = new Thread(() -> {
    if (baozi == null) {
        System.out.println("来晚了,卖光了");
        
        Thread.sleep(5000L);
        
        Thread.currentThread().suspend();
    }
    System.out.println("买到包子,回家");
});

// 包子制作中
Thread.sleep(3000L);

baozi = new Object();
System.out.println("新一笼的包子做好啦");

consumer.resume();

consumer.join();

4.2、wait/notify 机制

synchronized(LOCK) {
    Object.wait();  // wait会释放LOCK对象锁    
}
synchronized(LOCK) {
    Object.notify();   
}

这些方法只能由同一对象锁的持有者线程调用,也就是写在同步代码块里边,否则会抛出 IllegalMonitorStateException 异常。

wait 方法导致当前线程等待,加入该对象的等待集合中,并且放弃当前持有的对象锁。
notify/notifyAll 方法唤醒一个或所有正在等待这个对象锁的线程。

注意:虽然会 wait 自动解锁,但是对顺序有要求,如果在 notify 被调用之后,才开始 wait 方法的调用,线程会永远处于 WAITING 状态。

4.3、park/unpark 机制

LockSupport.park();
LockSupport.unpark();

线程调用 park 则等待 “许可”,unpark 方法为指定线程提供 “许可(permit)”。

不要求 park/unpark 的调用顺序。

多次调用 unpark 后,再调用 park,线程会直接运行。
但不会叠加,也就是说,连续多次调用 park 方法,第一次会拿到 “许可” 直接运行,后续调用会进入等待。

LockSupport 不会释放持有的对象锁,所以在同步代码块中,会出现死锁的现象。

死锁场景:

synchronized(LOCK) {
    LockSupport.park();
}

//...

synchronized(LOCK) {
    LockSuport.unpark();
}

伪唤醒

警告!之前代码中用 if 语句来判断是否进入等待状态,是错误的!
官方建议,应该在循环中检查等待条件,原因是处于等待状态的线程可能会收到错误报警和伪唤醒,如果不在循环中检查等待条件,程序就会在没有满足结束条件的情况下退出。

伪唤醒是指线程并非因为 notify、notifyAll、unpark 等 api 调用唤醒,是更底层原因导致的。

相关文章

  • boy-learning-thread | 1.1.4 线程通信

    相关源码:boy-learning-thread 个人博客:http://bruce.bugmakers.cl...

  • ios 多线程的故事4

    线程间通信 线程间通信:在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信 线程间通信的体现 1个...

  • 线程间通信

    线程间通信就是子线程和主线程之间的通信

  • 线程与线程,进程与进程之间的通信

    线程与线程之间的通信 一,为什么要线程通信? 1>多个线程...

  • Java基础知识02- 线程

    多线程通信 :什么是多线程通信 ?怎么进行通信 ? 多线程通信就是多个线程同时操作同一个全局变量,但是操作的动作不...

  • 《iOS高级开发之多线程编程之二》

    线程间的通信 在一个进程中,线程往往不是孤立存在的,多个线程之间经常进行通信,称为线程间通信。 NSThread ...

  • 线程通信的方法

    线程通信的方法 程序在使用多线程执行任务时,经常需要线程之间协同工作。此时,我们需要了解线程通信的手段。 线程通信...

  • Java线程通信

    线程通信 线程通信指的是多个线程在运行的期间,相互之间的数据交互协作。 1.通信方式 实现多个线程直接的协作,涉及...

  • 多线程之iOS线程间通信

    什么叫做线程间通信在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信 线程间通信的体现 1个线程传...

  • 线程间的通信

    1>NSThread的线程间的通信将一个线程执行的结果, 传到另外一个线程 (不同的线程之间的通信都是线程通信)S...

网友评论

      本文标题:boy-learning-thread | 1.1.4 线程通信

      本文链接:https://www.haomeiwen.com/subject/rbxgmctx.html