Java并发编程中的设计模式解析(一)

简介: Java并发编程,除了被用于各种Web应用、分布式系统和大数据系统,构成高并发系统的核心基础外,其本身也蕴含着大量的设计模式思想在里面。这一系列文章主要是结合Java源码,对并发编程中使用到的、实现的各类设计模式做归纳总结,以便进一步沉淀对Java并发设计的理解。

Java并发编程,除了被用于各种Web应用、分布式系统和大数据系统,构成高并发系统的核心基础外,其本身也蕴含着大量的设计模式思想在里面。这一系列文章主要是结合Java源码,对并发编程中使用到的、实现的各类设计模式做归纳总结,以便进一步沉淀对Java并发设计的理解。

模板设计模式

Thread类中run和start方法,就是一个典型的模板设计模式的实现,即:父类定义算法逻辑代码,子类实现其细节

 1 public synchronized void start() {
 2         /**
 3          * 线程对象新建后的New状态,其内部thereadStatus属性为0
 4          */
 5         if (threadStatus != 0)
 6             throw new IllegalThreadStateException();
 7 
 8         /* 同时会被添加到一个ThreadGroup */
 9         group.add(this);
10 
11         boolean started = false;
12         //调用JNI方法start0()来启动线程
13         try {
14             start0();
15             started = true;
16         } finally {
17         //线程结束之后,再次启动将抛出异常
18             try {
19                 if (!started) {
20                     group.threadStartFailed(this);
21                 }
22             } catch (Throwable ignore) {
23                 /* do nothing. If start0 threw a Throwable then
24                   it will be passed up the call stack */
25             }
26         }
27     }

下面以一个例子演示模板模式:

public class TemplateMethod {
    //相当于Thread类的start方法, 用final修饰避免被更改
    public final void print(String message) {
        System.out.println("-------------");
        wrapPrint(message);
        System.out.println("-------------");
    }
    //相当于Thread的run方法, 用protected修饰限于子类重写
    protected void wrapPrint(String message) {
        
    }
    
    public static void main(String[] args) {
        //通过匿名内部子类, 重写父类的wrapPrint方法, 从而实现不同的输出模板
        TemplateMethod t1 = new TemplateMethod() {
            @Override
            protected void wrapPrint(String message) {
                System.out.println("111" + message + "111");
            }
        };
        t1.print("Hello World!");
        
        TemplateMethod t2 = new TemplateMethod() {
            @Override
            protected void wrapPrint(String message) {
                System.out.println("222" + message + "222");
            }
        };
        t2.print("Hello World!");
    }
}

策略模式

创建Java多线程中,实现Runnable接口作为Target并传入Thread类的构造方法来生成线程对象的过程,就体现了GoF中策略模式的设计思想。下面是一个简单的示例:

首先,仿照Runnable接口的思想,定义一个用于处理数据库行的接口

1 /*
2  * RowHandler定义了对数据库查询返回结果操作的方法, 具体实现需要
3  * 实现类完成, 类似于Runnable接口
4  */
5 public interface RowHandler<T> {
6     T handle(ResultSet rs);
7 }

然后,仿照Thread方法,定义数据库查询的工作类

 1 public class RecordQuery  {
 2     
 3     private final Connection connection;
 4 
 5     public RecordQuery(Connection connection) {
 6         this.connection = connection;
 7     }
 8     //方法中传入RowHandler的实现类
 9     public <T> T query(RowHandler<T> handler, String sql, Object... params) throws SQLException {
10             PreparedStatement stmt;
11             ResultSet resultSet;
12             stmt = connection.prepareStatement(sql);
13             int index = 1;
14             for (Object param : params) {
15                 stmt.setObject(index++, param);
16             }
17             resultSet = stmt.executeQuery();
18             //调用实现类的handle方法来处理数据
19             return handler.handle(resultSet);
20     }
21 }

生产者-消费者模式

生产者-消费者模式是使用Java并发编程通信所实现的经典模式之一。该模式是通过队列这一数据结构来存储对象元素,由多线程分别充当生产者和消费者,生产者不断生成元素、消费者不断消费元素的过程。下面通过代码来演示:

实现一个带有入队和出队的队列

 1 /*
 2  * 通过一个生产者-消费者队列来说明线程通信的基本使用方法
 3  */
 4 public class EventQueue {
 5     //定义一个队列元素数量, 一旦赋值则不可更改
 6     private final int max;
 7     //定义一个空的内部类, 代表存储元素
 8     static class Event{        }
 9     //定义一个不可改的链表集合, 作为队列载体
10     private final LinkedList<Event> eventQueue = new LinkedList<>();
11     //如果不指定初始容量, 则容量默认为10
12     private final static int DEFAULT_MAX_EVENT = 10;
13     //使用自定义容量初始化队列
14     public EventQueue(int max) {
15         this.max = max;
16     }
17     //如果不指定初始容量, 则容量默认为10
18     public EventQueue() {
19         this(DEFAULT_MAX_EVENT);
20     }
21     //封装一个输出到控制台的方法
22     private void console(String message) {
23         System.out.printf("%s:%s\n",Thread.currentThread().getName(), message);
24     }
25     //定义入队方法
26     public void offer(Event event) {
27         //使用链表对象作为锁, 通过synchronized代码块实现同步
28         synchronized(eventQueue) {
29             //在循环中判断如果队列已满, 则调用锁的wait方法, 使生产者线程阻塞
30             while(eventQueue.size() >= max) {
31                 try {
32                     console(" the queue is full");
33                     eventQueue.wait();
34                 } catch (InterruptedException e) {
35                     e.printStackTrace();
36                 }
37             }
38             console(" the new event is submitted");
39             eventQueue.addLast(event);
40             //唤醒所有等待中的消费者;注意如果此处使用notify(),可能导致线程不安全
41             this.eventQueue.notifyAll();
42         }
43     }
44     //定义出队方法
45     public Event take() {
46         //使用链表对象作为锁
47         synchronized(eventQueue) {
48             //在循环中判断如果队列已空, 则调用锁的wait方法, 使消费者线程阻塞
49             while(eventQueue.isEmpty()) {
50                 try {
51                     console(" the queue is empty.");
52                     eventQueue.wait();
53                 } catch (InterruptedException e) {
54                     e.printStackTrace();
55                 }
56             }
57             Event event = eventQueue.removeFirst();
58             //唤醒所有等待中的生产者;注意如果此处使用notify(),可能导致线程不安全
59             this.eventQueue.notifyAll();
60             console(" the event " + event + " is handled/taked.");
61             return event;
62         }
63     }
64 }

验证该队列的类

 1 /*
 2  * producer/client pattern
 3  */
 4 public class EventClient {
 5     
 6     public static void main(String[] args) {
 7         //定义不可变队列实例
 8         final EventQueue eventQueue = new EventQueue();
 9         //新建生产者线程, 可以设置多个
10         new Thread(()->{
11             while(true) {
12                 eventQueue.offer(new EventQueue.Event());
13             }
14         }, "producer").start();
15         //新建消费者线程, 可以设置多个
16         new Thread(()->{
17             while(true) {
18                 eventQueue.take();
19                 try {
20                     TimeUnit.MILLISECONDS.sleep(10);
21                 } catch (InterruptedException e) {
22                     e.printStackTrace();
23                 }
24             }
25         }, "consumer").start();
26     }
27 }
目录
相关文章
|
5月前
|
设计模式 缓存 安全
【高薪程序员必看】万字长文拆解Java并发编程!(8):设计模式-享元模式设计指南
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的经典对象复用设计模式-享元模式,废话不多说让我们直接开始。
115 0
|
5月前
|
设计模式 缓存 监控
并发设计模式实战系列(14):CAS(无锁编程)
🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第十四章,废话不多说直接开始~
61 0
|
7月前
|
设计模式 SQL Java
【再谈设计模式】解释器模式~语法的解析执行者
解释器模式定义了一种语言的语法表示,并定义一个解释器来解释该语言中的句子。它使用类来表示每个语法规则,并且通过递归调用这些类的方法来解释表达式。本质上,它将一个复杂的表达式分解为一系列简单的部分,然后按照特定的语法规则进行解析和执行。
151 8
|
7月前
|
设计模式 机器学习/深度学习 前端开发
Python 高级编程与实战:深入理解设计模式与软件架构
本文深入探讨了Python中的设计模式与软件架构,涵盖单例、工厂、观察者模式及MVC、微服务架构,并通过实战项目如插件系统和Web应用帮助读者掌握这些技术。文章提供了代码示例,便于理解和实践。最后推荐了进一步学习的资源,助力提升Python编程技能。
|
10月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
10月前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
10月前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
10月前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
5天前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
39 1
|
5天前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
36 1

热门文章

最新文章

推荐镜像

更多
  • DNS