同步模式之保护性暂停

简介: 同步模式之保护性暂停

定义

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

要点

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

基本实现

1. public class GuardedObject{
2. private  Object response;
3. private  final  Object lock=new Object();
4. public Object get(){
5. synchronized (lock){
6. //条件不满足进行等待
7. while (response==null){
8. try {
9.                    lock.wait();
10.                } catch (InterruptedException e) {
11.                    e.printStackTrace();
12.                }
13.            }
14. return response;
15.        }
16.    }
17. 
18. public void complete(Object response){
19. synchronized (lock){
20. this.response=response;
21.            lock.notifyAll();
22.        }
23.    }
24. 
25. }
1. public static void main(String[] args) {
2.         ThreadText GuardedObject= new GuardedObject();
3. new Thread(()->{
4.             String response="结果";
5.             System.out.println("正在加载");
6. try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
7.             GuardedObject.complete(response);
8.         }).start();
9.         System.out.println("wait....");
10. //主线程进行等待
11. Object o = GuardedObject.get();
12.         System.out.println("输出结果===》"+o);
13.     }

带超时版 GuardedObject

1. public class GuardedObject {
2. private  Object response;
3. private  final  Object lock=new Object();
4. public Object get(Long millis){
5. synchronized (lock){
6. //记录最初时间
7. long begin=System.currentTimeMillis();
8. //已经经历时间
9. long timePassed=0;
10. //条件不满足进行等待
11. while (response==null){
12. long waitTime=millis-timePassed;
13. if(waitTime<=0){
14. break;
15.                }
16. try {
17.                    lock.wait();
18.                } catch (InterruptedException e) {
19.                    e.printStackTrace();
20.                }
21.                timePassed=  System.currentTimeMillis()- begin;
22.            }
23. return response;
24.        }
25.    }
26. 
27. public void complete(Object response){
28. synchronized (lock){
29. this.response=response;
30.            lock.notifyAll();
31.        }
32.    }
33. 
34. }

测试代码

1. public static void main(String[] args) {
2.         GuardedObject GuardedObject= new GuardedObject();
3. new Thread(()->{
4.             String response="结果";
5.             System.out.println("正在加载");
6.             GuardedObject.complete(null);
7. try { Thread.sleep(102); } catch (InterruptedException e) { e.printStackTrace(); }
8.             GuardedObject.complete(null);
9. try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); }
10.             GuardedObject.complete(response);
11.         }).start();
12.         System.out.println("wait....");
13. //主线程进行等待
14. Object o = GuardedObject.get(100);
15.         System.out.println("输出结果===》"+o);
16. 
17.     }

多任务版 GuardedObject

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

 如果需要在多个类之间使用 GuardedObject 对象,作为参数传递不是很方便,因此设计一个用来解耦的中间类, 这样不仅能够解耦【结果等待者】和【结果生产者】,还能够同时支持多个任务的管理

新增 id 用来标识 Guarded Object

1. public class GuardedObject {
2. //标识GuardedObject
3. private int id;
4. 
5. public int getId() {
6. return id;
7.     }
8. 
9. public void setId(int id) {
10. this.id = id;
11.     }
12. 
13. private Object response;
14. 
15. //获取结果
16. public Object get(long timeout) {
17. synchronized (this) {
18. //开始时间
19. long begin = System.currentTimeMillis();
20. //经历的时间
21. long passedTime = 0;
22. while (response == null) {
23. long waitTime = timeout - passedTime;
24. if (waitTime <= 0) {
25. break;
26.                 }
27. try {
28. this.wait();
29.                 } catch (InterruptedException e) {
30.                     e.printStackTrace();
31.                 }
32. //求得经历的时间
33.                 passedTime = System.currentTimeMillis() - begin;
34.             }
35. return response;
36.         }
37.     }
38. 
39. //产生结果
40. public void complete(Object response) {
41. synchronized (this) {
42. //给结果进行赋值
43. this.response = response;
44. this.notifyAll();
45.         }
46.     }
47. 
48. public GuardedObject(int id) {
49. this.id = id;
50.     }
51. 
52. 
53. }

中间解耦类

1. class MailBoxes {
2. private static Map<Integer, GuardedObject> boxes = new HashMap<>();
3. private static int id = 1;
4. 
5. //产生唯一id
6. private static synchronized int generateId() {
7. return id++;
8.     }
9. 
10. public static GuardedObject getGuardedObject(int id) {
11. return boxes.remove(id);
12.     }
13. 
14. 
15. public static GuardedObject createGuardedObject() {
16. GuardedObject guardedObject = new GuardedObject(generateId());
17.         boxes.put(guardedObject.getId(), guardedObject);
18. return guardedObject;
19.     }
20. 
21. public static Set<Integer> getIds(){
22. return boxes.keySet();
23.     }
24. 
25. }

业务相关类

1. class  People extends  Thread{
2. public void run() {
3. GuardedObject guardedObject = MailBoxes.createGuardedObject();
4.         System.out.println("开始收信id==>"+guardedObject.getId());
5. Object mail = guardedObject.get(5000);
6.         System.out.println("收到信id==>"+guardedObject.getId()+" 内容==>"+mail);
7. 
8.     }
9. }
10. 
11. 
12. class Postman extends Thread{
13. private  int id;
14. private  String mail;
15. public Postman(int id,String mail){
16. this.id=id;
17. this.mail=mail;
18.     }
19. 
20. public void run() {
21. GuardedObject guardedObject = MailBoxes.getGuardedObject(id);
22.         System.out.println("送信id==>"+id+" 内容==>"+mail);
23.         guardedObject.complete(mail);
24.     }
25. }

测试代码

1. public static void main(String[] args) {
2. for (int i = 0; i < 3; i++) {
3. new People().start();
4. try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
5.         }
6. 
7. for (Integer id:MailBoxes.getIds()){
8. new Postman(id,"内容"+id).start();
9. 
10.         }
11.     }

测试结果:

开始收信id==>1

开始收信id==>2

开始收信id==>3

送信id==>1 内容==>内容1

送信id==>2 内容==>内容2

送信id==>3 内容==>内容3

收到信id==>1 内容==>内容1

收到信id==>2 内容==>内容2

收到信id==>3 内容==>内容3

总结

保护性暂停(Guarded Suspension)是一种线程间的同步机制,它解决了等待-通知模式中的等待超时和虚假唤醒问题。在保护性暂停模式中,一个线程在等待某个特定条件的满足时,会通过循环的方式不断检查这个条件,同时在条件不满足时通过wait()方法来释放占用的锁,并进入等待状态;当条件被满足时,相应的其他线程会通过notify()或notifyAll()方法来唤醒正在等待的线程。

具体来说,保护性暂停包含以下几个方面:

  1. 条件判断:在保护性暂停中,线程在等待前会先进行一次条件判断,以确定是否需要进入等待状态,从而避免不必要的等待和唤醒。通常情况下,在条件不满足时线程会通过wait()方法进入等待状态,而在条件满足时则继续执行。
  2. 执行顺序:在保护性暂停中,线程之间的执行顺序是不可控的。例如,在一个生产者-消费者模型中,当生产者线程唤醒消费者线程时,不能保证其立即执行,也不能保证消费者线程的执行顺序。
  3. 同步机制:在保护性暂停中,需要使用同步机制来确保线程之间的可见性和互斥性。通常情况下,使用synchronized关键字来保护共享资源,在保证线程之间的可见性和互斥性的同时,避免了死锁和饥饿等问题。
  4. 等待超时:为了避免线程一直等待而导致程序不响应,保护性暂停通常会使用等待超时机制。即在等待一定时间后,如果条件仍然不满足,则主动放弃等待并返回一个默认值,从而避免阻塞线程。

总体来说,保护性暂停是一种有效的线程同步机制,它可以在多线程环境下保证数据的正确性和程序的健壮性。但是,在使用保护性暂停时需要注意线程之间的协作和同步问题,特别是在条件判断和等待超时等方面。


相关文章
|
C#
如何解决在PotPlayer中看视频音画不同步的问题(C#视频可用)
如何解决在PotPlayer中看视频音画不同步的问题(C#视频可用)
1040 0
|
2月前
|
存储 安全 调度
同步时视频与控制
【9月更文挑战第25天】
35 2
|
16天前
多线程通信和同步的方式有哪些?
【10月更文挑战第6天】
76 0
|
3月前
|
运维 监控 安全
自动恢复机制在哪些情况下可能无法正常工作
自动恢复机制在哪些情况下可能无法正常工作
|
6月前
|
消息中间件 Java
保护性暂停模式
保护性暂停模式
57 0
|
6月前
线程间的同步的方式有哪些
线程间的同步的方式有哪些
|
安全 程序员 数据库
进程间同步的方式有哪些
进程间同步的方式有哪些
792 0
|
Java 程序员
同步模式之顺序控制线程执行
同步模式是指在多线程编程中,为了保证线程之间的协作和正确性,需要对线程的执行顺序进行控制。顺序控制线程执行是一种同步模式,它通过控制线程的等待和唤醒来实现线程的有序执行。
138 0
同步模式之顺序控制线程执行
|
消息中间件 Cloud Native Java
线程同步模式之保护性暂停
保护性暂停是一种同步模式,用于保护共享资源的完整性。在多线程或多进程环境中,如果多个线程或进程同时访问共享资源,可能会导致数据不一致或者竞态条件等问题
161 0
|
消息中间件 Java
《JUC并发编程 - 模式篇》保护性暂停模式 | 顺序控制模式 | 生产者消费者模式 | 两阶段终止模式 | Balking模式 | 享元模式(一)
《JUC并发编程 - 模式篇》保护性暂停模式 | 顺序控制模式 | 生产者消费者模式 | 两阶段终止模式 | Balking模式 | 享元模式
《JUC并发编程 - 模式篇》保护性暂停模式 | 顺序控制模式 | 生产者消费者模式 | 两阶段终止模式 | Balking模式 | 享元模式(一)