Java 并发设计模式(二)

简介: Thread Local Storage 表示线程本地存储模式。 大多数并发问题都是由于变量的共享导致的,多个线程同时读写同一变量便会出现原子性,可见性等问题。局部变量是线程安全的,本质上也是由于各个线程各自拥有自己的变量,避免了变量的共享。

三、Guarded Suspension 模式



1. Guarded Suspension 实现

Guarded Suspension 意为保护性暂停。一个典型的使用场景是:当客户端线程 T 发送请求后,服务端这时有大量的请求需要处理,这时候就需要排队,线程 T 进入等待状态,直到服务端处理完请求并且返回结果。

Guarded Suspension 的实现很简单,有一个对象 GuardedObject,其内部有一个属性,即被保护的对象,还有两个方法,客户端调用 get() 方法,如果未获取到结果,则进入等待状态,即“保护性暂停”;还有一个 notice() 通知方法,当服务端处理完请求后,调用这个方法,并且唤醒等待中的线程。示意图如下:

示例代码如下:

public class GuardedObject<T> {
    private T obj;
    private final Lock lock = new ReentrantLock();
    private final Condition finished = lock.newCondition();
    //调用方线程获取结果
    T get(){
        lock.lock();
        try {
            while (未获取到结果){
                finished.await();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        finally {
            lock.unlock();
        }
        return obj;
    }
    //执行完后通知
    void notice(T obj){
        lock.lock();
        try {
            this.obj = obj;
            finished.signalAll();
        }
        finally {
            lock.unlock();
        }
    }
}

从代码中可以看到,Guarded Suspension 模式本质上就是一种等待-通知机制,只不过使用这种模式,在解决实际的问题的时候,需要根据情况进行程序功能的扩展。


2. 使用示例

还是上面提到的那个例子,当客户端发送请求后,需要等待服务端的响应结果,这时候就可以使用 Guarded Suspension 来实现,下面是代码示例:

public class SendRequest<T> {
    //相当于消息队列
    private final BlockingQueue<Request> queue = new ArrayBlockingQueue<>(5);
    //客户端发送请求
    void send(Request request) throws InterruptedException {
        //将消息存放至队列中
        queue.put(request);
        //创建Guarded Suspension模式的对象
        GuardedObject<Request> guardedObject = GuardedObject.create(request.id);
        //循环等待,获取结果
        Request res = guardedObject.get(Objects::nonNull);
    }
    //服务端处理请求
    void handle() throws InterruptedException {
        //从队列中获取请求
        Request request = queue.take();
        //调用请求对应的GuardedObject,并处理请求
        GuardedObject.handleRequest(request.id, request);
    }
    //请求类
    private static class Request{
        private int id;
        private String content;
    }
}
需要注意的是,这里并不是直接使用 new GuardedObject() 的方式来创建对象,这是因为需要找到每个请求和对象之间的对应关系,所以 GuardedObject 内部使用了一个 map 来保存对象,key 是对应的请求 id。
GuardedObject 类代码如下:
public class GuardedObject<T> {
    private T obj;
    private final Lock lock = new ReentrantLock();
    private final Condition finished = lock.newCondition();
    private static final ConcurrentHashMap<Integer, GuardedObject> map = new ConcurrentHashMap<>();
    //创建对象
    public static GuardedObject create(int id){
        GuardedObject guardedObject = new GuardedObject();
        //保存对象和请求的对应关系
        map.put(id, guardedObject);
        return guardedObject;
    }
    //处理请求
    public static void handleRequest(int id, Object obj){
        GuardedObject guardedObject = map.remove(id);
        if (guardedObject != null){
            //具体的处理逻辑省略
            //处理完后通知
            guardedObject.notice(obj);
        }
    }
    //调用方线程获取结果
    T get(Predicate<T> p){
        lock.lock();
        try {
            while (!p.test(obj)){
                finished.await();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        finally {
            lock.unlock();
        }
        return obj;
    }
    //执行完后通知
    void notice(T obj){
        lock.lock();
        try {
            this.obj = obj;
            finished.signalAll();
        }
        finally {
            lock.unlock();
        }
    }
}

四、Balking 模式


Balking 模式的典型应用场景是,业务逻辑依赖于某个条件变量的状态,因此这种模式又可以理解为多线程版本的 if。

public class BalkingTest {
    private boolean flag = false;
    public void execute(){
        if (!flag){
            return;
        }
        //具体的执行操作省略
        flag = false;
    }
    public void test(){
        //省略业务代码若干
        flag = true;
    }
}

例如上面这个例子,一段业务逻辑会改变 flag 的值,另一个方法会根据 flag 的值来决定是否继续执行。

这个程序并不是线程安全的,解决的办法也很简单,就是加互斥锁,然后可以将改变 flag 值的逻辑单独拿出来,如下:

public class BalkingTest {
    private boolean flag = false;
    public synchronized void execute(){
        if (!flag){
            return;
        }
        //具体的执行操作省略
        flag = false;
    }
    public void test(){
        //省略业务代码若干
        change();
    }
    public synchronized void change(){
        flag = true;
    }
}

Balking 模式一般可以使用互斥锁来实现,并且可以将对条件变量的改变的逻辑和业务逻辑进行分离,这样能够减小锁的粒度,提升性能。Balking 模式大多应用于需要快速失败的场景,即当条件变量不满足,则直接失败。这也是它和 Guarded Suspension 模式的区别,因为 Guarded Suspension 模式在条件不满足的时候,会一直等待条件满足。


相关文章
|
11天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
16天前
|
Java 数据库连接 数据库
如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面
本文介绍了如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面。通过合理配置初始连接数、最大连接数和空闲连接超时时间,确保系统性能和稳定性。文章还探讨了同步阻塞、异步回调和信号量等并发控制策略,并提供了异常处理的最佳实践。最后,给出了一个简单的连接池示例代码,并推荐使用成熟的连接池框架(如HikariCP、C3P0)以简化开发。
34 2
|
22天前
|
设计模式 Java 程序员
[Java]23种设计模式
本文介绍了设计模式的概念及其七大原则,强调了设计模式在提高代码重用性、可读性、可扩展性和可靠性方面的作用。文章还简要概述了23种设计模式,并提供了进一步学习的资源链接。
36 0
[Java]23种设计模式
|
6天前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
1月前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
26 1
|
1月前
|
设计模式 监控 算法
Java设计模式梳理:行为型模式(策略,观察者等)
本文详细介绍了Java设计模式中的行为型模式,包括策略模式、观察者模式、责任链模式、模板方法模式和状态模式。通过具体示例代码,深入浅出地讲解了每种模式的应用场景与实现方式。例如,策略模式通过定义一系列算法让客户端在运行时选择所需算法;观察者模式则让多个观察者对象同时监听某一个主题对象,实现松耦合的消息传递机制。此外,还探讨了这些模式与实际开发中的联系,帮助读者更好地理解和应用设计模式,提升代码质量。
Java设计模式梳理:行为型模式(策略,观察者等)
|
2月前
|
存储 设计模式 安全
Java设计模式-备忘录模式(23)
Java设计模式-备忘录模式(23)
|
2月前
|
设计模式 存储 算法
Java设计模式-命令模式(16)
Java设计模式-命令模式(16)
|
2月前
|
设计模式 存储 缓存
Java设计模式 - 解释器模式(24)
Java设计模式 - 解释器模式(24)
|
2月前
|
设计模式 安全 Java
Java设计模式-迭代器模式(21)
Java设计模式-迭代器模式(21)