设计模式
设计模式
单例模式
饿汉式
是线程安全的
private static Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; }
静态 一开始便装载
懒汉式
线程不安全,多线程下可能会出现多个实例
private static Singleton instance = null; public static Singleton getInstance() { if(instance == null) { instance = new Singleton(); } return instance; }
懒汉-Synchronized版
线程安全但效率低下,全部代码被同步
private static Singleton instance = null; public static Singleton getInstance() { synchronized(Singleton.class) { if(instance == null) { instance = new Singleton(); } } return instance; }
懒汉-Synchronized优化版
**线程不安全!**一个线程在执行初始化,但另一个线程判断instance为空后就阻塞在等待拿锁了,拿到锁就又初始化了。
private static Singleton instance = null; public static Singleton getInstance() { if(instance == null) { synchronized(Singleton.class) { instance = new Singleton(); } } return instance; }
懒汉-Volatile版
线程不安全。无法达成同步(一个线程进入if判断完毕后,另一个线程也进入if判断,还是null)
private static volatile Singleton instance; public static Singleton getInstance() { if(instance == null) { instance = new Singleton(); } return instance; }
Double-Check Locking(DCL) 双检查锁
使用volatile: 解决new Singleton时指令重排序问题(分配内存->初始化->引用地址赋值给对象)
class Singleton { //DCL //volatile解决有序性问题 private static volatile Singleton singleton; //私有化构造方法 private Singleton() {} public static Singleton newInstance() { // 先判断是否为null再考虑加锁, 提升效率 if(singleton == null){ // 加锁进行同步,确保只有一个线程进入下面的if判断 synchronized (Singleton.class){ //注意这里的双重判断 避免多线程时已经进入第一层 if(singleton == null) { singleton = new Singleton(); } } } return singleton; } }
静态内部类
class Singleton { private Singleton() {} //静态内部类不会再一开始就被装载,无内存消耗 //JVM装载静态内部类是线程安全的??? private static class SingletonInstance { private static final Singleton singleton = new Singleton(); } public static synchronized Singleton newInstance() { return SingletonInstance.singleton; } }
枚举Enum
消费者生产者
平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率
实现
- wait
- Lock
- BlockingQueue
为什么用while 不用 if?
while 自旋锁,防止虚假唤醒
虚假唤醒
虚假唤醒就是一些obj.wait()会在除了obj.notify()和obj.notifyAll()的其他情况被唤醒
举个例子,我们现在有一个生产者-消费者队列和三个线程。
1) 1号线程从队列中获取了一个元素,此时队列变为空。
2) 2号线程也想从队列中获取一个元素,但此时队列为空,2号线程便只能进入阻塞(cond.wait()),等待队列非空。
3) 这时,3号线程将一个元素入队,并调用cond.notify()唤醒条件变量。
4) 处于等待状态的2号线程接收到3号线程的唤醒信号,便准备解除阻塞状态,执行接下来的任务(获取队列中的元素)。
5) 然而可能出现这样的情况:当2号线程准备获得队列的锁,去获取队列中的元素时,此时1号线程刚好执行完之前的元素操作,返回再去请求队列中的元素,1号线程便获得队列的锁,检查到队列非空,就获取到了3号线程刚刚入队的元素,然后释放队列锁。
6) 等到2号线程获得队列锁,判断发现队列仍为空,1号线程“偷走了”这个元素,所以对于2号线程而言,这次唤醒就是“虚假”的,它需要再次等待队列非空。
BlockingQueue的实现
责任链模式
建造者模式
单例模式
工厂模式
简单工厂模式
抽象工厂模式
外观模式
**主要解决:**降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
何时使用: 1、客户端不需要知道系统内部的复杂联系,整个系统只需提供一个"接待员"即可。 2、定义系统的入口。
缺点: 不符合开闭原则,如果要改东西很麻烦,继承重写都不合适
https://www.runoob.com/design-pattern/facade-pattern.html
代理模式
结构型模式 一个类代表另一个类的功能
**意图:**为其他对象提供一种代理以控制对这个对象的访问。
**主要解决:**在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
静态代理
业务接口 --- > 基本实现类 ---> 代理类
缺点: 代码重复 、 为单一类型对象服务
动态代理
业务接口 ---> 基本实现类 --- >动态代理类
核心 InvocationHandler
享元模式
适配器模式
两个接口的桥梁 --- 电源适配器
Target -- Adaptee -- Adapter
一个事物本来已经很好了,但是找不到合适的对象去对接,(新老版本更替,老版本/新版本如何适配)
提高了类的复用,让没有关联的两个类一起运行,无层级关系
JavaIO 适配器模式和装饰者模式的区别
InputStreamReader继承了Reader抽象类并实现,且持有了InputStream的引用,这里是通过StreamDecoder类间接持有的,因为从byte到char要经过编码。
很显然,适配器就是InputStreamReader,源角色就是InputStream代表的实例对象,目标接口就是Reader类。OutputStreamWriter 也类似。
InputeStream 类就是以抽象组件存在的:而FileInputStream就是具体组件,它实现了抽象组件的所有接口;FilterInputStream类无疑就是装饰角色,它实现了InputStream类的所有接口,并且持有InputStream的对象实例的引用;
BufferedInputStream是具体的装饰器实现者,它给InputStream类附加了功能,这个装饰器类的作用就是使得InputStream读取的数据保存在内存中,而提高读取的性能。与这个装饰器类有类似功能的还有LineNumberInputStream(java 1.8 已经过期)类,它的作用就是提高行按行读取数据的功能,它们都是InputStream类增强了功能,或者提升了性能。
装饰者模式
向一个现有的对象添加新的功能,同时不改变其结构,作为现有类的一个包装,动态扩展一个实现类的功能
component : 抽象组件角色,定义一组抽象的接口,规定这个被装饰组件都有哪些功能
concreteComponent:实现这个抽象组件的所有功能。
Decorator:装饰器角色,它持有一个component对象实例的引用,定义一个与抽象组件一致的接口
ConcreteDecorator:具体的装饰器实现者,负责实现装饰器角色定义的功能