1. 最简单的懒汉单例模式
/**
* 1. 最简单的懒汉单例模式
* 2. 将构造方法私有化,这样是防止在其他地方被实例化,就出现多个班长对象了
* 3. 最后给外界提供一个方法,返回这个班长对象即可
* 4. 不能保证线程安全问题
*/
public class SimpleLazySingleton {
private static SimpleLazySingleton SIMPLE_LAZY_INSTANCE = null;
private SimpleLazySingleton() {
}
public static SimpleLazySingleton getMonitor() {
//如果两个线程同时到了判断null的地步,就会打破单例模式
if (SIMPLE_LAZY_INSTANCE == null) {
SIMPLE_LAZY_INSTANCE = new SimpleLazySingleton();
}
return SIMPLE_LAZY_INSTANCE;
}
public static void main(String[] args) {
new Thread(new ExecutorThread()).start();
new Thread(new ExecutorThread()).start();
System.out.println("end");
}
1.1 如何测试线程是否安全
/**
* @author xiyou
* 测试懒汉模式是否线程安全
*/
public class ExecutorThread implements Runnable {
@Override
public void run() {
SimpleLazySingleton instance = SimpleLazySingleton.getMonitor();
System.out.println(Thread.currentThread().getName() + ":" + instance);
}
}
public static void main(String[] args) {
new Thread(new ExecutorThread()).start();
new Thread(new ExecutorThread()).start();
}
测试结果:
1. 相同情况:(也会有线程安全问题,第一个结果会被第二个结果覆盖)
Thread-0:cn.net.health.tools.design.single.lazy.SimpleLazySingleton@3abf0e6c
Thread-1:cn.net.health.tools.design.single.lazy.SimpleLazySingleton@3abf0e6c
2. 线程不安全情况
Thread-1:cn.net.health.tools.design.single.lazy.SimpleLazySingleton@b91ca14
Thread-0:cn.net.health.tools.design.single.lazy.SimpleLazySingleton@5d52c55b
1.2 如何多线程打断点
A:多线程环境下,执行结果是相同实例
-
顺序执行线程安全
-
不是顺序执行,线程同时进入if判断,后者创建的实例会覆盖前者,2个打印的结果是后者创建的实例
如何多线程打断点,可以百度下,这里要说的是两个线程的断点要打到哪个地方?
对于A2的情况,只要把两个线程的断点都打到
monitor = new Monitor();
但是都不进行下一步,然后把第一个线程放开,会创建单例1但是不进行return monitor
的操作,然后放开线程2,会创建单例2,然后单例2会把单例1 覆盖掉
B: 结果不一致的情况
对于B的情况,只要把两个线程的断点都打到 monitor = new Monitor();
但是都不进行下一步,然后把第一个线程放开,会创建单例1然后让线程1走完全程并打印,然后放开线程2,就会出现不一致的情况
2. 线程安全的懒汉单例模式
/**
* @author xiyou
* 1. 相对于SimpleLazySingleton多了一个synchronized 线程安全了
* 2. 但是效率太差了,不管班长对象有没有被创建好,后面每个线程并发走到这,无用等待太多了
*/
public class SyncLazySingleton {
private static SyncLazySingleton SYNC_LAZY_INSTANCE = null;
private SyncLazySingleton() {
}
public synchronized static SyncLazySingleton getMonitor() {
if (SYNC_LAZY_INSTANCE == null) {
SYNC_LAZY_INSTANCE = new SyncLazySingleton();
}
return SYNC_LAZY_INSTANCE;
}
}
3. 双重校验DDL懒汉单例模式(线程非安全)
/**
* @author xiyou
* 1. 这个双重判断看似线程安全,但还是有线程安全问题
*/
public class DoubleCheckLazySingleton {
private static DoubleCheckLazySingleton DOUBLE_CHECK_LAZY_INSTANCE = null;
private DoubleCheckLazySingleton() {
}
public static DoubleCheckLazySingleton getMonitor() {
//外层判断用来判断是否要阻塞线程,提高效率,外层的可以去掉,但是会影响效率
if (DOUBLE_CHECK_LAZY_INSTANCE == null) {
synchronized (DoubleCheckLazySingleton.class) {
//内存判断是否要创建线程,保证线程安全,这个不能去掉,去掉线程不安全
if (null == DOUBLE_CHECK_LAZY_INSTANCE) {
DOUBLE_CHECK_LAZY_INSTANCE = new DoubleCheckLazySingleton();
}
}
}
return DOUBLE_CHECK_LAZY_INSTANCE;
}
}
4. Volatile双重校验线程安全的懒汉单例模式
只是比上面多了一个volatile关键字
/**
* @author xiyou
* /*
* 1. 最终版本的懒汉单例模式
* 2. 双重校验的volatile懒汉单例模式
* 3. 添加volatile 是为了防止类初始化的时候出现问题
* 4. 类初始化顺序:monitor =new Monitor();
* 3.1 在堆内存分配空间
* 3.2 把Monitor的构造方法初始化
* 3.3 把monitor对象指向在堆空间分配好的地址空间
* 5. 在多线程条件下,3.2 和3.3的顺序可能互调,volatile就说为了解决这个问题的
* 6. 故 它是线程安全的
*/
public class DoubleCheckVolatileLazySingleton {
private static volatile DoubleCheckVolatileLazySingleton DOUBLE_CHECK_VOLATILE_LZY_INSTANCE = null;
private DoubleCheckVolatileLazySingleton() {
}
public static DoubleCheckVolatileLazySingleton getMonitor() {
//外层判断用来判断是否要阻塞线程,提高效率,外层的可以去掉,但是会影响效率
if (DOUBLE_CHECK_VOLATILE_LZY_INSTANCE == null) {
synchronized (DoubleCheckVolatileLazySingleton.class) {
//内存判断是否要创建线程,保证线程安全,这个不能去掉,去掉线程不安全
if (null == DOUBLE_CHECK_VOLATILE_LZY_INSTANCE) {
DOUBLE_CHECK_VOLATILE_LZY_INSTANCE = new DoubleCheckVolatileLazySingleton();
}
}
}
return DOUBLE_CHECK_VOLATILE_LZY_INSTANCE;
}
}
5. 太复杂?有没有简单的?静态内部类实现
请翻看相关博文