我们可以通过打印懒汉式/饿汉式单例模式实例的hashcode来观察多线程下的单例模式所引发的问题。
下面是代码:
/**
* 懒汉式单例
*/
class MySingleton{
private static MySingleton mst;
public static MySingleton getInstance(){
if(mst==null){
return new MySingleton();
}
return null;
}
}
/**
* 饿汉式单例
*/
class MySingleton2{
private static MySingleton2 instance = new MySingleton2();
public static MySingleton2 getInstance(){
return instance;
}
}
//编写懒汉式的单例模式,创建100个线程,每个线程获得一个单例对象,看是否存在问题(打印对象的hashCode,看是否相同)
//分析问题原因,解决问题
public class Demo120801 {
public static void main(String[] args) {
new Thread(()->{
for (int i = 0; i <100 ; i++) {
MySingleton mySingleton = MySingleton.getInstance();
System.out.println("懒汉式:"+mySingleton.hashCode());
}
}).start();
new Thread(()->{
for (int i = 0; i <100 ; i++) {
MySingleton2 mySingleton2 = MySingleton2.getInstance();
System.out.println("饿汉式:"+mySingleton2.hashCode());
}
}).start();
}
下面是运行结果截取:
懒汉式:419696933
懒汉式:229758908
懒汉式:343752916
懒汉式:743742832
懒汉式:358628582
懒汉式:384763329
饿汉式:1926683415
饿汉式:1926683415
饿汉式:1926683415
饿汉式:1926683415
饿汉式:1926683415
饿汉式:1926683415
饿汉式:1926683415
通过结果我们可以看到,懒汉式的单例模式失效了,会生成多个实例。
我们一步一步拆解原因:
饿汉式在类加载时才会初始化,以后都不会,这也意味着它是线程安全的。
懒汉式是在需要的时候才会创建对象,在没有获取到有对象之前所有对象的创建都是通过的,因此,在多线程环境下线程启动时,都是需要创建的状态,最终导致创建了多个不同的实例。
解决懒汉式单例模式的方法,这里我列举一个例子:
class MySingleton{
//双重判断,提高程序性能
private static MySingleton mst;
public static MySingleton getInstance(){
//当实例被创建之后就不需要再上锁
if (mst==null) {
//实例没有被创建之前需要上锁
synchronized (MySingleton.class) {
if (mst == null) {
return new MySingleton();
}
}
}
return null;
}
}