前言
线程之间的通信就是当多个线程共同操作共享资源时,互相告知自己的状态,避免资源争夺。
在JUC中,提供了多个API来实现线程间通信,下面我们简单通过一个简单例子来快速入手线程间通信
下面的例子我们主要用到了await()和signalAll()这两个API来实现线程间通信
await()
:await()方法是线程间进行等待和通知的一种机制
用于让当前线程进入等待状态,直到其他线程通过调用signal()方法或signalAll()方法来唤醒它
当调用await()方法时,当前线程将释放对象上的锁,并进入等待状态,直到以下情况之一发生:
1、其他线程调用了相同对象的signal()方法,唤醒了在此对象上等待的一个线程;
2、其他线程调用了相同对象的signalAll()方法,唤醒了在此对象上等待的所有线程;
3、当前线程被中断,即其他线程调用了当前线程的interrupt()方法。
signalAll()
:signalAll()方法也是用于线程间通信的一种机制
用于唤醒等待在某个条件上的所有线程的方法
要正确使用signalAll()方法,需要满足以下要求:
1、调用signalAll()方法的线程必须先持有与条件关联的锁,通常是在进入await()方法之前。
2、其他等待线程必须能够重新获取该锁以继续执行。这通常需要在调用signalAll()方法后释放锁。
一、资源类
首先我们定义一个资源类,多个线程都通过这个类的API对这个资源类进行操作。
@Slf4j(topic = "e")
class Resource {
//定义两个属性,多个线程对这两个属性做出修改操作
private String info;
private int inId=0;
Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();
//定义方法init,
public void init(){
//加锁
lock.lock();
try {
//判断inId是否等于0,等于0则给info赋值,使用while而不是if是为了避免虚假唤醒
while (inId!=0){
//线程等待
condition.await();
}
//给info赋值
info=" my inId is 0";
inId++;
log.debug("this thread is:{}",Thread.currentThread().getName()+info);
//通知其他线程(实现线程间通信)
condition.signalAll();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
//关锁
lock.unlock();
}
}
//定义方法delete
public void delete() {
//加锁
lock.lock();
try {
//判断inId是否等于1,等于0则给info赋值,使用while而不是if是为了避免虚假唤醒
while (inId != 1) {
//线程等待
condition.await();
}
//给info赋值
info = " my inId is 1";
inId--;
log.debug("this thread is:{}",Thread.currentThread().getName()+info);
//通知其他线程(实现线程间通信)
condition.signalAll();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
//关锁
lock.unlock();
}
}
}
二、创建多个线程
//线程t1调用资源类的init方法
new Thread(()->{
for (int i = 0; i < 5; i++) {
resource.init();
}
},"t1").start();
//线程t2调用资源类的delete方法
new Thread(()->{
for (int i = 0; i < 5; i++) {
resource.delete();
}
},"t2").start();
//线程t3调用资源类的delete方法
new Thread(()->{
for (int i = 0; i < 5; i++) {
resource.delete();
}
},"t3").start();
//线程t4调用资源类的init方法
new Thread(()->{
for (int i = 0; i < 5; i++) {
resource.init();
}
},"t4").start();
最后的结果
全部的代码如下:
package com.darkforest.threadcommunication;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j(topic = "e")
class Resource {
//定义两个属性,多个线程对这两个属性做出修改操作
private String info;
private int inId=0;
Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();
//定义方法init,
public void init(){
//加锁
lock.lock();
try {
//判断inId是否等于0,等于0则给info赋值,使用while而不是if是为了避免虚假唤醒
while (inId!=0){
/*线程等待
* await()方法是线程间进行等待和通知的一种机制
* 用于让当前线程进入等待状态,直到其他线程通过调用signal()方法或signalAll()方法来唤醒它
* 当调用await()方法时,当前线程将释放对象上的锁,并进入等待状态,直到以下情况之一发生:
其他线程调用了相同对象的signal()方法,唤醒了在此对象上等待的一个线程;
其他线程调用了相同对象的signalAll()方法,唤醒了在此对象上等待的所有线程;
当前线程被中断,即其他线程调用了当前线程的interrupt()方法。
* */
condition.await();
}
//给info赋值
info=" my inId is 0";
inId++;
log.debug("this thread is:{}",Thread.currentThread().getName()+info);
//通知其他线程(实现线程间通信)
condition.signalAll();
} catch (InterruptedException e) {
//调用await()方法前,当前线程必须先获取到了对象上的锁,否则将会抛出IllegalMonitorStateException异常。
throw new RuntimeException(e);
}finally {
//关锁
lock.unlock();
}
}
//定义方法delete
public void delete() {
//加锁
lock.lock();
try {
//判断inId是否等于1,等于0则给info赋值,使用while而不是if是为了避免虚假唤醒
while (inId != 1) {
//线程等待
condition.await();
}
//给info赋值
info = " my inId is 1";
inId--;
log.debug("this thread is:{}",Thread.currentThread().getName()+info);
//通知其他线程(实现线程间通信)
condition.signalAll();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
//关锁
lock.unlock();
}
}
}
public class ThreadCommunication{
public static void main(String[] args) {
Resource resource=new Resource();
//线程t1调用资源类的init方法
new Thread(()->{
for (int i = 0; i < 5; i++) {
resource.init();
}
},"t1").start();
//线程t2调用资源类的delete方法
new Thread(()->{
for (int i = 0; i < 5; i++) {
resource.delete();
}
},"t2").start();
//线程t3调用资源类的delete方法
new Thread(()->{
for (int i = 0; i < 5; i++) {
resource.delete();
}
},"t3").start();
//线程t4调用资源类的init方法
new Thread(()->{
for (int i = 0; i < 5; i++) {
resource.init();
}
},"t4").start();
}
}
总结
对于线程间通信可以使用的API不仅仅是上面提到了这两种,大家有兴趣可以去扩展一下,我后面也会更新新的线程间通信机制。