单例模式是一种常见的设计模式,并且有几种不同的写法,不同的写法有不同的效果。
1.懒汉式
public class SingletonThread implements Runnable{
@Override
public void run() {
try {
System.out.println(Singleton.getInstance());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这种写法是最简单的形式,在第一次调用getInstance方法的时候才实例化,缺点是线程不安全。
测试:
修改Singleton:
public class Singleton {
private Singleton() {
};
private static Singleton singleton = null;
public static Singleton getInstance() throws InterruptedException {
if(singleton == null) {
Thread.sleep(100);// 延迟实例化时间,加强测试效果。
singleton = new Singleton();
}
return singleton;
}
}
SingletonThread测试类:
public class SingletonThread implements Runnable{
@Override
public void run() {
try {
System.out.println(Singleton.getInstance()); // 打印得到的Singleton实例hashcode
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Main测试类如下,使用多个线程调用getInstance()方法,
public class Main {
public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 10; i++) {
SingletonThread s = new SingletonThread();
Thread t = new Thread(s);
t.start();
}
}
}
运行之后:
com.sj.pattern.Singleton@17823918
com.sj.pattern.Singleton@401e9c3f
com.sj.pattern.Singleton@6150818a
com.sj.pattern.Singleton@610f7612
com.sj.pattern.Singleton@3e10c986
com.sj.pattern.Singleton@4ecac02f
com.sj.pattern.Singleton@4da9ec16
com.sj.pattern.Singleton@5faecf45
com.sj.pattern.Singleton@667262b6
com.sj.pattern.Singleton@19a40cfc
可以看到,生成了10个不同的Singleton实例,懒汉式写法在多线程环境下可能不会产生真正的单例对象。这样的问题可以再getInstance()方法之前加synchronized来使线程同步,但是这样的写法效率低,并且多线程环境下很少需要同步。
2.饿汉式
public class Singleton {
private Singleton() {
};
private static Singleton singleton = new Singleton();
public static synchronized Singleton getInstance() {
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
饿汉式是在加载类的时候就生成一个实例,避免了线程不安全。public class Singleton {
private Singleton() {};
private static Singleton singleton = null;
static {
singleton = new Singleton();
}
public static synchronized Singleton getInstance() {
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
变种写法的特点在于singleton实例化将在static代码块中进行,与上述的区别仅仅在于生成实例的时机不同。并且这两种方式都没有实现延迟加载。
3.静态内部类
public class Singleton {
private Singleton() {};
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static synchronized Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
静态内部类方式采用了ClassLoader的机制保证了INSTANCE实例的单例化,并且可以实现延迟加载,即在真正需要使用INSTANCE的时候才实例化。4.双重校验锁
public class Singleton {
private volatile static Singleton singleton = null;
public static Singleton getInstance() {
if(singleton == null) {
synchronized (Singleton.class) {
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
双重校验锁方式是懒汉式的升级版,在多线程环境下可以保证实例的唯一性。