利用wait和notify方法实现
有这么一个场景,我们有两个线程,其中一个线程需要等待获取另外一个线程的执行结果,之气那我们是使用join方法可以实现,现在我们不用jon来实现。
package com.dongmu.test;
/**
* 多线程设计宝石之保护性暂停模式
*
* 这个设计模式相对于join方法有什么好处呢?
* 1:我们在工作线程结束之后还可以执行其他的操作,而join方法只能等待工作的线程执行结束之后才嫩返回
* 结果让主线程继续运行。
* 2:我们这里等待结果的变量是局部的而不是全局的。
*/
public class Test19 {
public static void main(String[] args) {
GuardedObject guardedObject = new GuardedObject();
new Thread(()->{
Object o = guardedObject.get();
System.out.println("等待获取数据成功");
},"t1").start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{
guardedObject.cmplete();
},"t2").start();
}
}
class GuardedObject{
// 需要获取的结果
private Object response;
private Object lock = new Object();
public Object get(){
synchronized (lock){
while (response==null){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return response;
}
}
public void cmplete(){
synchronized (lock){
response = new Object();
lock.notifyAll();
}
}
}
可以设置超时时间的get方法
public Object get(long timeout){
long now = System.currentTimeMillis();
long passtime = 0;
synchronized (lock){
while (response==null){
if (passtime>=timeout) break;
try {
// 这里这么写是为了避免虚假唤醒的问题。也就是调用complete方法传递null值
lock.wait(timeout-passtime);
} catch (InterruptedException e) {
e.printStackTrace();
}
passtime = System.currentTimeMillis() - now;
}
return response;
}
}
同时我们可以看一下join方法的底层实现原理,这里面也用到了超时等待,我们上面所写的就是join超时等待的实现原理。
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
上面都是单任务的情况,我们还可以对上面的代码进行修改,改成多任务的版本。
多任务版本
在GuradeObject类中加入以下代码
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public GuardedObject(int id) {
this.id = id;
}
并创建一个公共类用来存放GuardedObject 类
/*
这个MailBox是一个通用类,而上面的Postman和people都是和业务相关的代码,以后可以根据业务逻辑来实现
*/
class MailBox{
private static Map<Integer,GuardedObject> map = new ConcurrentHashMap<>();
private static int id = 1;
private static synchronized int getId(){
return id++;
}
public static GuardedObject getGuardedObject(int id){
return map.remove(id);
}
public static GuardedObject createGurdeObject (){
GuardedObject guardedObject = new GuardedObject(getId());
map.put(guardedObject.getId(),guardedObject);
return guardedObject;
}
public static Set<Integer> getIds(){
return map.keySet();
}
}
同时然后编写业务类,分别添加任务和获取执行的结果
class People extends Thread{
@Override
public void run() {
GuardedObject gurdeObject = MailBox.createGurdeObject();
System.out.println("线程id"+gurdeObject.getId()+" 开始接收内容");
Object o = gurdeObject.get();
System.out.println("线程id"+gurdeObject.getId()+",获取到内容:"+o);
}
}
class PostMan extends Thread{
private int id;
public PostMan( int id) {
this.id = id;
}
@Override
public void run() {
GuardedObject guardedObject = MailBox.getGuardedObject(id);
guardedObject.cmplete("线程:"+id);
}
}
最后编写代码进行测试
for (int i = 0; i < 3; i++) {
new People().start();
}
Thread.sleep(2000);
for (Integer id : MailBox.getIds()) {
new PostMan(id).start();
}