使用场景:
一个类只能创建一个实例对象。如读取服务器配置文件的类,由单个实例对象直接读取。
实现流程:
(1)、定义一个私有变量;
(2)、将构造函数私有化;
(3)、提供一个获取实例的公用方法;
下面列出几种常见的实现模式,仅供参考:
饿汉式单例:在类创建的时候就初始化实例对象,每次调用的时候都是获取同一对象实例,是线程安全的。
实现类:
public class HungrySingleton {
private static HungrySingleton instance = new HungrySingleton();
private HungrySingleton(){};
public static HungrySingleton getInstance(){
return instance;
}
}
测试类:
public class HungrySingletonTest {
private class MyThread extends Thread{
@Override
public void run(){
System.out.println(HungrySingleton.getInstance().hashCode());
}
}
public static void main(String[] args) {
MyThread[] mts = new MyThread[10];
for(int i = 0 ; i < mts.length ; i++){
mts[i] = new HungrySingletonTest().new MyThread();
}
for (int j = 0; j < mts.length; j++) {
mts[j].start();
}
}
}
测试结果:
从测试结果可以看出,该方式是线程安全的。
普通懒汉式单例:创建类的时候不实例化对象,在调用获取实例方法的时候才创建对象。是线程不安全的。
实现类:
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton(){};
public static LazySingleton getInstance(){
if(instance == null){
instance = new LazySingleton();
}
return instance;
}
}
测试类:
public class LazySingletonTest {
private class MyThread extends Thread{
@Override
public void run(){
System.out.println(LazySingleton.getInstance().hashCode());
}
}
public static void main(String[] args) {
MyThread[] mts = new MyThread[10];
for(int i = 0 ; i < mts.length ; i++){
mts[i] = new LazySingletonTest().new MyThread();
}
for (int j = 0; j < mts.length; j++) {
mts[j].start();
}
}
}
测试结果:
从测试结果可以看出,该方式是线程不安全的。
对线程不安全的懒汉式单例模式,有多种的解决方案。简单的如:在获取实例的公共方法里加上同步标识符synchronized。这样的话可以保证在多线程的环境下获取的实例是唯一的,但也相应地影响了效率。这里列举一种常规的解决方案——Double Check Locking双检查锁机制。
双检查锁机制(DCL)
实现类:
public class DclSingleton {
//使用volatile保证线程间可见性
private static volatile DclSingleton instance = null;
private DclSingleton(){};
public static DclSingleton getInstance(){
if(instance == null){
synchronized(DclSingleton.class){
//二次检查
if(instance == null){
instance = new DclSingleton();
}
}
}
return instance;
}
}
从测试结果可以看出,该方式是线程安全的,且将同步的范围限制到了最小。是推荐的一种单例实现方式。