单例模式(Singleton Pattern):确保一个类有且只有
一个实例,并提供一个全局访问点。
在开发中,很多时候有一些对象其实我们只需要一个,例如:线程池(threadpool)
、缓存(cache)
、默认设置
、注册表(registry)
、日志对象
等等,这个时候把它设计为单例模式是最好的选择。
Java中单例模式是一种广泛使用的设计模式,单例模式有很多好处,它能够避免实例对象的重复创建,不仅可以减少每次创建对象的时间开销,还可以节约内存空间(比如spring管理的无状态bean);还能够避免由于操作多个实例导致的逻辑错误。如果一个对象有可能贯穿整个应用程序,而且起到了全局统一管理控制的作用,那么单例模式也许是一个值得考虑的选择。
/**
* @author inke219223m
*/
public class Singleton {
private static Singleton instance;
private Singleton_01() {}
public static Singleton getInstance() {
if (null != instance) return instance;
instance = new Singleton();
return instance;
}
}
此模式保证了线程安全,但是由于把锁加到了方法上,所有的访问都因为需要锁占用导致资源浪费。
/**
* @author inke219223m
*/
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (null != instance) return instance;
instance = new Singleton();
return instance;
}
}
/**
* @author inke219223m
*/
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
// 注意此处还得有次判空~
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
此模式在程序启动时直接运行加载,后续有外部需要使用的时候获取即可。无论程序中是否用到这样的类都会在程序启动之初创建。(这也是它的缺点,无意义地占用内存)
/**
* @author inke219223m
*/
public class Singleton {
private static Singleton instance = new Singleton();
public Singleton(){};
public static Singleton getInstance() {
return instance;
}
}
/**
* @author inke219223m
*/
public class Singleton {
private static Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
使用类的静态内部类实现的单例模式,保证了线程安全,也保证了懒加载,也不会因为加索的方式耗费性能,这主要是因为JVM虚拟机保证多线程并发访问正确性,一个类的构造方法在多线程环境可以被正确加载(推荐使用)。
/**
* @author inke219223m
*/
public class Singleton {
private static class SingletonHolder {
private static Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
Java并发库提供了很多原子类支持并发访问数据安全性,AtomicReference可以封装引用一个实例,支持并发访问。使用CAS的好处就是不需要使用传统的加锁方式保证线程安全,而是依赖于CAS的忙等算法,依赖于底层硬件的时间。相对于其他锁的实现没有线程的切换和组测也就没有了额外的开销,可以支持比较大的并发性,缺点就是如果一直没有获取到将会处于死循环中。
/**
* @author inke219223m
*/
public class Singleton {
private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();
private static Singleton instance;
private Singleton() {
}
public static final Singleton getInstance() {
for (; ; ) {
Singleton instance = INSTANCE.get();
if (null != instance) return instance;
INSTANCE.compareAndSet(null, new Singleton());
return INSTANCE.get();
}
}
}
这种方式在功能上与共有域方法相近,但更加简洁,无偿提供串行化机制,绝对防止对此实例化(但是在继承场景下不可用)。
/**
* 单元素的枚举类型已经成为实现Singleton的最佳方法
* @author inke219223m
*/
public enum Singleton {
INSTANCE;
public void doSomething() {
System.out.println("doSomething");
}
}
class MainTest {
public static void main(String[] args) {
Singleton.INSTANCE.doSomething();
}
}
项目地址:https://gitee.com/codingce/codingce-leetcode