六种单例模式
Posted NewWorldForU
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了六种单例模式相关的知识,希望对你有一定的参考价值。
目录
- 1、 适合小对象的单例
- 2、 适合大对象,多线程,性能要求不高,第一版懒加载
- 3、 适合大对象,多线程,性能要求不高,第二版懒加载,单重判断
- 4、 适合大对象,多线程,性能要求不高,第三版懒加载,双重判断
- 5、 静态内部类
- 6、 枚举
- 测试主方法
1、 适合小对象的单例
/**
1. 如何保证如下类的实例对象在内存中只有一份
2. (1)让外界从一个池中取对象(通过池保证类的实例对象只有一个)
3. (2)让外界直接通过类的静态方法获取类内部创建的一个实例
*/
class Singlenton01//应用场景:适合小对象
static Singlenton01 instance = new Singlenton01();
private Singlenton01()
public static Singlenton01 getInstance()
return instance;
饿汉式的优点:方便,简单,易于理解。
饿汉式的缺点:如果对象是大对象,在创建了大对象之后很久不去使用则会造成内存的极大的浪费。
2、 适合大对象,多线程,性能要求不高,第一版懒加载
/**
* 线程不安全?导致线程不安全的原因是什么?
* (1)多个线程并发执行
* (2)多个线程有共享数据集(例如instance)
* (3)多个线程在共享数据上的操作不是原子操作(所谓原子操作是指不会被线程调度机制打断的操作)
*/
class Singlenton02//应用场景:适合大对象,单线程
private static Singlenton02 instance;
private Singlenton02()
System.out.println("Singlenton02.Singlenton02()");
public static Singlenton02 getInstance()
if(instance == null)
instance = new Singlenton02();
return instance;
3、 适合大对象,多线程,性能要求不高,第二版懒加载,单重判断
class Singlenton03//应用场景:适合大对象,多线程,性能要求不高
private static Singlenton03 instance;
private Singlenton03()
System.out.println("Singlenton03.Singlenton03()");
public static synchronized Singlenton03 getInstance()
if(instance == null)
instance = new Singlenton03();
return instance;
4、 适合大对象,多线程,性能要求不高,第三版懒加载,双重判断
class Singlenton04//应用场景:适合大对象,多线程,性能要求不高
/**
*优化: 在保证线程安全的情况下,尽量保证线程阻塞
*/
private static volatile Singlenton04 instance;
/**
* volatile关键字的作用
* (1)禁止指令重排序(jvm对JAVA程序进行编译时会有指令重排序)
* (2)保证可见性(多CPU,一个线程对某个线程共享变量的修改对另一个线程可见的)
* (3)但不保证原子性
*/
private Singlenton04()
System.out.println("Singlenton04.Singlenton04()");
public static Singlenton04 getInstance()
if(instance == null) //1位置
synchronized (Singlenton04.class)
if(instance == null)
instance = new Singlenton04(); //2位置
return instance;
这种方式使用双重检查锁,多线程环境下执行getInstance()时先判断单例对象是否已经初始化,如果已经初始化,就直接返回单例对象,如果未初始化,就在同步代码块中先进行初始化,然后返回,效率很高。
但是这种方式有一个重要的优化,问题的根源出在位置2
sInstance =new Singleton();这句话创建了一个对象,他可以分解成为如下3行代码:
memory = allocate(); // 1.分配对象的内存空间
ctorInstance(memory); // 2.初始化对象
sInstance = memory; // 3.设置sInstance指向刚分配的内存地址
上述伪代码中的2和3之间可能会发生重排序,重排序后的执行顺序如下
memory = allocate(); // 1.分配对象的内存空间
sInstance = memory; // 2.设置sInstance指向刚分配的内存地址,此时对象还没有被初始化
ctorInstance(memory); // 3.初始化对象
因为这种重排序并不影响Java规范中的规范:intra-thread sematics允许那些在单线程内不会改变单线程程序执行结果的重排序。
线程B访问到的是一个还未初始化的对象。
所以为了禁止这种优化,采用了volatile。
5、 静态内部类
class Singlenton05//适用场景:大对象,高并发,高性能
private Singlenton05()
static class Inner
private static Singlenton05 instance = new Singlenton05();
public static Singlenton05 getSinglenton05()
return Inner.instance;
懒加载的好处:可以在使用对象的适合再去创建对象,可以节省内存。
6、 枚举
enum Instance06
instance06;
private Instance06()
public static Instance06 getInstance06()
return instance06;
测试主方法
public class TestInstance
public static void main(String[] args)
Instance06 instance01 = Instance06.getInstance06();
Instance06 instance02 = Instance06.getInstance06();
System.out.println(instance01==instance02);
System.out.println(instance01.hashCode());
System.out.println(instance02.hashCode());
部分内容参考下链接
https://www.cnblogs.com/a154627/p/10046147.html
———— What is worth doing is worth doing well.
以上是关于六种单例模式的主要内容,如果未能解决你的问题,请参考以下文章