饿汉式单例模式(写法一)
/**
* 饿汉式单例 写法一
* 优点:简单,性能高,线程安全
* 缺点:浪费内存(当我们的单例对象数量不可控的时候,可能造成内存浪费)
*/
public class HungrySingleton {
//类被加载的时候,单例对象就已经被创建
private static final HungrySingletoninstance =new HungrySingleton();
private HungrySingleton() {
}
public final static HungrySingleton getInstance(){
return instance;
}
}
饿汉式单例(写法二)
/**
* 饿汉式单例 写法二
*/
public class HungryStaticSingleton {
private static final HungryStaticSingletoninstance ;
static {
instance =new HungryStaticSingleton();
}
private HungryStaticSingleton() {
}
public static HungryStaticSingleton getInstance(){
return instance;
}
}
简单的懒汉式写法
/*** 简单的懒汉式写法
* 优点:节省资源,性能更高看,可控
* 缺点:非线程安全
*/
public class LazySimpleSingleton {
private static LazySimpleSingletoninstance;
private LazySimpleSingleton() {
}
public final static LazySimpleSingleton getInstance(){
if(null==instance){
instance =new LazySimpleSingleton();
}
return instance;
}
}
这种写法是非线程安全的,最终导致的结果会出现两种情况:
1,两个线程得到的结果相同:
a)两个线程按顺序进入判断,第2个线程由于判断了非空,这种情况下,第2个线程不会创建对象;从而两个线程得到的结果是一样的;
以下Test代码创建了两个线程
第一个线程已经创建了 一个实例
第二个线程进来后先判断了是否等于null
此时,第二个线程由于判断了,所以不会产生一个新的实例,沿用第一个线程创建的实例
这种情况下,最终导致的结果两个线程创建的实例也是相同的
b)两个线程同时进入到判断中来,后进来的线程会覆盖之前第一个线程创建的对象,这种情况下得到的结果也是一样,但和第一种有本质的区别;
第一个线程进入if里面
第2个线程也进入到if里面
然后第一个线程就实例化了一个对象
紧接着第二个线程也实例化了一个对象
此时两个线程实例化的对象是不一样的,最终打印的时候第二个线程创建的对象将第一个创建的对象覆盖掉了,这样也造成了一种假象,两个实例对象是一样的
2,两个线程得到的结果不同。
这种情况下就是两个线程同时到达if判断里面了,第一个线程实例化了对象继续往下执行打印,第一个线程又实例化了一个对象也继续执行往下打印;最终结果
将以上简单的懒汉式的方式加上锁
public class SyncLazySimpleSingleton {
private static SyncLazySimpleSingletoninstance;
private SyncLazySimpleSingleton() {
}
public synchronized final static SyncLazySimpleSingleton getInstance(){
if(null==instance){
instance =new SyncLazySimpleSingleton();
}
return instance;
}
}
饿汉式
简单的饿汉式 内存浪费
静态块的饿汉式
懒汉式:
简单懒汉式 sync性能问题
双重检查锁 DoubleCheck 造成代码不优雅
静态内部类 会被反射破坏
注册式:
枚举式Enum 内存浪费
容器式 Container 兼顾之前的所有