• 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
    ",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 }
  • 相关阅读:
    java 13-6 Char的包装类Character
    java13-5 JDK1.5以后的一个新特性和Integer的面试题
    java 13-4 Integer和String、int之间的转换,进制转换
    java 13-3 int类型的包装包Integer
    java 13-2 Arrays工具类
    java 13-1 数组高级二分查找
    java12
    kafka语句示例
    zookeeper安装
    redhat java配置
  • 原文地址:https://www.cnblogs.com/leoliu168/p/9919715.html
Copyright © 2020-2023  润新知