JAVA高并发六 并发设计模式

单例模式

       单例是最常见的一种设计模式, 一般用于全局对象管理, 比如xml配置读写之类的.

       一般分为懒汉式, 饿汉式.

       懒汉式: 方法上加synchronized

       

      这种方式, 由于每次获取示例都要获取锁, 不推荐使用, 性能较差

      懒汉式: 使用双检锁 + volatile

      

        本方式是对直接在方法上加锁的一个优化, 好处在于只有第一次初始化获取了锁.

后续调用getInstance已经是无锁状态. 只是写法上稍微繁琐点.

        懒汉式: 使用静态内部类

        

        该方式既解决了同步问题, 也解决了写法繁琐问题. 推荐使用改写法.

        缺点在于无法响应事件来重新初始化INSTANCE.

        饿汉式

        

       缺点在于对象在一开始就直接初始化了.

Future模式

        该模式的核心思想是异步调用. 有点类似于异步的ajax请求.

        当调用某个方法时, 可能该方法耗时较久, 而在主函数中也不急于立刻获取结果.

        因此可以让调用者立刻返回一个凭证, 该方法放到另外线程执行,

        后续主函数拿凭证再去获取方法的执行结果即可, 其结构图如下

      

       jdk中内置了Future模式的支持, 其接口如下:

      

       通过FutureTask实现

       注意其中两个耗时操作.

              如果doOtherThing耗时2s, 则整个函数耗时2s左右.

              如果doOtherThing耗时0.2s, 则整个函数耗时取决于RealData.costTime, 即1s左右结束.

  1. public class FutureDemo1 {
  2.     public static void main(String[] args) throws InterruptedException, ExecutionException {
  3.         FutureTask<String> future = new FutureTask<String>(new Callable<String>() {
  4.             @Override
  5.             public String call() throws Exception {
  6.                 return new RealData().costTime();
  7.             }
  8.         });
  9.         ExecutorService service = Executors.newCachedThreadPool();
  10.         service.submit(future);
  11.         System.out.println("RealData方法调用完毕");
  12.         // 模拟主函数中其他耗时操作
  13.         doOtherThing();
  14.         // 获取RealData方法的结果
  15.         System.out.println(future.get());
  16.     }
  17.     private static void doOtherThing() throws InterruptedException {
  18.         Thread.sleep(2000L);
  19.     }
  20. }
  21. class RealData {
  22.     public String costTime() {
  23.         try {
  24.             // 模拟RealData耗时操作
  25.             Thread.sleep(1000L);
  26.             return "result";
  27.         } catch (InterruptedException e) {
  28.             e.printStackTrace();
  29.         }
  30.         return "exception";
  31.     }
  32. }

        通过Future实现

        与上述FutureTask不同的是, RealData需要实现Callable接口.

  1. public class FutureDemo2 {
  2.     public static void main(String[] args) throws InterruptedException, ExecutionException {
  3.         ExecutorService service = Executors.newCachedThreadPool();
  4.         Future<String> future = service.submit(new RealData2());
  5.         System.out.println("RealData2方法调用完毕");
  6.         // 模拟主函数中其他耗时操作
  7.         doOtherThing();
  8.         // 获取RealData2方法的结果
  9.         System.out.println(future.get());
  10.     }
  11.     private static void doOtherThing() throws InterruptedException {
  12.         Thread.sleep(2000L);
  13.     }
  14. }
  15. class RealData2 implements Callable<String>{
  16.     public String costTime() {
  17.         try {
  18.             // 模拟RealData耗时操作
  19.             Thread.sleep(1000L);
  20.             return "result";
  21.         } catch (InterruptedException e) {
  22.             e.printStackTrace();
  23.         }
  24.         return "exception";
  25.     }
  26.     @Override
  27.     public String call() throws Exception {
  28.         return costTime();
  29.     }
  30. }

         另外Future本身还提供了一些额外的简单控制功能, 其API如下

  1. // 取消任务
  2.     boolean cancel(boolean mayInterruptIfRunning);
  3.     // 是否已经取消
  4.     boolean isCancelled();
  5.     // 是否已经完成
  6.     boolean isDone();
  7.     // 取得返回对象
  8.     V get() throws InterruptedException, ExecutionException;
  9.     // 取得返回对象, 并可以设置超时时间
  10.     V get(long timeout, TimeUnit unit)
  11.             throws InterruptedException, ExecutionException, TimeoutException;

生产者消费者模式

       生产者-消费者模式是一个经典的多线程设计模式. 它为多线程间的协作提供了良好的解决方案。

       在生产者-消费者模式中,通常由两类线程,即若干个生产者线程和若干个消费者线程。

       生产者线程负责提交用户请求,消费者线程则负责具体处理生产者提交的任务。

       生产者和消费者之间则通过共享内存缓冲区进行通信, 其结构图如下

          

        PCData为我们需要处理的元数据模型, 生产者构建PCData, 并放入缓冲队列.

       消费者从缓冲队列中获取数据, 并执行计算.

生产者核心代码

复制代码

 1         while(isRunning) {
 2             Thread.sleep(r.nextInt(SLEEP_TIME));
 3             data = new PCData(count.incrementAndGet);
 4             // 构造任务数据
 5             System.out.println(data + " is put into queue");
 6             if (!queue.offer(data, 2, TimeUnit.SECONDS)) {
 7                 // 将数据放入队列缓冲区中
 8                 System.out.println("faild to put data : " + data);
 9             }
10         }

复制代码

消费者核心代码

复制代码

 1         while (true) {
 2             PCData data = queue.take();
 3             // 提取任务
 4             if (data != null) {
 5                 // 获取数据, 执行计算操作
 6                 int re = data.getData() * 10;
 7                 System.out.println("after cal, value is : " + re);
 8                 Thread.sleep(r.nextInt(SLEEP_TIME));
 9             }
10         }

复制代码

        生产消费者模式可以有效对数据解耦, 优化系统结构.

        降低生产者和消费者线程相互之间的依赖与性能要求.

        一般使用BlockingQueue作为数据缓冲队列, 他是通过锁和阻塞来实现数据之间的同步, 

        如果对缓冲队列有性能要求, 则可以使用基于CAS无锁设计的ConcurrentLinkedQueue.

原文链接

猜你喜欢

转载自blog.csdn.net/qq_34479912/article/details/81708050