同步模式-保护性暂停

同步模式-保护性暂停

1.概念

即 Guarded Suspension,用在一个线程等待另一个线程的执行结果。

要点:

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

在这里插入图片描述

保护性暂停模式的特点:

  • 与join()相比,不用等到另一个线程结束再执行别的代码。
  • 等待结果的变量能够设置成局部的,不用设置成全局的。

2.GuardedObject代码和测试代码

模拟网上下载

package com.concurrent.p3;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

/**
 * 同步模式-保护性暂停
 */
@Slf4j(topic = "c.TestModel_GuardedObject")
public class TestModel_GuardedObject {
    @Test
    public void testGuardedObject() {
        //定义共享对象
        GuardedObject guardedObject = new GuardedObject();

        //线程1执行下载
        Thread t1 = new Thread(() -> {
            log.debug("正在下载,请稍等...");
            List<String> list = (ArrayList<String>) WebDownload.download();
            guardedObject.complete(list);
            log.debug("已经完成下载");
        }, "t1");
        t1.start();

        //线程2获取下载结果
        Thread t2 = new Thread(() -> {
            log.debug("获取下载内容...");
            List<String> list = (ArrayList<String>) guardedObject.get();
            list.forEach((s) -> {
                log.debug(s);
            });
        }, "t2");
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 线程同步对象:GuardedObject
 */

class GuardedObject {
    //同步对象
    private Object resp;

    //获取结果
    public Object get() {
        synchronized (this) {
            //循环判断,如果当前结果为空则等待;不为空则返回结果
            while (this.resp== null) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return resp;
        }
    }

    //产生结果
    public void complete(Object resp) {
        synchronized (this) {
            this.resp= resp;
            //唤醒所有等待线程
            this.notifyAll();
        }
    }
}

/**
 * 模拟网页下载器:WebDownload
 */
@Slf4j(topic = "c.WebDownload")
class WebDownload {
    public static Object download() {
        List<String> list = new ArrayList<>();
        //模拟5秒内完成下载
        for (int i = 0; i < 5; i++) {
            log.debug("还需要{}秒完成下载", (5 - i));
            try {
                Thread.sleep(1000);
                list.add("Java" + i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return list;
    }
}

在这里插入图片描述

3.扩展1-添加获取超时参数

//获取结果扩展-添加获取超时参数
//timeout 最大等待时间
public Object get(long timeout) {
    synchronized (this) {
        long passedTime = 0;
        long beginTime = System.currentTimeMillis();
        //循环判断,如果当前结果为空则等待;不为空则返回结果
        while (this.resp == null) {
            long waitTime = timeout - passedTime;
            //如果经历时间超过超时时间,返回
            if (waitTime <= 0) {
                break;
            }
            try {
                this.wait(waitTime);  //避免虚假唤醒15:00:01
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            passedTime = System.currentTimeMillis() - beginTime;
        }
        return resp;
    }
}

在这里插入图片描述

4.扩展2-多任务版GuardedObject

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

在这里插入图片描述
新增 id 用来标识 Guarded Object。

同步类MailGuardedObject:

package com.concurrent.p3.multiGuardedObject;

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.MailGuardedObject")
public class MailGuardedObject {
    //标识 GuardedObject resp
    private Integer id;

    private Object resp;

    public MailGuardedObject(Integer id) {
        this.id = id;
    }

    public Integer getId() {
        return id;
    }


    //产生结果
    public void complete(Object obj) {
        synchronized (this) {
            this.resp = obj;
            this.notifyAll();
        }
    }

    //获取结果
    public Object get() {
        synchronized (this) {
            while (resp == null) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return resp;
        }
    }

    //超时获取结果
    public Object get(long timeout) {
        synchronized (this) {
            long begin = System.currentTimeMillis();
            long passTime = 0;  //经历时间
            while (resp == null) {
                long waitTime = timeout - passTime;
                if (waitTime <= 0) {
                    break;
                }
                try {
                    this.wait(waitTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                passTime = System.currentTimeMillis() - begin;
            }
            return resp;
        }
    }

}

中间解耦类 MailBox

package com.concurrent.p3.multiGuardedObject;

import lombok.extern.slf4j.Slf4j;

import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

/**
 * 中间解耦类
 * 解耦结果的产生者和结果获取者
 * RPC框架常用
 */
@Slf4j(topic = "c.MailBox")
public class MailBox {
    //HashTable是线程安全的
    private static Map<Integer, MailGuardedObject> mailBoxs = new Hashtable<>();

    //唯一标识
    private static int id = 1;

    //id递增
    private synchronized static int generateId() {
        return id++;
    }

    //产生MailGuardedObject
    public static MailGuardedObject createMailGuardedObject() {
        //创建MailGuardedObject对象,id自增
        MailGuardedObject mailGuardedObject = new MailGuardedObject(generateId());
        mailBoxs.put(mailGuardedObject.getId(), mailGuardedObject);
        return mailGuardedObject;
    }

    //根据id获取MailGuardedObject
    public static MailGuardedObject getMailGuardedObject(int id) {
        return mailBoxs.remove(id);
    }

    //返回编id集合
    public static Set<Integer> getIds() {
        return mailBoxs.keySet();
    }
}

收信人类 User

package com.concurrent.p3.multiGuardedObject;

import lombok.extern.slf4j.Slf4j;

/**
 * 用户线程,获取邮件
 */
@Slf4j(topic = "c.User")
public class User extends Thread {
    @Override
    public void run() {
        //收信
        MailGuardedObject mailGuardedObject = MailBox.createMailGuardedObject();
        log.debug("开始收信 id:{}", mailGuardedObject.getId());
        Object mail = mailGuardedObject.get(5000);
        log.debug("收信 id:{},内容:{}", mailGuardedObject.getId(), mail);
    }
}

邮递员类 Postman

package com.concurrent.p3.multiGuardedObject;

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Postman")
public class Postman extends Thread {

    private int id;
    private String mail;

    public Postman(int id, String mail) {
        this.id = id;
        this.mail = mail;
    }

    @Override
    public void run() {
        MailGuardedObject mailGuardedObject = MailBox.getMailGuardedObject(id);
        log.debug("送信id:{},内容:{}", mailGuardedObject.getId(), mail);
        mailGuardedObject.complete(mail);
    }
}

测试代码

import org.junit.Test;

@Slf4j(topic = "c.TestModel_MultiGuardedObject")
public class TestModel_MultiGuardedObject {

    @Test
    public void test() throws InterruptedException {
        for (int i = 0; i < 3; i++) {
            new User().start();
        }
        Thread.sleep(1000);
        for (Integer id : MailBox.getIds()) {
            new Postman(id, "[content:" + id + "]").start();
        }

        Thread.sleep(5000);
    }
}

在这里插入图片描述

5.join原理

join()应用了保护性暂停模式。

源码分析

public final void join() throws InterruptedException {
    join(0); 
}
public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();    //开始时间
    long now = 0;  //记录经历的时间

    if (millis < 0) {    //等待时间小于0,不合法
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {  //等待时间等于0,会一直等待下去。wait()
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
	   // 防止错误唤醒
            long delay = millis - now;  
            if (delay <= 0) {  
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值