一、等待与唤醒

/**
* 线程通讯问题
* 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. java检测当前CPU负载状态的方法

    1.java检测当前CPU负载状态 在Java中,直接检测CPU负载状态并不像在操作系统命令行中那样简单,因为Java标准库并没有直接提供这样的功能.但是,我们可以通过几种方法间接地获取CPU负载信息 ...

  2. yum源的基本知识

    一.yum源配置 1.本地yum源配置内容 [local]#仓库名称,自定义,担具有唯一性 '唯一性是说在yum.repos.d这个文件夹中只能有一个这个名字的yum仓库' name=local_ce ...

  3. 你在测试中发现了一个bug,但是开发经理认为这不是一个bug,你应该怎样解决?

    首先,将问题提交到缺陷管理库里面进行备案. 然后,要获取判断的依据和标准: 根据需求说明书.产品说明.设计文档等,确认实际结果是否与计划有不一致的地方,提供缺陷是否确认的直接依据: 如果没有文档依据, ...

  4. [踩坑记录] Vue3 customRef 传入对象没有进入set方法

    问题描述 学习Vue3 Ref 相关 API 的时候,遇到了 customRef 这个 API,它可以让我们自定义 ref 的更新的过程 但是使用 customRef 有一个问题就是,如果你传入的是初 ...

  5. Java中创建对象的5种方式总结

    引言 作为Java开发人员,我们每天都会代码中创建对象,但我们通常使用依赖管理系统,比如Spring框架,然后,这里有很多种创建对象的方式,本文就对Java创建对象的几种方式进行总结 五种创建方式 创 ...

  6. 记录一次 OpenStack 集群在创建虚机后无法获取 IP 的问题

    现象 所有的组件状态都是正常工作的,DHCP 服务正常工作,在个个虚拟网络设备端口抓包,发现 OpenVSwitch 上 DHCP 包可以通行,虚机获取 DHCP Discover 请求可以正常发出, ...

  7. Stable Diffusion(二)WebUI使用指南

    1. 前言 基于 https://stable-diffusion-art.com/ 内的教程进行翻译与整理,帮助快速上手 stable-diffusion 的使用. 2. 环境 AWS DeepLe ...

  8. 安卓内核编译:关闭"error, forbidden warning"

    安卓内核编译:关闭error, forbidden warning 背景 最近在编译Android kernel时,遇到error, forbidden warning, 导致编译中断,大大降低了de ...

  9. 3562-IgH EtherCAT主站开发案例

  10. FLUTTER 中 Isolate 的一个例子.

    isolate 起因 最近看了一点isolate的东西, 自己写了一个例子. 普通的的 consummer-producer例子是只有前后两端的,实际上,会把前后两端再进行包装. 我这里这个例子,是把 ...