代码参考:《重学Java设计模式·小傅哥》
目录
- 1、静态类使用
- 2、懒汉模式(线程不安全)
- 3、懒汉模式(线程安全)
- 4、饿汉模式(线程安全)
- 5、使用类的内部类(线程安全)
- 6、双重锁检验(线程安全)
- 7、CAS[AtomicReference](线程安全)
- 8、枚举单例(线程安全)
在单例模式的实现上,可以考虑以下几点:
1、是否支持懒汉模式
2、是否线程安全
1、静态类使用
public class Singleton_00 {
public static Map<String, String> cache = new ConcurrentHashMap<String, String> ();
}
使用静态类的方式可以在第一次运行时就直接初始化Map类,同时也不需要延迟加载使用。
仅用于全局访问,使用方便。
如果需要被继承或者维持一些特定状态,则不能使用。
这个方法不属于单例模式,但是蕴含单例模式思想
2、懒汉模式(线程不安全)
public class Singleton_01 {
private static Singleton_01 instance;
private Singleton_01() {}
public static Singleton_01 getInstance() {
if (null != instance) return instance;
instance = new Singleton_01();
return instance;
}
}
单例模式的特点就是不允许外部直接创建,也就是构造函数腰围private。
此方式单例满足了懒汉加载,但是如果有多个访问者同时去获取对象实例会造成多同样的实例并存,没有达成单例要求。
3、懒汉模式(线程安全)
public class Singleton_02 {
private static Singleton_02 instance;
private Singleton_02() {}
public static synchronized Singleton_02 getInstance() {
if (null != instance) return instance;
instance = new Singleton_02();
return instance;
}
}
此模式保证了线程安全,但是由于把锁加到了方法上,所有的访问都因为需要锁占用导致资源浪费。
4、饿汉模式(线程安全)
public class Singleton_03 {
private static Singleton_03 instance = new Singleton_03();
private Singleton_03() {}
public static Singleton_03 getInstance() {
return instance;
}
}
此模式在程序启动时直接运行加载,后续有外部需要使用的时候获取即可。
无论程序中是否用到这样的类都会在程序启动之初创建。(这也是它的缺点,无意义地占用内存)
5、使用类的内部类(线程安全)
public class Singleton_04 {
private static class SingletonHolder {
private static Singleton_04 instance = new Singleton_04();
}
private Singleton_04() {}
public static Singleton_04 getInstance() {
return SingletonHolder.instance;
}
}
使用类的静态内部类实现的单例模式,保证了线程安全,也保证了懒加载,也不会因为加索的方式耗费性能
这主要是因为JVM虚拟机保证多线程并发访问正确性,一个类的构造方法在多线程环境可以被正确加载
推荐使用
6、双重锁检验(线程安全)
public class Singleton_05 {
private static Singleton_05 instance;
private Singleton_05() {}
public static Singleton_05 getInstance() {
if (null != instance) return instance;
synchronized (Singleton_05.class) {
if (null == instance)
instance = new Singleton_05();
}
return instance;
}
}
双重锁的方式是方法级锁的优化,减少了部分获取实例的耗时,同时这种方法也满足懒加载
7、CAS[AtomicReference](线程安全)
public class Singleton_06 {
private static final AtomicReference<Singleton_06> INSTANCE =
new AtomicReference<Singleton_06>();
private static Singleton_06 instance;
private Singleton_06() {}
public static final Singleton_06 getInstance() {
for (;;)
{
Singleton_06 instance = INSTANCE.get();
if (null != instance) return instance;
INSTANCE.compareAndSet(null, new Singleton_06());
return INSTANCE.get();
}
}
public static void main(String[] args) {
System.out.printIn(Singleton_06.getInstance());
System.out.printIn(Singleton_06.getInstance());
}
}
java并发库提供了很多原子类支持并发访问数据安全性,AtomicReference可以封装引用一个实例,支持并发访问。
使用CAS的好处就是不需要使用传统的加锁方式保证线程安全,而是依赖于CAS的忙等算法,依赖于底层硬件的时间。
相对于其他锁的实现没有线程的切换和组测也就没有了额外的开销,可以支持比较大的并发性
缺点就是如果一直没有获取到将会处于死循环中。
8、枚举单例(线程安全)
public enum Singleton_07 {
INSTANCE;
public void test() {
System.out.printIn("hi~");
}
}
调用方式:
public void test() {
Singleton_07.INSTANCE.test();
}
这种方式在功能上与共有域方法相近,但更加简洁,无偿提供串行化机制,绝对防止对此实例化。
但是在继承场景下不可用