单例的实现有很多种,大部分人其实还是用的 synchronized
目录
-
synchronized 懒汉式单例
public class Singleton { private static Singleton instance=null; private Singleton() { } public synchronized static Singleton getInstance(){ if (instance == null) { instance = new Singleton(); } return instance; } } #这种方式效率比较低,性能不是太好,不过也可以用, 因为是对整个方法加上了线程同步,其实只要在new的时候考虑线程同步就行了 ,这种方法不推荐使用。 *****************邪恶的分割线************************** public class Singleton { private static Singleton instance; private final static Object syncLock = new Object(); private Singleton() { } public static Singleton getInstance(){ if (instance == null) { synchronized (syncLock) { if (instance == null) { instance = new Singleton(); } } } return instance; } } 上面两种都是单例的实现方式
2.java 饿汉式单例
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
饿汉式在 类加载的时候就初始化了,一次是线程安全的,懒汉式不是线程的安全的,因此懒汉式需要配合synchronized关键字
你也可以这样写饿汉式单例
public class Singleton {
private Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return this.instance;
}
}
或者是下面这样写
public class Singleton {
private static class SingletonInite {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonInite.INSTANCE;
}
}
3.用枚举实现单例(推荐这么用)
class Singleton {
}
public enum CreateSinleton{
Singleton ;
private Singleton instance;
CreateSinleton() {
//这个是枚举类的构造方法,反编译之后对应的是每一个枚举子项类型的构造方法
instance = new Singleton ();
}
public Singleton getInstance() {
return instance;
}
}
获取枚举中的单例:
public class Main {
public static void main(String[] args) {
//CreateSinleton.INSTANCE其实就是获取枚举中的INSTANCE类
//INSTANCE.getInstance()其实就是INSTANCE类调用其中的方法,写在枚举中的方法,对每一个枚举元素都是公用的
Singleton singleton = CreateSinleton.INSTANCE.getInstance();
}
}
注意:enum 中的Singleton 反编译之后是static final 的,这也是为什么枚举在构造法方法中初始化类之后是单例的。
4.CAS自旋实现单例
public class Singleton {
/** CAS 实现 持有 单例实例的引用,CAS 技术保证原子性,AtomicReference 持有引用的变量使用 volatile 修饰保证 可见性 */
private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>();
private Singleton() {}
public static Singleton getInstance() {
for (;;) {
/** 单例实现了 直接返回*/
Singleton singleton = INSTANCE.get();
if (null != singleton) {
return singleton;
}
/** 未创建 单例实例,直接 cas 原子创建 */
singleton = new Singleton();
if (INSTANCE.compareAndSet(null, singleton)) {
return singleton;
}
}
}
}
基于CAS实现单例的优缺点
用CAS的好处在于不需要使用传统的锁机制来保证线程安全,CAS是一种基于忙等待的算法,依赖底层硬件的实现,相对于锁它没有线程切换和阻塞的额外消耗,可以支持较大的并行度。
CAS的一个重要缺点在于如果忙等待一直执行不成功(一直在死循环中),会对CPU造成较大的执行开销。另外,如果N个线程同时执行到singleton = new Singleton();的时候,会有大量对象创建,很可能导致内存溢出。