单例模式
单例是最常见的一种设计模式, 一般用于全局对象管理, 比如xml配置读写之类的.
一般分为懒汉式, 饿汉式.
懒汉式: 方法上加synchronized

这种方式, 由于每次获取示例都要获取锁, 不推荐使用, 性能较差
懒汉式: 使用双检锁 + volatile

本方式是对直接在方法上加锁的一个优化, 好处在于只有第一次初始化获取了锁.
后续调用getInstance已经是无锁状态. 只是写法上稍微繁琐点.
懒汉式: 使用静态内部类

该方式既解决了同步问题, 也解决了写法繁琐问题. 推荐使用改写法.
缺点在于无法响应事件来重新初始化INSTANCE.
饿汉式

缺点在于对象在一开始就直接初始化了.
Future模式
该模式的核心思想是异步调用. 有点类似于异步的ajax请求.
当调用某个方法时, 可能该方法耗时较久, 而在主函数中也不急于立刻获取结果.
因此可以让调用者立刻返回一个凭证, 该方法放到另外线程执行,
后续主函数拿凭证再去获取方法的执行结果即可, 其结构图如下

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

通过FutureTask实现
注意其中两个耗时操作.
如果doOtherThing耗时2s, 则整个函数耗时2s左右.
如果doOtherThing耗时0.2s, 则整个函数耗时取决于RealData.costTime, 即1s左右结束.
- public class FutureDemo1 {
- public static void main(String[] args) throws InterruptedException, ExecutionException {
- FutureTask future = new FutureTask(new Callable() {
- @Override
- public String call() throws Exception {
- return new RealData().costTime();
- }
- });
- ExecutorService service = Executors.newCachedThreadPool();
- service.submit(future);
- System.out.println("RealData方法调用完毕");
- // 模拟主函数中其他耗时操作
- doOtherThing();
- // 获取RealData方法的结果
- System.out.println(future.get());
- }
- private static void doOtherThing() throws InterruptedException {
- Thread.sleep(2000L);
- }
- }
- class RealData {
- public String costTime() {
- try {
- // 模拟RealData耗时操作
- Thread.sleep(1000L);
- return "result";
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return "exception";
- }
- }
通过Future实现
与上述FutureTask不同的是, RealData需要实现Callable接口.
- public class FutureDemo2 {
- public static void main(String[] args) throws InterruptedException, ExecutionException {
- ExecutorService service = Executors.newCachedThreadPool();
- Future future = service.submit(new RealData2());
- System.out.println("RealData2方法调用完毕");
- // 模拟主函数中其他耗时操作
- doOtherThing();
- // 获取RealData2方法的结果
- System.out.println(future.get());
- }
- private static void doOtherThing() throws InterruptedException {
- Thread.sleep(2000L);
- }
- }
- class RealData2 implements Callable{
- public String costTime() {
- try {
- // 模拟RealData耗时操作
- Thread.sleep(1000L);
- return "result";
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return "exception";
- }
- @Override
- public String call() throws Exception {
- return costTime();
- }
- }
另外Future本身还提供了一些额外的简单控制功能, 其API如下
- // 取消任务
- boolean cancel(boolean mayInterruptIfRunning);
- // 是否已经取消
- boolean isCancelled();
- // 是否已经完成
- boolean isDone();
- // 取得返回对象
- V get() throws InterruptedException, ExecutionException;
- // 取得返回对象, 并可以设置超时时间
- V get(long timeout, TimeUnit unit)
- 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.
原文链接