1.饿汉式
/*
* 1.简单且线程安全
* 2.加载会增加耗时
* 3.如果此类未使用到, 则会一直空闲浪费空间
* 4.final可省去
*/
public class TestSingleton {
private static final TestSingleton testSingleton = new TestSingleton();
private TestSingleton {};
public static TestSingleton getInstance() {
return testSingleton;
}
}
2.懒汉式
静态代码块:
// 线程不安全
public class TestSingleton {
private static TestSingleton testSingleton;
private TestSingleton {};
static {
try{
testSingleton= new TestSingleton();
}catch(Exception e){
throw new RuntimeException("Exception occured in creating singleton instance");
}
}
public static TestSingleton getInstance() {
return testSingleton;
}
}
// 线程安全
public class TestSingleton {
private static TestSingleton testSingleton;
private TestSingleton {};
public static synchronized TestSingleton getInstance() {
if (testSingleton == null) {
testSingleton = new TestSingleton();
}
return testSingleton;
}
}
3.静态内部类(Bill Pugh)
// 线程安全
public class TestSingleton {
private TestSingleton {};
private static class InnerObject {
// final可省去
private static final TestSingleton INSTANCE = new TestSingleton();
}
public static TestSingleton getInstance() {
return InnerObject.INSTANCE;
}
}
4.双重检查锁(Double Checked Locking)
/**
* 优化了懒汉模式的性能
* 但这缺存在一个不易察觉的错误:无法达到同步
*/
public class TestSingleton {
private static TestSingleton testSingleton;
private TestSingleton {};
public static TestSingleton getInstance() {
synchronized (TestSingleton.class) {
if (testSingleton == null) {
testSingleton = new TestSingleton();
}
return testSingleton;
}
}
}
为什么说达不到同步呢?
多线程时, 处理器和编译器为了提高程序的性能会发生指令重排.
例如:
TIME | Thread1 | Thread2 |
---|---|---|
T1 | 检查testSingleton是否为空 | |
T2 | 获取锁 | |
T3 | 再次检查testSingleton是否为空 | |
T4 | 为testSingleton分配空间 | |
T5 | 将testSingleton指向内存空间 | |
T6 | 检查到testSingleton不为空 | |
T7 | 访问testSingleton(此时对象还未完成初始化) | |
T8 | 初始化testSingleton |
在这种情况下,T7时刻线程2对testSingleton的访问,访问的是一个初始化未完成的对象。
使用了volatile关键字后,重排序被禁止,所有的对其写(write)的操作都将发生在读(read)操作之前。
/**
* 1.优化了锁机制所产生的性能消耗
* 2.真正实现了同步
*/
public class TestSingleton {
private volatile static TestSingleton testSingleton;
private TestSingleton {};
public static TestSingleton getInstance() {
if (testSingleton == null) {
synchronized (TestSingleton.class) {
if (testSingleton == null) {
testSingleton = new TestSingleton();
}
}
}
return testSingleton;
}
}
5.枚举
public enum TestSingleton {
// 枚举元素本身就是单例
INSTANCE;
// 添加自己需要的操作, 直接通过TestSingleton.INSTANCE.doSomething()的方式调用即可
// 此方法方便、简洁又安全
public void doSomething() {
System.out.println("...");
}
}
6.额外补充
1.单例模式的破坏
除了枚举
以外的其他几种方式, 均能使用反射
进行破坏
对于破坏行为, 个人认为, 既然编程者需要新的示例而使用此方法, 大可不必去限制
当然如果想限制, 只需在私有构造方法中加入简单的NULL判断即可
例如:
private TestSingleton {
if (testSingleton != null) {
throw new RuntimeException("...");
}
};
2.工具类用单例模式还是静态方法
- 如果没有配置信息的工具类, 选静态类,随处调用,不需引用.
- 如果有配置信息的工具类,最好还是使用单例模式. 如: 多数据源