今天总结一下单利模式,平常用的也比较多,但是深入研究还是最近一段时间,学习总结,单利目前有以下几种写法
饿汉式单例:
饿汉式单例是在类加载的时候就立即初始化,并且创建单例对象。绝对线程安全,在线 程还没出现以前就是实例化了,不可能存在访问安全问题。 优点:没有加任何的锁、执行效率比较高,在用户体验上来说,比懒汉式更好。 缺点:类加载的时候就初始化,不管用与不用都占着空间,浪费了内存。
写法如下
public class HungrySingleton {
//创建一个静态私有的自己,在类加载的时候及创建
private static final HungrySingleton hungrySingleton = new HungrySingleton();
public HungrySingleton() {
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
大部分时候很少用,只有在知道必须要用到单利模式的时候才这个写。
懒汉式单例:在需要使用的时候才开始加载,为了线程安全需要加锁
public class LazySingleton {
//volatile 多线程会回到主线程读写该对象,保证数据的一致性。但是不能保证顺序性
private volatile static LazySingleton lazySingleton ;
public LazySingleton() {
}
//比较常见的写法
public static LazySingleton getInstance(){
if ( lazySingleton == null){
//加锁 防止多线程 生成多个实例对象
synchronized (LazySingleton.class){
if (lazySingleton == null){
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
}
还有一种目前比较流行的写法,利用静态内部类的原理来创建单例模式
public class SecLazySingleton {
public SecLazySingleton() {
if (Insideton.lazy != null){
throw new RuntimeException("不允许反射创建该实例");
}
}
//static final 保证的唯一性
public static final SecLazySingleton getInstance(){
return Insideton.lazy;
}
//在调用的时候内部类才加载生成实例对象
private static class Insideton{
private static final SecLazySingleton lazy = new SecLazySingleton();
}
/**防止被反序列化
//原因序列化时 在jdk 中ObjectInputStream类中的readObject方法
//---》调用readObject0()方法----》调用了 ObjectInputStream 的 readOrdinaryObject()---》
ObjectStreamClass 的 isInstantiable()
boolean isInstantiable() { requireInitialized(); return (cons != null);
}判断实例对象构造方法是否为空,不为空就实例 然后回到ObjectInputStream 的 readOrdinaryObject()
有一段
if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod())
//hasReadResolveMethod()方法
boolean hasReadResolveMethod() { requireInitialized();
return (readResolveMethod != null);
}
ObjectStreamClass()方法中给 readResolveMethod 进行赋值
readResolveMethod = getInheritableMethod( cl, "readResolve", null, Object.class);
。。。。。。。。。。。。。。。。。后面一系列的逻辑
其实序列化的时候就创建了2次,新创建的对象没有返回,还是使用以前的对象,一般情况下不会去这么干序列化单利
**/
private Object readResolve(){
return Insideton.lazy;
}
}
jdk再设计的时候考虑到了单例会被各种破坏,于是利用jdk的规则,有人发明了
注册式单例:
每一个实例都登记到某一个地方,使用唯一的标 识获取实例.
public enum EnumSingleton {
INSTANCE;
private Object data; public Object getData() {
return data;
}
public void setData(Object data) { this.data = data;
}
public static EnumSingleton getInstance(){
return INSTANCE;
}
}
容器式单例(注册式单例的第二种写法)
public class ContainerSingleton {
public ContainerSingleton() {
}
private static Map ioc = new ConcurrentHashMap<>();
//不敢保证线程安全
public static Object getBean(String className){
synchronized (ioc) {
if (!ioc.containsKey(className)) {
Object obj = null;
try {
obj = Class.forName(className).newInstance();
ioc.put(className, obj); } catch (Exception e) {
e.printStackTrace();
}
return obj;
} else {
return ioc.get(className); }
}
}
}
个人平时写的话 都比较喜欢第二种和第三种,既保证了线程安全,内存消耗相对不会太浪费。