目的:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
应用场景:
数据库连接、缓存操作、分布式存储。
怎么实现单例模式:
思考:既然是单例,那就不能让大家随随便便new 了,否则,你new一个,我new一个,还如何保证单例呢?
不让大家new,那就让类自己new吧,怎么做到呢?构造器私有;
自己new完了,大家如何获取实例呢?那就对外提供一个方法吧,没有类实例的对象,怎么访问这个方法呢?只能依靠类来访问了,方法前加static吧;
方法是有了,可static方法只能访问static变量啊,那就把变量也搞成static吧;
单例分类
饿汉式:类加载时便已初始化实例,用空间换时间,不管现在是否使用,先创建了再说;线程安全吗?可以通过反射机制攻击;线程安全[多个类加载器除外];
懒汉式:我懒我快乐,用的时候在创建实例被,不着急;具体细分为:双重校验锁、内部类实现
枚举实现:简单,一行代码搞定;
代码实现:
/**
*饿汉式1
*/
class Singleton0{
private static Singleton0 _singleton = null;
static {
_singleton = new Singleton0();
}
private Singleton0(){}
public static Singleton0 getInstance(){
return _singleton;
}
}
/**
*饿汉式2
*/
class Singleton1{
private static Singleton1 _singleton = new Singleton1();
private Singleton1(){}
public static Singleton1 getInstance(){
return _singleton;
}
}
//懒汉式:用的的时候在初始化实例;用时间换空间;线程安全吗?
/**
*懒汉式1:大多数线程安全,但每次获取实例都要加锁,效率低下;
*/
class Singleton2{
private static Singleton2 _singleton = null;
private Singleton2(){}
public static synchronized Singleton2 getInstance(){
if(null == _singleton){
_singleton = new Singleton2();
}
return _singleton;
}
}
/**
*懒汉式2:大多数线程安全,有所改进,加锁次数少了;
*双重校验锁
*/
class Singleton3{
private static Singleton3 _singleton = null;
private Singleton3(){}
public static Singleton3 getInstance(){
if(null == _singleton){
synchronized(Singleton3.class){
if(null == _singleton){
_singleton = new Singleton3();
}
}
}
return _singleton;
}
}
/**
*懒汉式3:线程安全;
*new singleton();这步非原子操作,jvm会进行重排序;导致线程不安全;
*volatile 可禁止重排序;
*/
class Singleton4{
private static volatile Singleton4 _singleton = null;
private Singleton4(){}
public static Singleton4 getInstance(){
if(null == _singleton){
synchronized(Singleton4.class){
if(null == _singleton){
_singleton = new Singleton4();
}
}
}
return _singleton;
}
}
/**
*懒汉式4:线程安全;
*内部类实现;
*/
class Singleton5{
private static class SingletonHolder{
private static Singleton5 _singleton = new Singleton5();
}
private Singleton5(){}
public static Singleton5 getInstance(){
return SingletonHolder._singleton;
}
}
/**
*懒汉式5:线程安全;
*内部类实现;
*/
class Singleton6{
private static class SingletonHolder{
public static final Singleton6 _singleton = new Singleton6();
}
private Singleton6(){}
public static final Singleton6 getInstance(){
return SingletonHolder._singleton;
}
}
/**
*上面提到的所有实现方式都有两个共同的缺点:
*(1)都需要额外的工作(Serializable、transient、readResolve())来
*实现序列化,否则每次反序列化一个序列化的对象实例时都会创建一个新的例。
*(2)使用反射强行调用私有构造器(如果要避免这种情况,可以修改构造器,让
*它在创建第二个实例的时候抛异常)。
*/
/**
*枚举实现单例模式:简洁,无偿地提供了序列化机制,由JVM从根本上提供保
*障,绝对防止多次实例化。线程安全
*没有延迟加载
*/
enum Singleton7{
instance;
}
补充:Object obj = new Object(); 这一句到底发生了什么?
1.给obj分配内存,在栈中分配并初始化为null
2.调用构造函数,生成对象实例,在堆中分配
3.把obj指向在堆中分配的对象由于指令重排序优化,执行顺序可能会变成1,3,2,
在多线程中,那么当一个线程执行完1,3之后,被另一个线程抢占,
这时obj已经不是null了,就会直接返回。
然而2还没有执行过,也就是说这个对象实例还没有初始化过。
懒汉式的模型中懒汉式2即有此问题存在。
上面代码编译后生成的字节码文件和反编译生成的对应代码如下截图
图先放这里,以后可能有用。