1. 使用synchronized
饱汉:双重检查锁定
、饿汉
、静态内部类
、枚举
都属于利用synchronized同步原理实现
1.1 饱汉:双重检查锁定(double-checked locking)
public class SingleTon {
// 静态实例变量加上volatile
private static volatile SingleTon instance;
// 私有化构造函数
private SingleTon() {}
// 双重检查锁
public static SingleTon getInstance() {
if (instance == null) {
synchronized(SingleTon.class){
if(instance == null){
instance = new SingleTon();
}
}
}
return instance;
}
1.2 饿汉
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
1.3 静态内部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {
}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
以上的静态内部类、饿汉等模式均是通过定义静态的成员变量,以保证单例对象可以在类初始化的过程中被实例化。
这其实是利用了ClassLoader的线程安全机制。ClassLoader的loadClass方法在加载类的时候使用了synchronized关键字。
所以, 除非被重写,这个方法默认在整个装载过程中都是线程安全的。所以在类加载过程中对象的创建也是线程安全的。
1.4 枚举
public enum EnumFactory{
singletonFactory;
private MySingleton instance;
private EnumFactory(){//枚举类的构造方法在类加载是被实例化
instance = new MySingleton();
}
public MySingleton getInstance(){
return instance;
}
}
class MySingleton{//需要获实现单例的类,比如数据库连接Connection
public MySingleton(){}
枚举其实底层是依赖Enum类实现的,这个类的成员变量都是static类型的,并且在静态代码块中实例化的,和饿汉有点像, 所以他天然是线程安全的。
2. 利用CAS原理
public class Singleton {
private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();
private Singleton() {
}
public static Singleton getInstance() {
for (; ; ) {
Singleton singleton = INSTANCE.get();
if (null != singleton) {
return singleton;
}
singleton = new Singleton();
if (INSTANCE.compareAndSet(null, singleton)) {
return singleton;
}
}
}
}
3. 使用ThreadLocal
与前面的方式不同的是,ThreadLocal
保证的是单个线程内部访问的是同一个实例,不同线程访问的不是同一个实例,即局部单例,并非全局单例。
public class Singleton {
private static final ThreadLocal<Singleton> singleton = new ThreadLocal<Singleton>() {
@Override
protected Singleton initialValue() {
return new Singleton();
}
};
public static Singleton getInstance() {
return singleton.get();
}
private Singleton() {
}
}
序列化与反序列化破坏单例问题
序列化对象时,序列化前后得到对象不是同一个实例。解决办法就是在反序列化的过程中使用readResolve()
方法,该方法在反序列化时会被调用,该方法不是接口定义的方法,有点儿约定俗成的感觉,单例实现的代码如下:
import java.io.ObjectStreamException;
import java.io.Serializable;
public class MySingleton implements Serializable {
private static final long serialVersionUID = 1L;
//内部类
private static class MySingletonHandler{
private static MySingleton instance = new MySingleton();
}
private MySingleton(){}
public static MySingleton getInstance() {
return MySingletonHandler.instance;
}
//该方法在反序列化时会被调用,该方法不是接口定义的方法,有点儿约定俗成的感觉
protected Object readResolve() throws ObjectStreamException {
System.out.println("调用了readResolve方法!");
return MySingletonHandler.instance;
}
评论