并发编程-模式篇(同步模式之保护性暂停)

这篇具有很好参考价值的文章主要介绍了并发编程-模式篇(同步模式之保护性暂停)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

同步模式之保护性暂停

定义

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

要点:

有一个结果需要从一个线程传递到另一个线程,让他们关联同一个 GuardedObject

如果有结果不断从一个线程到另一个线程那么可以使用消息队列(见生产者/消费者)

JDK 中,join 的实现、Future 的实现,采用的就是此模式 因为要等待另一方的结果,

因此归类到同步模式

实现

class GuardedObject {
 private Object response;
 private final Object lock = new Object();
 public Object get() {
synchronized (lock) {
// 条件不满足则等待
while (response == null) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return response;
}
}
 public void complete(Object response) {
synchronized (lock) {
// 条件满足,通知等待线程
this.response = response;
lock.notifyAll();
}
 }
}

* 应用

一个线程等待另一个线程的执行结果

public static void main(String[] args) {
 GuardedObject guardedObject = new GuardedObject();
 new Thread(() -> {
try {
// 子线程执行下载
List<String> response = download();
log.debug("download complete...");
guardedObject.complete(response);
} catch (IOException e) {
e.printStackTrace();
}
 }).start();
 log.debug("waiting...");
 // 主线程阻塞等待
 Object response = guardedObject.get();
 log.debug("get response: [{}] lines", ((List<String>) response).size());
}

执行结果

08:42:18.568 [main] c.TestGuardedObject - waiting...
08:42:23.312 [Thread-0] c.TestGuardedObject - download complete...
08:42:23.312 [main] c.TestGuardedObject - get response: [3] lines

带超时版 GuardedObject

如果要控制超时时间呢

class GuardedObjectV2 {
 private Object response;
 private final Object lock = new Object();
public Object get(long millis) {
synchronized (lock) {
// 1) 记录最初时间
long begin = System.currentTimeMillis();
// 2) 已经经历的时间
long timePassed = 0;
while (response == null) {
// 4) 假设 millis 是 1000,结果在 400 时唤醒了,那么还有 600 要等
long waitTime = millis - timePassed;
log.debug("waitTime: {}", waitTime);
if (waitTime <= 0) {
log.debug("break...");
break;
}
try {
lock.wait(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 3) 如果提前被唤醒,这时已经经历的时间假设为 400
timePassed = System.currentTimeMillis() - begin;
log.debug("timePassed: {}, object is null {}", 
timePassed, response == null);
}
return response;
}
 }
 public void complete(Object response) {
synchronized (lock) {
// 条件满足,通知等待线程
this.response = response;
log.debug("notify...");
lock.notifyAll();
}
 }
}

测试,没有超时

public static void main(String[] args) {
 GuardedObjectV2 v2 = new GuardedObjectV2();
 new Thread(() -> {
sleep(1);
v2.complete(null);
sleep(1);
v2.complete(Arrays.asList("a", "b", "c"));
 }).start();
 Object response = v2.get(2500);
 if (response != null) {
log.debug("get response: [{}] lines", ((List<String>) response).size());
} else {
log.debug("can't get response");
 }
}

输出

08:49:39.917 [main] c.GuardedObjectV2 - waitTime: 2500
08:49:40.917 [Thread-0] c.GuardedObjectV2 - notify...
08:49:40.917 [main] c.GuardedObjectV2 - timePassed: 1003, object is null true
08:49:40.917 [main] c.GuardedObjectV2 - waitTime: 1497
08:49:41.918 [Thread-0] c.GuardedObjectV2 - notify...
08:49:41.918 [main] c.GuardedObjectV2 - timePassed: 2004, object is null false
08:49:41.918 [main] c.TestGuardedObjectV2 - get response: [3] lines

测试,超时

// 等待时间不足
List<String> lines = v2.get(1500);

输出

08:47:54.963 [main] c.GuardedObjectV2 - waitTime: 1500
08:47:55.963 [Thread-0] c.GuardedObjectV2 - notify...
08:47:55.963 [main] c.GuardedObjectV2 - timePassed: 1002, object is null true
08:47:55.963 [main] c.GuardedObjectV2 - waitTime: 498
08:47:56.461 [main] c.GuardedObjectV2 - timePassed: 1500, object is null true
08:47:56.461 [main] c.GuardedObjectV2 - waitTime: 0
08:47:56.461 [main] c.GuardedObjectV2 - break...
08:47:56.461 [main] c.TestGuardedObjectV2 - can't get response
08:47:56.963 [Thread-0] c.GuardedObjectV2 - notify...

* 原理之 join

多任务版 GuardedObject

图中 Futures 就好比居民楼一层的信箱(每个信箱有房间编号),左侧的 t0,t2,t4 就好比等待邮件的居民,右 侧的 t1,t3,t5 就好比邮递员

这样不仅能够解耦【结果等待者】和【结果生产者】,还能够同时支持多个任务的管理

新增 id 用来标识 Guarded Object

class GuardedObject {
 // 标识 Guarded Object
 private int id;
 public GuardedObject(int id) {
 this.id = id;
 }
 public int getId() {
 return id;
 }
 // 结果
 private Object response;
 // 获取结果
 // timeout 表示要等待多久 2000
 public Object get(long timeout) {
 synchronized (this) {
 // 开始时间 15:00:00
 long begin = System.currentTimeMillis();
 // 经历的时间
 long passedTime = 0;
 while (response == null) {
 // 这一轮循环应该等待的时间
 long waitTime = timeout - passedTime;
 // 经历的时间超过了最大等待时间时,退出循环
 if (timeout - passedTime <= 0) {
 break;
 }
 try {
 this.wait(waitTime); // 虚假唤醒 15:00:01
 } catch (InterruptedException e) {
 e.printStackTrace();
}
 // 求得经历时间
 passedTime = System.currentTimeMillis() - begin; // 15:00:02 1s
 }
 return response;
 }
 }
 // 产生结果
 public void complete(Object response) {
 synchronized (this) {
 // 给结果成员变量赋值
 this.response = response;
 this.notifyAll();
 }
 }
}

中间解耦类

class Mailboxes {
 private static Map<Integer, GuardedObject> boxes = new Hashtable<>();
 private static int id = 1;
 // 产生唯一 id
 private static synchronized int generateId() {
 return id++;
 }
 public static GuardedObject getGuardedObject(int id) {
 return boxes.remove(id);
 }
 public static GuardedObject createGuardedObject() {
 GuardedObject go = new GuardedObject(generateId());
 boxes.put(go.getId(), go);
 return go;
 }
 public static Set<Integer> getIds() {
 return boxes.keySet();
 }
}

业务相关类

class People extends Thread{
 @Override
 public void run() {
 // 收信
 GuardedObject guardedObject = Mailboxes.createGuardedObject();
 log.debug("开始收信 id:{}", guardedObject.getId());
 Object mail = guardedObject.get(5000);
 log.debug("收到信 id:{}, 内容:{}", guardedObject.getId(), mail);
 }
}
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() {
 GuardedObject guardedObject = Mailboxes.getGuardedObject(id);
 log.debug("送信 id:{}, 内容:{}", id, mail);
 guardedObject.complete(mail);
 }
}

测试

public static void main(String[] args) throws InterruptedException {
 for (int i = 0; i < 3; i++) {
 new People().start();
 }
 Sleeper.sleep(1);
 for (Integer id : Mailboxes.getIds()) {
 new Postman(id, "内容" + id).start();
 }
}

某次运行结果文章来源地址https://www.toymoban.com/news/detail-733452.html

10:35:05.689 c.People [Thread-1] - 开始收信 id:3
10:35:05.689 c.People [Thread-2] - 开始收信 id:1
10:35:05.689 c.People [Thread-0] - 开始收信 id:2
10:35:06.688 c.Postman [Thread-4] - 送信 id:2, 内容:内容2
10:35:06.688 c.Postman [Thread-5] - 送信 id:1, 内容:内容1
10:35:06.688 c.People [Thread-0] - 收到信 id:2, 内容:内容2
10:35:06.688 c.People [Thread-2] - 收到信 id:1, 内容:内容1
10:35:06.688 c.Postman [Thread-3] - 送信 id:3, 内容:内容3
10:35:06.689 c.People [Thread-1] - 收到信 id:3, 内容:内容3

到了这里,关于并发编程-模式篇(同步模式之保护性暂停)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 并发编程 --- 信号量线程同步

    上文编码技巧 --- 同步锁对象的选定中,提到了在C#中,让线程同步有两种方式: 锁(lock、Monitor等) 信号量(EventWaitHandle、Semaphore、Mutex) 加锁是最常用的线程同步的方法,就不再讨论,本篇主要讨论使用信号量同步线程。 实际上,再C#中 EventWaitHandle 、 Semaphore 、 Mutex 都是

    2024年02月16日
    浏览(42)
  • c++并发编程实战-第4章 并发操作的同步

    想象一种情况:假设晚上坐车外出,如何才能确保不坐过站又能使自己最轻松? 这种方式存在双重浪费: 线程 th1(wait_for_flag)须不断查验标志,浪费原本有用的处理时间,这部分计算资源原本可以留给其他线程使用。 线程 th1(wait_for_flag)每次循环都需要给互斥上锁,导致

    2024年02月08日
    浏览(42)
  • 《C++并发编程实战》读书笔记(3):并发操作的同步

    当线程需要等待特定事件发生、或是某个条件成立时,可以使用条件变量 std::condition_variable ,它在标准库头文件 condition_variable 内声明。 wait() 会先在内部调用lambda函数判断条件是否成立,若条件成立则 wait() 返回,否则解锁互斥并让当前线程进入等待状态。当其它线程调用

    2024年02月10日
    浏览(36)
  • 16 Go并发编程(三): Go并发的传统同步机制

    Go 传统同步机制 在《Go并发编程初探》中我们提到同步概念,所谓同步是相对异步而言,即串行相对于并行。 在学习Go通信机制时我们知道管道其实就是并发单元同步方式的一种,基于CSP并发模型,Go在语言原语上使管道作为核心设计,这是Go的设计哲学,也是Go所提倡的同步

    2023年04月08日
    浏览(39)
  • 【JAVA开发面试】如何处理并发访问如何进行代码的单元测试Java多线程编程消息中间件设计模式技术难题是如何解决的

    【 点我-这里送书 】 本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题 中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明

    2024年02月03日
    浏览(52)
  • Python异步编程之web框架 异步vs同步 Redis并发对比

    主题: 比较异步框架和同步框架在RedisIO操作的性能差异 python版本 :python 3.8 数据库 :redis 5.0.7 压测工具 :locust web框架 :同步:flask 异步:starlette 请求并发量 : 模拟10个用户 服务器配置 : Intel(R) i7-12700F 客户端配置 :Intel(R) i7-8700 3.20GHz flask是python中轻量级web框架,特点是灵

    2024年02月10日
    浏览(46)
  • Python异步编程之web框架 异步vs同步 数据库IO任务并发支持对比

    主题: 比较异步框架和同步框架在数据库IO操作的性能差异 python版本 :python 3.8 数据库 :mysql 8.0.27 (docker部署) 压测工具 :locust web框架 :同步:flask 异步:starlette 请求并发量 : 模拟10个用户 服务器配置 : Intel(R) i7-12700F 客户端配置 :Intel(R) i7-8700 3.20GHz python中操作数据库通常

    2024年02月08日
    浏览(58)
  • 并发编程学习(十):共享模式无锁、原子整数、原子引用类型

            获取共享变量时,为了保证该变量的可见性,需要使用volatile修饰。         它可以用来修饰成员变量和静态成员变量,它可以避免线程从自己的工作缓存中查找变量,必须到主存中获取它的值,线程操作volatile变量都是直接操作主存,即一个线程对volatile变量的

    2024年02月04日
    浏览(35)
  • 【并发编程】自研数据同步工具的优化:创建线程池多线程异步去分页调用其他服务接口获取海量数据

    前段时间在做一个数据同步工具,其中一个服务的任务是调用A服务的接口,将数据库中指定数据请求过来,交给kafka去判断哪些数据是需要新增,哪些数据是需要修改的。 刚开始的设计思路是,,我创建多个服务同时去请求A服务的接口,每个服务都请求到全量数据,由于这些

    2024年02月12日
    浏览(40)
  • JUC并发编程学习笔记(十七)彻底玩转单例模式

    单例中最重要的思想-------构造器私有! 恶汉式、懒汉式(DCL懒汉式!) 恶汉式 懒汉式 DCL懒汉式 完整的双重检测锁模式的单例、懒汉式、DCL懒汉式 但是有反射!只要有反射,任何的代码都不安全,任何的私有都是摆设 正常的单例模式: 反射破坏单例: 怎么去解决这

    2024年02月05日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包