在开发中,我们也许会用到只能实现一个实例的类的情况,也就是面向对象中重要的设计模式——单例模式,下面,就来介绍一下集中常见的单例模式以及优缺点。
- 饿汉
public class Singleton {
private Singleton(){}
private static Singleton singleton = new Singleton();
public static Singleton getInstance(){
return singleton;
}
}
这种实现的额方式,如其名字一般,在开始就创建一个实例,需要的时候调用,有着线程安全的特点,但是存在一些问题,就是浪费资源,如果某个单例模式用的不多,那么每次创建的实例就是浪费了。针对这个特点,就有了懒汉模式。
- 懒汉模式
public class Singleton {
private Singleton(){}
private static Singleton singleton;
public static Singleton getInstance(){
if (singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
这是懒汉的实现形式,通过对比可知,此模式只在使用的时候才会创建实例,并赋予用户使用。看起来貌似很完美了,节省了资源。但是在实际的使用中,会遇到多线程的问题,此时有可能创建可不止一个实例,因此,这种实现方式是不安全的。针对此,可以使用java中的锁机制。
- 懒汉优化——java锁
public class Singleton {
private Singleton(){}
private static Singleton singleton;
public static Singleton getSingleton(){
if (singleton == null){
synchronized (Singleton.class){
singleton = new Singleton();
}
}
return singleton;
}
}
此实现方式用到了锁机制,在一定程度上保证了线程的安全。但是咱们按照调试bug的思路自己过一遍,假如有两个线程,都通过了if语句,将要执行synchronized 方法内的代码,但是此时只能第一个执行(先来的),于是第一个执行完毕,第二个继续执行,由此可见,第二个线程也执行了synchronized 中的代码,因此,这种方式并没有完美的解决多线程的问题。于是,针对这种特点,引入了双重锁机制。
- 懒汉优化——双重锁
public class Singleton {
private Singleton(){}
private static Singleton singleton;
public static Singleton getSingleton(){
if (singleton == null){
synchronized (Singleton.class){
if (singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
仔细观看上面的实现方式,根据上面的检查方式再过一遍,发现第一个线程执行完毕后,第二个线程会进入第二个if语句,但是没有执行if语句中的内容,一次,解决了在多线程的生产环境中的问题。但是,,还要问一句,真的是完美的吗??? 引用别人的解释:
不用双重检查锁的单例没有线程安全问题 却有效率问题。 效率在于每次来的都要得到锁。 用双重检查锁的单例有线程安全问题 效率好一些。 线程安全问题在于 讲一个没有完全实例化的对象释放出去。 public static Singleton getInstance() { if (instance == null)//4 { synchronized(Singleton.class) { //1 if (instance == null) //2 instance = new Singleton(); //3 } } return instance; 这段代码 中,一个线程看到第四步 发现 不为null,拿去使用了。但是实际是这个不为null的对象,并没有完全实例化。 原因在于: 3处 这个地方代码只有一行,真正意义上完成一个对象需要三步。 1,分配对象的内存空间 1买了房子 2,为该对象的属性赋值 2给房子装修 3,初始化该对象。 3 把名字给你。 这个顺序不一定,加入说 3 执行了 再执行2 就出问题了. 解决办法是 给instance的修饰加上volatile 。这上这个字后 保证不会重排序。。。。这样得到的就是正确的对象。
- 懒汉优化——volitile
public class Singleton {
private Singleton(){}
private static volatile Singleton singleton;
public static Singleton getSingleton(){
if (singleton == null){
synchronized (Singleton.class){
if (singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
按照大神的理论,这样就没问题了,,但是细心的我们可以发现,代码非常繁琐,,那么性能怎样呢,,明确的告诉你,并不高,尽管他已经实现了线程安全和懒汉,下面介绍一个更好的。
- 懒汉——java内部类
public class Singleton {
private Singleton(){}
private static class Instance{
static Singleton singleton = new Singleton();
}
public static Singleton getSingleton(){
return Instance.singleton;
}
}
相对来说,这才是最好的单例模式实现形式。