线程间通信

多线程编程步骤

  1. 创建资源类,编写属性和操作方法
  2. 在资源中的操作方法
    1. 判断
    2. 干活
    3. 通知
  3. 创建多个线程,调用资源类的操作方法
  4. 使用while循环进行条件判断,防止虚假唤醒问题

一个加减实例 & 虚假唤醒问题

实现对一个初始值0进行轮流加减操作

public class AddAndSub {
private int num;
public int getNum() {
return num;
}
public AddAndSub() {
num = 0;
}
public synchronized void add() {
// 判断
while(num != 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 业务逻辑
num++;
System.out.println(Thread.currentThread().getName() + " + 1");
// 通知
notifyAll();
}
public synchronized void sub() {
while(num != 1) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
System.out.println(Thread.currentThread().getName() + " - 1");
notifyAll();
}
} class RunAddAndSub {
public static void main(String[] args) {
AddAndSub addAndSub = new AddAndSub();
Runnable r1 = () -> {
for(int i = 0; i < 10; i++) {
addAndSub.add();
}
};
Runnable r2 = () -> {
for(int i = 0; i < 10; i++) {
addAndSub.sub();
}
};
new Thread(r1, "A").start();
new Thread(r2, "B").start();
System.out.println(addAndSub.getNum());
}
}

为什么要在条件判断使用while?

这是因为比如add方法A线程+1notifyAll后,可能是处于等待状态的C线程抢到了锁,但是这时候num并不是0,所以要使用while在线程唤醒后再次判断。这种问题称为虚假唤醒问题,

Lock接口实现

lock.newCondition设置等待条件
class Share {
private int num = 0;
private final Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void incr() {
lock.lock();
while(num != 0) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.println(Thread.currentThread().getName() + " + 1");
condition.signalAll();
lock.unlock();
}
public void decr() {
lock.lock();
while(num == 0) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
System.out.println(Thread.currentThread().getName() + " - 1");
condition.signalAll();
lock.unlock();
}
}
public class TheadDemo {
public static void main(String[] args) {
var share = new Share();
Runnable r1 = () -> {
for(int i = 0; i < 10; i++) {
share.incr();
}
};
Runnable r2 = () -> {
for(int i = 0; i < 10; i++) {
share.decr();
}
};; new Thread(r1, "A").start();
new Thread(r2, "B").start();
new Thread(r1, "C").start();
new Thread(r2, "D").start();
}
}

注意condition.signalAll()要写在unlock之前

线程间定制化通信

如上面的四个线程执行的时候,只是+-线程的顺序执行,执行相同方法的线程是没有顺序的,规定相同方法的线程的执行顺序,即是线程的定制化通信。

线程间通信案例

启动三个线程,要求如下:

  • AA打印5次,BB打印10次,CC打印15次
  • 如此进行10轮
设置标志位

当标志位为1,AA执行,当标志位为2,BB执行,当标志位为3,CC执行

class Print {
private int flag = 1; public synchronized void print5(int num) throws InterruptedException {
while (flag != 1) {
this.wait();
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":::" + i + " ,loop" + num);
}
flag = 2;
this.notifyAll();
} public synchronized void print10(int num) throws InterruptedException {
while (flag != 2) {
this.wait();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":::" + i + " ,loop" + num);
}
flag = 3;
this.notifyAll();
} public synchronized void print15(int num) throws InterruptedException {
while (flag != 3) {
this.wait();
}
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName() + ":::" + i + " ,loop" + num);
}
flag = 1;
this.notifyAll();
}
} public class ThreadDemo2 {
public static void main(String[] args) {
Print print = new Print();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
print.print5(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "AA").start(); new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
print.print10(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "BB").start(); new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
print.print15(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "CC").start();
}
}

JUC(二)线程间通信的更多相关文章

  1. juc包:使用 juc 包下的显式 Lock 实现线程间通信

    一.前置知识 线程间通信三要素: 多线程+判断+操作+通知+资源类. 上面的五个要素,其他三个要素就是普通的多线程程序问题,那么通信就需要线程间的互相通知,往往伴随着何时通信的判断逻辑. 在 java ...

  2. Java多线程(二) —— 线程安全、线程同步、线程间通信(含面试题集)

    一.线程安全 多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的. 讲到线程安全问题,其实是指多线程环境下对共享资源的访问可能会 ...

  3. JUC之线程间的通信

    线程通信 视频1: 2021.12.18 JUC视频学习片段 对上次多线程编程步骤补充(中部): 创建资源类,在资源类中创建属性和操作方法 在资源类里面操作 判断 干活 通知 创建多个线程,调用资源类 ...

  4. Java多线程编程核心技术---线程间通信(二)

    通过管道进行线程间通信:字节流 Java提供了各种各样的输入/输出流Stream可以很方便地对数据进行操作,其中管道流(pipeStream)是一种特殊的流,用于在不同线程间直接传送数据,一个线程发送 ...

  5. Java笔记(二十)……线程间通信

    概述 当需要多线程配合完成一项任务时,往往需要用到线程间通信,以确保任务的稳步快速运行 相关语句 wait():挂起线程,释放锁,相当于自动放弃了执行权限 notify():唤醒wait等待队列里的第 ...

  6. 【原】iOS多线程之线程间通信和线程互斥

    线程间通信 1> 线程间通信分为两种 主线程进入子线程(前面的方法都可以) 子线程回到主线程 2> 返回主线程 3> 代码 这个案例的思路是:当我触摸屏幕时,会在子线程加载图片,然后 ...

  7. Java线程间通信-回调的实现方式

    Java线程间通信-回调的实现方式   Java线程间通信是非常复杂的问题的.线程间通信问题本质上是如何将与线程相关的变量或者对象传递给别的线程,从而实现交互.   比如举一个简单例子,有一个多线程的 ...

  8. 线程间通信的三种方式(NSThread,GCD,NSOperation)

    一.NSThread线程间通信 #import "ViewController.h" @interface ViewController ()<UIScrollViewDel ...

  9. Windows环境下多线程编程原理与应用读书笔记(4)————线程间通信概述

    <一>线程间通信方法 全局变量方式:进程中的线程共享全局变量,可以通过全局变量进行线程间通信. 参数传递法:主线程创建子线程并让子线程为其服务,因此主线程和其他线程可以通过参数传递进行通信 ...

  10. java线程间通信:一个小Demo完全搞懂

    版权声明:本文出自汪磊的博客,转载请务必注明出处. Java线程系列文章只是自己知识的总结梳理,都是最基础的玩意,已经掌握熟练的可以绕过. 一.从一个小Demo说起 上篇我们聊到了Java多线程的同步 ...

随机推荐

  1. CSS 选择器-认识并应用选择器

    在内嵌式和外部css中,要想将CSS样式应用于特定的HTML元素,首先需要找到该目标元素,这时需要用到CSS中的选择器. 选择器:选择要添加样式的 HTML 标签的一种方法.模式. 首先学习 css2 ...

  2. OO_Lab2总结博客

    OO_Lab2 一.单元内容 本单元内容为规格化设计,即通过参考已经完成的JML描述实现一个社交网络相关功能. 本单元整体来说难度不大,但是却是我最惨的一次作业,所以本博客可能会主要谈一谈测试中的一些 ...

  3. 转:MyBatis 日志打印

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u012666996/article/details/79106599Mybatis SQL语句控制台 ...

  4. HCK 、PCLK、FCLK的区别

    HCLK is used for AHB bus, which is used by the ARM920T, the memory controller, the interrupt control ...

  5. 宝塔linux面板进行数据库操作显示"数据库管理密码"

    在使用宝塔linux面板时,对数据库进行操作之后,显示数据库管理密码错误.经历了卸载数据库并删除了数据库文件和olddata, 在从新安装,仍然显示数据库管理密码错误. 这时,我们可以进入到 /www ...

  6. 后端008_配置Security登录授权过滤器

    ------------恢复内容开始------------ 现在我们就可以去进行springscurity的配置了.首先我们新建一个配置类.然后该类需要添加@Configuration注解,然后还要 ...

  7. 如何加密一个sheel脚本!

    脚本写完后,如果要发布给其它人使用的话,可能会因安全原因而受阻,特别是脚本中包含密码等原因,而对脚本加密则可以解决此问题,本文提供了CentOS7/8环境下,加密shell脚本需要安装的程序和方法. ...

  8. Promise静态方法实现(all race finally resolve reject)

    示例 // Promise.resolve() Promise.resolve(1).then((data) => { console.log(data) // 1 }) // Promise. ...

  9. c# form-data表单提交,post form上传数据、文件

    引用自:https://www.cnblogs.com/DoNetCShap/p/10696277.html 表单提交协议规定:要先将 HTTP 要求的 Content-Type 设为 multipa ...

  10. 发布jar包到远程仓库 (maven deploy)

    背景: 项目有开放服务模块,现有个需求,需要把开放服务提供成一个jar包,用户可以直接对接. 流程: 1.在pom.xml文件添加distributionManagement节点,将项目打包上传到私服 ...