public class Singleton {
private static Singleton instance = null;
private Singleton() {
System.out.println(Thread.currentThread().getName() + "\t 我是构造方法Singleton()");
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public static void main(String[] args) {
/** 单线程(main线程的操作动作................)**/
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
for (int i = 1; i <= 10; i++) {
new Thread(()->{
Singleton.getInstance();
},String.valueOf(i)).start();
}
}
}
两种结果:
/** 单线程(main线程的操作动作................)**/
/** 多线程(main线程的操作动作................)**/
加上synchronized
public class Singleton {
private static Singleton instance = null;
private Singleton() {
System.out.println(Thread.currentThread().getName() + "\t 我是构造方法Singleton()");
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public static void main(String[] args) {
/** 单线程(main线程的操作动作................)**/
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
/** 多线程(main线程的操作动作................)**/
for (int i = 1; i <= 10; i++) {
new Thread(()->{
Singleton.getInstance();
},String.valueOf(i)).start();
}
}
}
但是在这里加synchronized太重,能保证数据一致性,但影响并非性
这里只需要控制一行代码,了解下DCL(Double Check Lock 双端检锁机制)
public class Singleton {
private static Singleton instance = null;
private Singleton() {
System.out.println(Thread.currentThread().getName() + "\t 我是构造方法Singleton()");
}
/**
* DCL(Double Check Lock 双端检锁机制)
**/
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
public static void main(String[] args) {
/** 单线程(main线程的操作动作................)**/
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
/** 多线程(main线程的操作动作................)**/
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
Singleton.getInstance();
}, String.valueOf(i)).start();
}
}
}
但还存在问题,DCL(双端检锁)机制不一定线程安全,原因是有指令重排序的存在,加入volatile可以禁止指令重排,原因在于某一个线程执行到第一次检测,读取到到instance不为null时,instance的引用对象可能没有完成初始化.instance=new Singleton();可以分为三步完成,(伪代码);
memory=allocate();//1.分配对象内存空间instance(memory);//2.初始化对象instance=memory;//3.设置instance指向刚分配的内存地址,此时instance!=null;
步骤2和步骤3不存在数据依赖性关系,而且无论重排前还是重排后程序执行结果在单线程中并没有改变,因此这种重排优化是允许的.
memory=allocate();//1.分配对象内存空间instance=memory;//3.设置instance指向刚分配的内存地址,此时instance!=null;但是对象还没有初始化完成!instance(memory);//2.初始化对象
但是指令重排只会保证串行语义的执行的一致性(单线程),但并不会关心多线程间但语义一致性,所以当一条线程访问instance不为null时,由于instance实例未必已初始化完成,也就造成了线程安全问题.
(举个例子:老师为新同学张三分配了一个座位,但是张三还未到,没有就坐,其他同学去听到了这个座位是张三的,去判断不为空,但是实际张三这个真人还没有坐到座位上,发生了指令重排,我们应该考虑避免这种指令重排问题)
(张三这个真人来了,并坐到座位上,其他同学再去要微信号,手机号,才不为空)
总结下:多线程高并发环境下:单粒模式:
1):双端检索
2):volaile
public class Singleton {
//2):volaile
private static volatile Singleton instance = null;
private Singleton() {
System.out.println(Thread.currentThread().getName() + "\t 我是构造方法Singleton()");
}
/**
* DCL(Double Check Lock 双端检锁机制)
**/
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
public static void main(String[] args) {
/** 单线程(main线程的操作动作................)**/
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
// System.out.println(Singleton.getInstance()==Singleton.getInstance());
/** 多线程(main线程的操作动作................)**/
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
Singleton.getInstance();
}, String.valueOf(i)).start();
}
}
}