今天再为大家提供一个实现单例模式的方法,注册式单例模式。
首先创建一个容器类,用来盛放创建的单例。
public class ContainerSingleton2 {
private ContainerSingleton2(){}
private static Map<String,Object> ioc=new ConcurrentHashMap<>();
public static Object getInstance(String className){
Object instance=null;
if(!ioc.containsKey(className)){
try {
instance=Class.forName(className).newInstance();
ioc.put(className,instance);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return instance;
}
return ioc.get(className);
}
}
上方代码似乎已经实现了单例模式。那么它到底是否符合线程安全,我们来测试一下。
首先创建一个实体类:
public class Pojo {
}
我们通过多线程创建该实体类的实例,看是否生成的实例符合单例模式的特点。
线程类:
public class ContainerSingleThread implements Runnable{
@Override
public void run() {
//获取实例对象
Pojo instance= (Pojo) ContainerSingleton2.getInstance("com.example.singleton.Pojo");
System.out.println(Thread.currentThread().getName()+"--->"+instance);
}
}
测试类:
public class ContainerSingleTest {
public static void main(String[] args) {
Thread t1 = new Thread(new ContainerSingleThread());
Thread t2 = new Thread(new ContainerSingleThread());
t1.start();
t2.start();
}
}
多运行几次,我们得到的结果如下:
Thread-0--->com.example.singleton.Pojo@6a5f10aa
Thread-1--->com.example.singleton.Pojo@406851d4
很显然实际结果与我们想的不一样。在多线程争夺时,上方代码没办法保证线程安全性。接下来我们对容器类做一个优化:
ublic class ContainerSingleton {
private ContainerSingleton() {
}
private static Map<String, Object> ioc = new ConcurrentHashMap<>();
public static Object getInstance(String className) {
Object instance = null;
if (!ioc.containsKey(className)) {
synchronized (ContainerSingleton.class) {
if (!ioc.containsKey(className)) {
try {
instance = Class.forName(className).newInstance();
ioc.put(className, instance);
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
}
}
return ioc.get(className);
}
}
加上双重校验。这样可以保证在多线程的情况下保证线程的安全性。原因很简单,从代码层看,我们校验了两次(!ioc.containsKey(className))。第一次,为了判断是否竞争锁。第二次是为了判断是否创建单例。