线程同步模式之保护性暂停

简介: 保护性暂停是一种同步模式,用于保护共享资源的完整性。在多线程或多进程环境中,如果多个线程或进程同时访问共享资源,可能会导致数据不一致或者竞态条件等问题

👳我亲爱的各位大佬们好😘😘😘
♨️本篇文章记录的为 线程同步模式之保护性暂停 相关内容,适合在学Java的小白,帮助新手快速上手,也适合复习中,面试中的大佬🙉🙉🙉。
♨️如果文章有什么需要改进的地方还请大佬不吝赐教❤️🧡💛
👨‍🔧 个人主页 : 阿千弟

何为保护性暂停

保护性暂停是一种同步模式,用于保护共享资源的完整性。在多线程或多进程环境中,如果多个线程或进程同时访问共享资源,可能会导致数据不一致或者竞态条件等问题。为了避免这种情况,可以使用保护性暂停来保护共享资源。

实现方式:

在访问共享资源之前,先获取一个锁,然后再访问共享资源。如果锁已经被其他线程或进程占用,则当前线程或进程会被阻塞,直到锁被释放为止。在访问完共享资源之后,需要释放锁,以便其他线程或进程可以继续访问共享资源。

作用:

保护性暂停可以保证共享资源的完整性,但是也会带来一定的性能开销。因为每个线程或进程在访问共享资源之前都需要获取锁,如果锁的竞争比较激烈,就会导致线程或进程的等待时间增加,从而影响系统的性能。因此,在使用保护性暂停时,需要权衡保证数据完整性和系统性能之间的关系,选择合适的锁机制和并发控制策略。

  1. 具体定义:

Guarded Suspension(被监视的挂起),用在一个线程等待另一个线程的执行结果
在这里插入图片描述

要点

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

代码实现

GuardedObject

@Slf4j
public 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;
    }

    //接受结果的等待时间为mills, 如果该期间仍然没有接受到返回结果, 则
    public Object get(long mills){
   
   
        // 1) 记录最初时间
        long beginTime = System.currentTimeMillis();
        // 2) 已经经历的时间
        long passTime = 0;
        synchronized (lock){
   
   
            while (response==null){
   
   
                // 4) 假设 millis 是 1000,结果在 400 时唤醒了,那么还有 600 要等
                long waitTime = mills - passTime;
                if (waitTime <= 0){
   
   
                    break;
                }
                try {
   
   
                    lock.wait(waitTime);
                } catch (InterruptedException e) {
   
   
                    e.printStackTrace();
                }
                // 3) 如果提前被唤醒,这时已经经历的时间假设为 400
                passTime = System.currentTimeMillis() - beginTime;
                log.debug("timePassed: {}, object is null {}",passTime, response == null);
            }
        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 = Downloader.download();
                log.info("下载完成");
                guardedObject.complete(response);
            } catch (IOException e) {
   
   
                e.printStackTrace();
            }
        }).start();

        log.info("正在下载");

        Object response = guardedObject.get(500);
        if(response!=null){
   
   
            log.debug("get response: [{}] lines", ((List<String>) response).size());
        } else {
   
   
            log.debug("can't get response");
        }

    }
}

下载案例demo

public class Downloader {
   
   
    public static List<String> download() throws IOException {
   
   
        HttpURLConnection conn = (HttpURLConnection) new URL("https://www.baidu.com/").openConnection();
        List<String> lines = new ArrayList<>();
        try (BufferedReader reader =
                     new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
   
   
            String line;
            while ((line = reader.readLine()) != null) {
   
   
                lines.add(line);
            }
        }
        return lines;
    }
}

测试,超时

17:41:21.194 [main] INFO com.jrm.juc.GuardedObject - 正在下载
17:41:21.700 [main] DEBUG com.jrm.juc.GuardedObject - timePassed: 503, object is null true
17:41:21.701 [main] DEBUG com.jrm.juc.GuardedObject - can't get response
17:41:25.560 [Thread-0] INFO com.jrm.juc.GuardedObject - 下载完成

Process finished with exit code 0

如果这篇【文章】有帮助到你💖,希望可以给我点个赞👍,创作不易,如果有对Java后端或者对spring, 分布式, 云原生感兴趣的朋友,请多多关注💖💖💖
👨‍🔧 个人主页 : 阿千弟

目录
相关文章
|
6月前
|
存储 缓存 Java
9.队列:生产消费模式及线程池的运用
9.队列:生产消费模式及线程池的运用
57 0
|
6月前
|
数据处理
多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)
多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)
64 1
|
3月前
|
NoSQL Redis
Lettuce的特性和内部实现问题之在同步调用模式下,业务线程是如何拿到结果数据的
Lettuce的特性和内部实现问题之在同步调用模式下,业务线程是如何拿到结果数据的
|
3月前
|
NoSQL 关系型数据库 MySQL
简述redis的单线程模式
简述redis的单线程模式
|
4月前
|
Prometheus 监控 数据可视化
通用快照方案问题之Hystrix进行指标监控如何解决
通用快照方案问题之Hystrix进行指标监控如何解决
43 0
|
4月前
|
设计模式 安全 NoSQL
Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
61 0
|
4月前
|
存储 设计模式 监控
Java面试题:如何在不牺牲性能的前提下,实现一个线程安全的单例模式?如何在生产者-消费者模式中平衡生产和消费的速度?Java内存模型规定了变量在内存中的存储和线程间的交互规则
Java面试题:如何在不牺牲性能的前提下,实现一个线程安全的单例模式?如何在生产者-消费者模式中平衡生产和消费的速度?Java内存模型规定了变量在内存中的存储和线程间的交互规则
45 0
|
6月前
|
缓存 NoSQL 中间件
【后端面经】【缓存】36|Redis 单线程:为什么 Redis 用单线程而 Memcached 用多线程?epoll、poll和select + Reactor模式
【5月更文挑战第19天】`epoll`、`poll`和`select`是Linux下多路复用IO的三种方式。`select`需要主动调用检查文件描述符,而`epoll`能实现回调,即使不调用`epoll_wait`也能处理就绪事件。`poll`与`select`类似,但支持更多文件描述符。面试时,重点讲解`epoll`的高效性和`Reactor`模式,该模式包括一个分发器和多个处理器,用于处理连接和读写事件。Redis采用单线程模型结合`epoll`的Reactor模式,确保高性能。在Redis 6.0后引入多线程,但基本原理保持不变。
63 2
|
6月前
|
前端开发 网络协议 JavaScript
如何在前端实现WebSocket发送和接收TCP消息(多线程模式)
请确保在你的服务器端实现WebSocket的处理,以便它可以接受和响应前端发送的消息。同时,考虑处理错误情况和关闭连接的情况以提高可靠性。
448 0
|
缓存 编译器 调度
[笔记]Windows核心编程《七》用户模式下的线程同步
[笔记]Windows核心编程《七》用户模式下的线程同步

相关实验场景

更多