一、等待与唤醒

/**
* 线程通讯问题
* Object wait, notify, notifyAll
* Condition await signal signAll
* CountDownLatch 当前线程等待若干个其他线程执行完成之后再执行
* CyclicBarrier 一组线程等待某个状态之后再全部开始执行
* Semaphore 控制某一组资源的访问权限
*/

- 代码案例,奇数线程打印奇数,偶数线程打印偶数

1、使用Object自带的方法实现等待与唤醒:

  /**
* 休眠唤醒案例
* 打印10以内的奇偶数
* 奇数线程打印,偶数线程等待
*
* 这个案例使用锁对象实现
*/
static class OddAndEvenDemo {
private int printNo = 0;
private Object lockObj = new Object();
/**
* 奇数打印方法,由奇数线程调用
*/
public void odd() {
while (printNo < 10) {
synchronized (lockObj) {
if (!isEvenNo(printNo)) {
System.out.println("奇数:" + printNo);
printNo ++;
lockObj.notify();
} else {
try {
lockObj.wait(); // 等待偶数线程执行完毕
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
}
} /**
* 偶数打印方法,偶数线程调用
*/
public void even() {
while (printNo < 10) {
synchronized (lockObj) {
if (isEvenNo(printNo)) {
System.out.println("偶数:" + printNo);
printNo ++;
lockObj.notify();
} else {
try {
lockObj.wait(); // 等待偶数线程执行完毕
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
}
}
}

判断是否奇偶数的方法:

    static boolean isEvenNo(int evenNo) {
return evenNo % 2 == 0;
}

执行部分:

    /**
* 使用锁对象自身的等待与唤醒方法实现
*/
@Test
public void useLockObj() {
OddAndEvenDemo oddAndEvenDemo = new OddAndEvenDemo(); // 开启奇数线程
Thread oddThread = new Thread(() -> oddAndEvenDemo.odd());
// 开启偶数线程
Thread evenThread = new Thread(() -> oddAndEvenDemo.even());
oddThread.start();
evenThread.start();
}

2、使用Condition实现等待与唤醒:

  /**
* 等待唤醒Condition方法
*/
static class OddAndEvenDemo2 {
private int printNo = 0;
private Lock lock = new ReentrantLock(); // 不设置为公平锁
private Condition condition = lock.newCondition();
/**
* 奇数打印方法,由奇数线程调用
*/
public void odd() {
while (printNo < 10) {
lock.lock();
try {
if (!isEvenNo(printNo)) {
System.out.println("奇数:" + printNo);
printNo ++;
condition.signal();
} else {
try {
condition.await();
} catch (Exception exception) {
exception.printStackTrace();
}
}
} catch (Exception exception) {
exception.printStackTrace();
} finally {
lock.unlock();
}
}
} /**
* 偶数打印方法,偶数线程调用
*/
public void even() {
while (printNo < 10) {
lock.lock();
try {
if (isEvenNo(printNo)) {
System.out.println("偶数:" + printNo);
printNo ++;
condition.signal();
} else {
try {
condition.await(); // 等待偶数线程执行完毕
} catch (Exception exception) {
exception.printStackTrace();
}
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
}

执行部分:

    /**
* 使用Condition对象方法实现
*/
@Test
public void useCondition() {
OddAndEvenDemo2 oddAndEvenDemo = new OddAndEvenDemo2(); // 开启奇数线程
Thread oddThread = new Thread(() -> oddAndEvenDemo.odd());
// 开启偶数线程
Thread evenThread = new Thread(() -> oddAndEvenDemo.even());
oddThread.start();
evenThread.start();
}

- Object方法和Condition的区别总结:

1、Object锁对象基于同步关键字组合使用,等待与唤醒都是使用Object的wait & notify 且锁使用syncornized

2、Condition用于配合Lock对象组合使用,等待与唤醒使用 signal & await方法

二、指定数量等待 CountDownLatch

代码案例:

设置三个运动员线程和一个教练线程

只有等待三个运动员线程准备就绪之后,教练线程开始吹口哨开始训练

package cn.cloud9.test.multithread;

import java.util.concurrent.CountDownLatch;

/**
*
*/
public class CountDownLatchDemo { static class CoachRacerDemo {
private CountDownLatch cdl = new CountDownLatch(3); // 设置需要等待的线程数量 /**
* 运动员方法
*/
public void racer() {
// 获取线程名称
String name = Thread.currentThread().getName();
System.out.println(name + " is preparing ... ");
try {
Thread.sleep(1000); } catch (Exception exception) {
exception.printStackTrace();
}
System.out.println(name + " is ready!");
cdl.countDown();
} /**
* 教练方法
*/
public void coach() {
String name = Thread.currentThread().getName();
System.out.println(name + " wait racer prepare ready ...");
try {
cdl.await();
} catch (Exception exception) {
exception.printStackTrace();
}
System.out.println("all racer is ready! start training!");
} } public static void main(String[] args) {
CoachRacerDemo coachRacerDemo = new CoachRacerDemo();
Thread racer1 = new Thread(() -> coachRacerDemo.racer(), "racer-01");
Thread racer2 = new Thread(() -> coachRacerDemo.racer(), "racer-02");
Thread racer3 = new Thread(() -> coachRacerDemo.racer(), "racer-03"); Thread coach = new Thread(() -> coachRacerDemo.coach(), "coach"); // coach线程会先等待其他线程执行,直到等待数量的线程都执行完毕之后开始继续执行
coach.start();
racer1.start();
racer2.start();
racer3.start();
}
}

三、统一执行 CyclicBarrier

package cn.cloud9.test.multithread;

import java.util.Date;
import java.util.concurrent.CyclicBarrier; /**
* CyclicBarrier
* 作用:
* 让一组线程等待到某个状态之后,再全部同时执行
* CyclicBarrier底层基于 ReentrantLock和Condition实现
*
*/
public class CyclicBarrierDemo { static class RunTogetherDemo {
final CyclicBarrier cyclicBarrier = new CyclicBarrier(3); // 参与同时起跑的线程数 public void startThread(int sec) {
String name = Thread.currentThread().getName();
System.out.println(name + " 正在准备...");
try {
Thread.sleep(sec);
cyclicBarrier.await();
} catch (Exception exception) {
exception.printStackTrace();
}
System.out.println(name + " 已经启动完毕:" + new Date().getTime());
}
} public static void main(String[] args) {
final RunTogetherDemo cyclicBarrierDemo = new RunTogetherDemo();
Thread thread1 = new Thread(() -> cyclicBarrierDemo.startThread(300));
Thread thread2 = new Thread(() -> cyclicBarrierDemo.startThread(400));
Thread thread3 = new Thread(() -> cyclicBarrierDemo.startThread(500));
thread1.start();
thread2.start();
thread3.start();
} }

执行之后三个线程会在同一时刻开始执行await方法后的代码块

尽管之前让线程睡眠了不同时长,最后启动完毕的时间戳是一样的

四、资源访问控制 Semaphore

代码案例:

8个工人 使用 3台机器,机器为互斥资源(即每次只能让一个工人来操作)

package cn.cloud9.test.multithread;

import java.util.concurrent.Semaphore;

/**
* 互斥案例
*/
public class SemaphoreDemo { /**
*
*/
static class WorkMachineDemo implements Runnable {
private int worker;
private Semaphore semaphore; public WorkMachineDemo(int worker, Semaphore semaphore) {
this.worker = worker;
this.semaphore = semaphore;
} @Override
public void run(){
try {
// 1、工人获取机器
semaphore.acquire();
// 2、打印工人获取到机器,开始工作
String name = Thread.currentThread().getName();
System.out.println(name + " 获取到机器,开始作业");
// 3、线程睡眠一秒,模拟工人机器操作中
Thread.sleep(1000);
// 3、使用完毕,工人下机
semaphore.release();
System.out.println(name + " 作业完毕,工人下机");
} catch (Exception exception) {
exception.printStackTrace();
}
}
} public static void main(String[] args) {
int worker = 8;
Semaphore semaphore = new Semaphore(3);
WorkMachineDemo workMachineDemo = new WorkMachineDemo(worker, semaphore);
for (int i = 0; i < worker; i++) {
new Thread(workMachineDemo).start();
}
}
}

【Java】MultiThread 多线程 Re02 线程通讯的更多相关文章

  1. Java:多线程,线程同步,同步锁(Lock)的使用(ReentrantLock、ReentrantReadWriteLock)

    关于线程的同步,可以使用synchronized关键字,或者是使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象.本文探讨Lock对象. synchronize ...

  2. 【java】-- 多线程之间实现通讯

    1.多线程之间如何实现通讯 1.1.什么是多线程之间通讯? 多线程之间通讯,其实就是多个线程在操作同一个资源,但是操作的动作不同. 画图演示 1.2.多线程之间通讯需求 需求:第一个线程写入(inpu ...

  3. Java 基础 多线程和线程池基础

    一,多线程 1.1 多线程介绍 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线程:线程是进程中的一个执行单元,负 ...

  4. java 中多线程之间的通讯之生产者和消费者 (多个线程之间的通讯)

    在真实开发 中关于多线程的通讯的问题用到下边的例子是比较多的 不同的地方时if 和while 的区别 如果只是两个线程之间的通讯,使用if是没有问题的. 但是在多个线程之间就会有问题 /* * 这个例 ...

  5. Java:多线程,线程池,使用CompletionService通过Future来处理Callable的返回结果

    1. 背景 在Java5的多线程中,可以使用Callable接口来实现具有返回值的线程.使用线程池的submit方法提交Callable任务,利用submit方法返回的Future存根,调用此存根的g ...

  6. java核心技术-多线程之线程内存模型

    对于每一种编程语言,理解它的内存模型是理所当然的重要.下面我们从jvm的内存模型来体会下java(不限java语言,严格来讲是JVM内存模型,所有JVM体系的变成语言均适用)的内存模型. 堆: 就是我 ...

  7. java核心-多线程(4)-线程类基础知识

    1.并发 <1>使用并发的一个重要原因是提高执行效率.由于I/O等情况阻塞,单个任务并不能充分利用CPU时间.所以在单处理器的机器上也应该使用并发. <2>为了实现并发,操作系 ...

  8. java 中多线程之间的通讯之等待唤醒机制

    wait notify () nitifyAll () 都使用在同步中,因为要对持有监视器(锁)的线程操作 所以要使用在同步中,因为只有同步才具有锁 为什么这些操作线程的方法要定义object类中呢 ...

  9. java核心技术-多线程之线程基础

    说起线程,无法免俗首先要弄清楚的三个概念就是:进程.线程.协程.OK,那什么是进程,什么是线程,哪协程又是啥东西.进程:进程可以简单的理解为运行在操作系统中的程序,程序时静态代码,进程是动态运行着的代 ...

  10. Java:多线程,线程池,ThreadPoolExecutor详解

    1. ThreadPoolExecutor的一个常用的构造方法 ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepA ...

随机推荐

  1. python-使用百度AipOcr实现表格文字图片识别

    注:本博客中的代码实现来自百度问答:https://jingyan.baidu.com/article/c1a3101ef9131c9e646deb5c.html 代码运行环境:win10  pyth ...

  2. vue过滤器 - filters

    在数据被渲染之前,可以对其进行进一步处理,比如将字符截取或者将小写统一转换为大写等等,过滤器本身就是一个方法. 过滤器可以定义全局或局部 # 全局 // 回调函数中的参数1永久是绑定的数据 Vue.f ...

  3. 【Java面试题-基础知识01】Java数据类型四连问?

    一.Java中的基础数据类型有哪些? Java中的基本数据类型包括: 1. byte:8位有符号整数,范围为-128到127.2. short:16位有符号整数,范围为-32768到32767.3. ...

  4. redis 远程连接

    redis-cli -h host -p port -a password -h 服务器地址 -p 端口号 -a 密码

  5. leetcode | 107. 二叉树的层序遍历 II | javascript实现 | c++实现

    题目 给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 . (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历) 思路 题目的要求相当于是求层序遍历数组的转置,我们只需利用js的 ...

  6. 内部网关协议RIP-路由选择协议

    路由信息协议RIP(Routing Information Protocol)是内部网关协议IGP中最先得到广泛使用的协议,其相关标准文档为RFC1058. 一.RIP基本工作原理 RIP要求自治系统 ...

  7. 使用腾讯元宝+markmap生成思维导图

    AI可以帮助我们进行提炼和总结, 节省了大量搜索资料和查阅的时间,像上图这张思维导图,就是使用腾讯元宝大模型进行内容提炼,再使用markmap生成思维导图,下面讲解下详细实现步骤: 一.工具准备 腾讯 ...

  8. redis简单应用demo - 订单号自增长的思路:业务编码+地区+自增数值

    redis简单应用demo1.字符串127.0.0.1:6379> set hello toneyOK127.0.0.1:6379> type hellostring127.0.0.1:6 ...

  9. Excel表格MID函数使用-截图单元格字符长度

    Excel表格MID函数使用-截图单元格字符长度 =MID(B2,1,6) //代表的是从B2单元格,从第1个字符开始,截图6个字符长度.然后双击该单元格,整个列都会截取

  10. WPF/C#:数据绑定到方法

    在WPF Samples中有一个关于数据绑定到方法的Demo,该Demo结构如下: 运行效果如下所示: 来看看是如何实现的. 先来看下MainWindow.xaml中的内容: <Window.R ...