线程学习(15)-保护性暂停模式

保护性暂停是什么?

定义

即 Guarded Suspension,用在一个线程等待另一个线程的执行结果
要点
有一个结果需要从一个线程传递到另一个线程,让他们关联同一个 GuardedObject
如果有结果不断从一个线程到另一个线程那么可以使用消息队列(见生产者/消费者)
JDK 中,join 的实现、Future 的实现,采用的就是此模式
因为要等待另一方的结果,因此归类到同步模式


普通方式

package com.bo.threadstudy.four;

import lombok.extern.slf4j.Slf4j;

/**
 * 保护性暂停模式
 * 即 Guarded Suspension,用在一个线程等待另一个线程的执行结果
 * 要点
 * 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个 GuardedObject
 * 如果有结果不断从一个线程到另一个线程那么可以使用消息队列(见生产者/消费者)
 * JDK 中,join 的实现、Future 的实现,采用的就是此模式
 * 因为要等待另一方的结果,因此归类到同步模式
 */
@Slf4j
public class ProtectiveSuspensionTest {
    //其实就是线程间传递结果信息的问题,我先自己写一个
    public static void main(String[] args) throws InterruptedException {
        GuardedSuspension guardedSuspension = new GuardedSuspension();

        new Thread(() -> {
            String s = (String)guardedSuspension.get();
            log.debug(s);
        }).start();

        log.debug("开始");
        Thread.sleep(2000);

        guardedSuspension.set("你的名字");
    }

}

//这里的话我按泛型来写吧,好久没用了
class GuardedSuspension<T>{
    private T obj;
    private Object lock = new Object();

    public T get(){
        synchronized (lock){
            while(obj == null){
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //唤醒后再执行操作
        }

        return obj;
    }

    //底层用到notify,wait,肯定得加锁
    public void set(T obj){
        synchronized (lock){
            this.obj = obj;
            lock.notifyAll();
        }

    }
}

这里要用wait的原因,一方面多线程操作数据误差,另一个,如果不用wait,死循环会占用很多CPU资源。

wait超时版本

在wait上加超时,这个东西就在等待时间结束后自动释放,防止锁死。

在这里,我直接在wait后面加个时间是不可以的,要这么想,我时间完了,然后此时obj对象没有值,继续while循环,然后继续wait等待时间,又是死循环。

所以,我们需要添加一个条件。可以让这个等待时间到了后跳出循环。以第一次进入同步方法为初始时间,假设我中间被其它线程唤醒了,记录一下唤醒时间。然后,我是否已经获得我所期望的值,如果获得,那么就可以 继续往后面走了。如果没有获得,判断我现在的等待时间,和期望等待时间比较大小,如果时间已经到了,那么跳出循环,没有到,则继续等待剩余的时间。

package com.bo.threadstudy.four;

import lombok.extern.slf4j.Slf4j;

/**
 * 保护性暂停模式
 * 即 Guarded Suspension,用在一个线程等待另一个线程的执行结果
 * 要点
 * 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个 GuardedObject
 * 如果有结果不断从一个线程到另一个线程那么可以使用消息队列(见生产者/消费者)
 * JDK 中,join 的实现、Future 的实现,采用的就是此模式
 * 因为要等待另一方的结果,因此归类到同步模式
 */
@Slf4j
public class ProtectiveSuspensionTest {
    //其实就是线程间传递结果信息的问题,我先自己写一个
    public static void main(String[] args) throws InterruptedException {
        GuardedSuspension guardedSuspension = new GuardedSuspension();

        new Thread(() -> {
            String s = (String)guardedSuspension.get(1000);
            log.debug(s);
        }).start();

        log.debug("开始");
        Thread.sleep(3000);

        guardedSuspension.set("你的名字");
    }

}

//这里的话我按泛型来写吧,好久没用了
class GuardedSuspension<T>{
    private T obj;
    private Object lock = new Object();

    public T get(long time){
        synchronized (lock){
            //最初的时间
            long begin = System.currentTimeMillis();
            //已经经历的时间
            long timePassed = 0;
            while(obj == null){
                try {
                    if(timePassed < time){
                        lock.wait(time - timePassed);
                    }else{
                        break;
                    }

                    timePassed = System.currentTimeMillis() - begin;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //唤醒后再执行操作
        }

        return obj;
    }

    //底层用到notify,wait,肯定得加锁
    public void set(T obj){
        synchronized (lock){
            this.obj = obj;
            lock.notifyAll();
        }

    }
}

多任务版GuardedObject

图中 Futures 就好比居民楼一层的信箱(每个信箱有房间编号),左侧的 t0,t2,t4 就好比等待邮件的居民,右
侧的 t1,t3,t5 就好比邮递员
如果需要在多个类之间使用 GuardedObject 对象,作为参数传递不是很方便,因此设计一个用来解耦的中间类,
这样不仅能够解耦【结果等待者】和【结果生产者】,还能够同时支持多个任务的管理

 就相当于现在有一个大快递柜,然后送件人和取件人是一对一的关系。完成操作。

老师写的代码对我而言,不好理解,我自己按自己的思路写了一个。没有线程安全问题。

package com.bo.threadstudy.four;

import lombok.extern.slf4j.Slf4j;

import java.util.Hashtable;
import java.util.LinkedList;

/**
 * 多个元素间的保护性暂停模式
 */
@Slf4j
public class ManyProtectiveSuspensionTest {

    public static void main(String[] args) {

        MiddleObjList middleObjList = new MiddleObjList();

        for (int i = 0; i < 3; i++) {
            int finalI = i;
            new Thread(() -> {
                MiddleObj<String> obj = new MiddleObj<String>(finalI);
                obj.set("送你个礼物"+finalI);
                log.debug("送你个礼物"+finalI);
                middleObjList.produce(obj);
            },"邮递员"+i).start();

        }

        for (int i = 0; i < 3; i++) {
            int finalI = i;
            new Thread(() -> {
                MiddleObj middleObj = middleObjList.custome(finalI);
                String s = (String)middleObj.get(1000);
                log.debug(finalI+"礼物已收到:"+s);
            },"收信人"+i).start();

        }

    }
}

/**
 * 中间对象
 */
class MiddleObj<T>{
    private T response;

    private Object lock = new Object();
    //需要id来做一个一对一的关系
    private int id;

    public MiddleObj(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public T get(long time){
        synchronized (lock){
            long begin = System.currentTimeMillis();
            //记录一个中间时间
            long passedTime = 0;

            while(response == null){
                //需要阻塞至时间到头
                long middleTime = time - passedTime;
                if(middleTime <= 0){
                    break;
                }

                //阻塞线程时间
                try {
                    lock.wait(middleTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                passedTime = System.currentTimeMillis() - begin;
            }

        }
        return response;
    }

    public void set(T response){
        synchronized (lock){
            this.response = response;
            lock.notifyAll();
        }
    }

}

//存放中间对象
class MiddleObjList{
    private Hashtable<Integer,MiddleObj> table = new Hashtable<Integer,MiddleObj>();

    //这个消费可以通过ID来进行消费,这里应该也是分为生产消费两种方法
    //生产
    public void produce(MiddleObj obj){
        //得考虑并发插入的情况,不过Hashtable是线程安全的,所以暂时先不用考虑
        table.put(obj.getId(),obj);
        synchronized (this){
            this.notifyAll();
        }

    }

    //消费
    public MiddleObj custome(int id){
        //消费完成后,需要移除
        while(!table.containsKey(id)){
            try {
                synchronized (this){
                    this.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        MiddleObj middleObj = table.remove(id);
        return middleObj;
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值