使用wait/notify 实现线程间的通信
API
obj.wait() 让进入 object 监视器的线程到 waitSet 等待
obj.notify() 在 object 上正在 waitSet 等待的线程中挑一个唤醒
obj.notifyAll() 让 object 上正在 waitSet 等待的线程全部唤醒
都属于Object的方法,必须获得此对象的锁才能调用
import lombok.extern.slf4j.Slf4j;
/**
* @Author blackcat
* @create 2021/7/25 21:00
* @version: 1.0
* @description:wait、notify、notifyAll api
*/
@Slf4j
public class ThreadWait {
final static Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
log.info("wait1 enter");
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("wait1 out");
}, "wait1").start();
new Thread(() -> {
log.info("wait2 enter");
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("wait2 out");
}, "wait2").start();
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
// lock.notify();
lock.notifyAll();
}
}
}
//notifyAll()的结果
//21:02:39.005 [wait2] INFO - wait2 enter
//21:02:39.005 [wait1] INFO - wait1 enter
//21:02:41.016 [wait1] INFO - wait1 out
//21:02:41.016 [wait2] INFO - wait2 out
//notify()的结果
//21:02:39.005 [wait2] INFO - wait2 enter
//21:02:39.005 [wait1] INFO - wait1 enter
//21:02:41.016 [wait2] INFO - wait2 out
原理
Monitor:对象的监视器,只要发生同步操作,线程就为当前对象创建一个Monitor对象与之关联,Monitor只能被一个线程持有,此时当前对象就处于锁定状态,其它线程只能阻塞等待。
Java虚拟机(HotSpot)中,Monitor是通过ObjectMonitor实现的(c++),里面有三个重要的属性
ObjectMonitor() {
_WaitSet = NULL; //处于wait状态的线程,会被加入到_WaitSet
_EntryList = NULL ; //处于等待锁block状态的线程,会被加入到该列表
_owner = NULL;
_count = 0; //记录个数
}
Monitor 有两个队列 WaitSet 和 _EntryList,存储ObjectWaiter列表(所有等待的线程都会被包装成ObjectWaiter);① 线程申请owner Monitor对象,首先会被加入到 _EntryList ;② 线程申请owner Monitor对象,进入到 Owner区域,此时count +1; ③线程调用wait方法,进入到 WaitSet ,释放锁,此时count -1 ④ 线程再次申请owner ⑤ 线程处理完毕后释放资源并退出。
保护性暂停模式
即 Guarded Suspension,一个线程等待另一个线程的执行结果
synchronized(lock) {
while(条件不成立) {
lock.wait();
}
// 干活
}
//另一个线程
synchronized(lock) {
lock.notifyAll();
}
import lombok.extern.slf4j.Slf4j;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/**
* @Author blackcat
* @create 2021/7/25 21:49
* @version: 1.0
* @description:保护性暂停模式
*/
@Slf4j
public class GuardedObjectTest {
public static void main(String[] args) {
//线程1等待线程2的结果
GuardedObject object = new GuardedObject(1);
new Thread(() -> {
log.info("begin");
Object response = object.get(2000L);
log.info("end");
}).start();
new Thread(() -> {
log.info("等待结果");
Object response = object.get();
log.info("get response: [{}] size", ((List<String>) response).size());
}).start();
new Thread(() -> {
log.info("执行下载");
try{
List<String> download = Downloader.download();
object.set(download);
}catch (Exception e){
e.printStackTrace();
}
}).start();
}
}
class GuardedObject {
private int id;
private Object response;
public GuardedObject(int id) {
this.id = id;
}
public int getId() {
return id;
}
Object get() {
synchronized (this) {
while (response == null) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return response;
}
}
Object get(Long timeout) {
synchronized (this) {
long begin = System.currentTimeMillis();
long now = 0;
while (response == null) {
timeout = timeout - now;
if (timeout <= 0) {
break;
}
try {
this.wait(timeout);
now = System.currentTimeMillis() - begin;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return response;
}
}
void set(Object response) {
synchronized (this) {
this.response = response;
this.notifyAll();
}
}
}
//下载工具类
class Downloader {
public static List<String> download() throws IOException {
HttpURLConnection conn = (HttpURLConnection) new URL("https://www.baidu.com/").openConnection();
List<String> lines = new ArrayList<>();
try (BufferedReader reader =
new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
lines.add(line);
}
}
return lines;
}
}
生产者-消费者模式
import lombok.extern.slf4j.Slf4j;
import java.util.LinkedList;
/**
* @Author blackcat
* @create 2021/7/25 21:57
* @version: 1.0
* @description:生产者-消费者模式
*/
@Slf4j
public class MessageProduct {
public static void main(String[] args) {
MessageQueue queue = new MessageQueue(2);
for (int i = 0; i < 4; i++) {
int id = i;
new Thread(() -> {
queue.put(new Message(id, "生产者" + id));
},"生产者"+id).start();
}
// 1 个消费者线程, 处理结果
new Thread(() -> {
while (true) {
Message message = queue.get();
Object response = (Object) message.getMsg();
log.debug("take message({}): [{}] ", message.getId(), response);
}
}, "消费者").start();
}
}
class Message {
private int id;
private Object msg;
public Message(int id, Object msg) {
this.id = id;
this.msg = msg;
}
public int getId() {
return id;
}
public Object getMsg() {
return msg;
}
@Override
public String toString() {
return "Message{" +
"id=" + id +
", msg=" + msg +
'}';
}
}
@Slf4j
class MessageQueue {
private int capacity;
private LinkedList<Message> list = new LinkedList<>();
public MessageQueue(int capacity) {
this.capacity = capacity;
}
public void put(Message msg) {
synchronized (this) {
while (list.size() >= capacity) {
try {
log.debug("put wait");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("生产{}",msg);
list.add(msg);
this.notifyAll();
}
}
public Message get() {
synchronized (this) {
while (list.isEmpty()) {
try {
log.debug("get wait");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Message first = list.removeFirst();
this.notifyAll();
return first;
}
}
}
join
import lombok.extern.slf4j.Slf4j;
/**
* @Author blackcat
* @version: 1.0
* @description:
*/
@Slf4j
public class MakeTea {
public static void main(String[] args) {
makeTea();
}
private static void makeTea() {
//洗水壶 -->烧开水
Thread t1 = new Thread(()->{
log.info("洗水壶");
sleep(1);
log.info("烧开水");
sleep(5);
},"老王");
//洗茶壶 --> 洗茶杯 -->拿茶叶
//当前线程等待水烧开再泡茶
Thread t2 = new Thread(()->{
log.info("洗茶壶");
sleep(1);
log.info("洗茶杯");
sleep(2);
log.info("拿茶叶");
sleep(1);
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("泡茶");
},"小王");
t1.start();
t2.start();
}
private static void sleep(int i ) {
try {
Thread.sleep(i * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Threadlocal
Java中的ThreadLocal类可以让你创建的变量只被同一个线程进行读和写操作。
import lombok.extern.slf4j.Slf4j;
/**
* @Author blackcat
* @create 2021/7/25 22:16
* @version: 1.0
* @description:ThreadLocalExample
*/
@Slf4j
public class ThreadLocalExample {
public static class MyRunnable implements Runnable {
private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
@Override
public void run() {
threadLocal.set( (int) (Math.random() * 100) );
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("get result:{}",threadLocal.get());
}
}
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread1 = new Thread(runnable,"t1");
Thread thread2 = new Thread(runnable,"t2");
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
同一个ThreadLocal变量在父线程中被设置值后,在子线程中是获取不到的。
import lombok.extern.slf4j.Slf4j;
/**
* @Author blackcat
* @create 2021/7/25 22:27
* @version: 1.0
* @description:ThreadLocal,InheritableThreadLocal
*/
@Slf4j
public class ThreadLocalTest {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
private static InheritableThreadLocal<String> inThreadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) {
//在main线程中添加main线程的本地变量
threadLocal.set("mainVal");
inThreadLocal.set("inMainVal");
//新创建一个子线程
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
log.info("子线程中的本地变量值:{}" ,threadLocal.get());
log.info("子线程中的in本地变量值:{}" ,inThreadLocal.get());
}
});
thread.start();
//输出main线程中的本地变量值
log.info("main线程中的本地变量值:{}" ,threadLocal.get());
log.info("main线程中的in本地变量值:{}" ,inThreadLocal.get());
}
}