单例模式的好处:java
简单的单例:设计模式
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }
上面代码须要注意几点:缓存
缺点:Singleton实例在什么时候建立不受控制,instance对象会在第一次初始化时被建立安全
好比,在Singleton中加入一个属性:数据结构
public class Singleton { public static int STATUS = 1; //...和上面代码一致 }
那么,只要调用Singleton.STATUS就会实例化Singleton。而不用等到调用getInstance()方法。多线程
延迟加载的单例:并发
特色:只会在instance第一次使用时建立对象。框架
缺点:使用了synchronized锁,并发坏境下对性能产生必定影响。dom
/** * 延迟的单例 */ public class LazySingleton { private LazySingleton(){ } private static LazySingleton instance = null; public static synchronized LazySingleton getInstance(){ if (instance == null) instance = new LazySingleton(); return instance; } }
使用内部类实现的单例:异步
优势:在真正须要时建立对象;性能优越。
public class StaticSingleton { private StaticSingleton(){ System.out.println("StaticSingleton is created!"); } private static class SingletonHolder{ private static StaticSingleton instance = new StaticSingleton(); } public static StaticSingleton getInstance(){ return SingletonHolder.instance; } }
什么是不变模式:经过使用一种不可改变的对象,依靠对象的不变性,达到在没有同步操做的多线程环境下依然保持内部状态的一致性和正确性。这就是不变模式。
核心思想:天生对多线程友好,对象一旦被建立,它的内部状态将永远不会发生改变。别的线程不能改变,它本身也不会改变本身。这和只读属性有点区别,只读属性能够本身改变本身。
应用场景知足两个条件:
如何实现?须要注意4点:
public final class Product { //一下全部final定义的对象都只赋值一次,随后不会改变 private final String no; private final String name; private final double price; public Product(String no, String name, double price){ this.no = no; this.name = name; this.price = price; } public String getNo() { return no; } public String getName() { return name; } public double getPrice() { return price; } }
属性中的final确保全部数据都只赋值一次,定义class为final是不但愿被继承。以避免子类破坏该类的不变模式。
JDK中的例子:最典型的是String类,全部元数据的包装类都是不变模式。
注意:不变模式是经过回避问题而不是解决问题来处理多线程并发访问控制的,不变对象不须要同步操做。并发中性能上会提升。
介绍:生产者-消费者模式是一个典型的多线程设计模式,它为多线程间的协做提供了良好的解决方案。在这个模式中,通产有两类线程,若干个生产者线程和若干个消费者线程,还有一个共享内存缓冲区。生产者线程负责提交用户请求,消费者线程则负责具体处理生产者提交的任务。生产者和消费者之间经过共享内存缓冲区进行通讯。
基本结构:
特色:生产者和消费者解耦,二者都不须要知道对方的存在。容许消费者和生产者之间存在时间差。
具体结构:
[生-消_2019-09-27_15-09-37](D:\user\80004133\Pictures\生-消_[](https://img2018.cnblogs.com/blog/1541399/201911/1541399-20191129145700779-1544050544.png
例子:实现一个基于生产者-消费者求整数平方的并行程序
生产者:它构建PCData对象,并放入BlockingQueue队列中。
public class Producer implements Runnable { private volatile boolean isRunning = true; private BlockingDeque<PCData> queue; //内存缓冲区,经过构造时外部引入,保证和消费者用的是一样的内存缓冲区. private static AtomicInteger count = new AtomicInteger(); //总数,原子操做. private static final int SLEEPTIME = 1000; public Producer(BlockingDeque<PCData> queue) { this.queue = queue; } @Override public void run() { PCData data = null; Random random = new Random(); System.out.println("start producter .." + Thread.currentThread().getId()); try { while (isRunning) { Thread.sleep(random.nextInt(SLEEPTIME)); data = new PCData(count.incrementAndGet()); System.out.println(data + " is put into Queue"); if (!queue.offer(data, 2, TimeUnit.SECONDS)) { //插入一个数据到缓冲区 System.out.println("failed to put data " + data); } } } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } } public void stop() { isRunning = false; } }
消费者:从BlockingQueue中取得PCData对象,并进行相应计算。
public class Consumer implements Runnable { private BlockingDeque<PCData> queue; //共享缓冲区 private static final int SLEEPTIME = 1000; public Consumer(BlockingDeque<PCData> queue) { this.queue = queue; } @Override public void run() { System.out.println("start Consumer id : " + Thread.currentThread().getId()); Random r = new Random(); try { while (true) { PCData data = queue.take(); //从共享缓冲区中拿到一个数据消费 if (null != data) { int re = data.getIntData() * data.getIntData(); System.out.println(MessageFormat.format("{0} * {0} = {1}", data.getIntData(), re)); Thread.sleep(r.nextInt(SLEEPTIME)); } } } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } } }
共享数据模型:PCData
public class PCData { private final int intData; public PCData(int d) { intData = d; } public PCData(String d) { intData = Integer.parseInt(d); } public int getIntData() { return intData; } @Override public String toString() { return "PCData{" + "intData=" + intData + '}'; } }
主函数:建立一个生产者和消费者共用的共享缓冲区,建立生产者和消费者线程,并提交到线程池。
class Main { public static void main(String[] a) throws InterruptedException { BlockingDeque<PCData> queue = new LinkedBlockingDeque<>(10); Producer producter1 = new Producer(queue); Producer producter2 = new Producer(queue); Producer producter3 = new Producer(queue); Consumer consumer1 = new Consumer(queue); Consumer consumer2 = new Consumer(queue); Consumer consumer3 = new Consumer(queue); ExecutorService es = Executors.newCachedThreadPool(); es.execute(producter1); es.execute(producter2); es.execute(producter3); es.execute(consumer1); es.execute(consumer2); es.execute(consumer3); //运行时间 Thread.sleep(1000 * 10); //中止生产者 producter1.stop(); producter2.stop(); producter3.stop(); //中止生产者后,预留时间给消费者执行 Thread.sleep(1000 * 5); es.shutdown(); } }
现成的Disruptor框架是一个无锁的缓存框架。
介绍:Disruptor框架使用无锁的方式实现了一个环形队列,很是适用于生产者-消费者模式,好比事件和消息发布。
消费者如何监控缓冲区中的信息呢?,Disruptor提供了几种策略:
什么是伪共享问题?
为了提升CPU速度,CPU有一个高速缓存Cache。在高速缓存中,读写数据的最小单位为缓存行,它是从主存(memory)复制到缓存(Cache)的最小单位。
若是两个变量存放在一个缓存行时,在多线程访问中,可能会相互影响彼此的性能。如图,当cpu1的x被修改,cpu2上的缓存行就会变成无效,致使Cache没法命中。若是CPU不能命中缓存,系统的吞吐量就会降低。
解决办法:
让x变量一行,行的空位置让padding填充。这样,就能保证x被修改时,cpu2的缓存不会失效。