源无极

导航

 

 

      在java中,单例有很多种写法,面试时,手写代码环节,除了写算法题,有时候也会让手写单例模式

这里记录一下单例的几种写法和优缺点。

  • 1.初级写法

  • 2.饿汉式

  • 3.懒汉式

  • 4.双锁检验

  • 5.内部类

  • 6.枚举

1.初级写法

 1 package sheJiMoShe;
 2 
 3 public class Singleton {
 4      /*
 5       * 缺点:只在单线程的情况下正常运行,在多线程的情况下,就会出问题。
 6       * 例如:当两个线程同时运行到判断instance是否为空的if语句,并且instance确实没有创建好时,
 7       * 那么两个线程都会创建一个实例。
 8       */
 9      
10        private static Singleton instance=null;
11         private Singleton(){
12             
13         }
14         public static Singleton getInstance(){
15             if(instance==null){
16                 instance=new Singleton();
17             }
18             return instance;
19         }
20 }

上面这种写法,在并发环境下,会出现多个实例。

2.饿汉式

饿汉式的特点是:类在加载时就直接初始化了实例。即使没用到,也会实例化。

 1 package sheJiMoShe;
 2 
 3 public class ESingleton {
 4    5      //初试化静态的instance创建一次。如果我们在Singleton类里面写一个静态的方法不需要创建实例,它仍然会早早的创建一次实例。而降低内存的使用率。
 6 
 7       //缺点:没有lazy loading的效果,从而降低内存的使用率。
 8     private static ESingleton instance=new ESingleton();
 9     private ESingleton(){
10         
11     }
12     public static ESingleton getInstance(){
13         return instance;
14     }
15 }

 

3.懒汉式

懒汉式的特点是用到这个实例时才去调用方法实例化。这个和2中的看起来是一样的,因为这个实例化方法加了synchronized ,这样安全一些。

 1 package sheJiMoShe;
 2 
 3 public class BSingleton {
 4     //多线程的情况可以用。(懒汉式,不好)
 5       private static BSingleton instance=null;
 6         private BSingleton(){
 7             
 8         }
 9         //整个方法锁住了,效率低
10         public static synchronized BSingleton getInstance(){
11             if(instance==null){
12                 instance=new BSingleton();
13             }
14             return instance;
15         }
16 }

 

4.双锁检验(可行)

双重非空判断,new对象前加一次锁。

volatile 关键字的使用可以解决一定程度上的多线程访问时出现的同步问题,保证一定程度的线程安全。如果整形的变量由volatile修饰,在其值发生改变后,

能够快速被其他线程可见,告知其他线程自己发生改变。

 1 package sheJiMoShe;
 2 
 3 public class SynchronizedSingleton {
 4 
 5      private volatile  static SynchronizedSingleton instance=null;
 6         private SynchronizedSingleton(){
 7             
 8         }
 9         /*
10          * 只是在实例为空时才进行同步创建
11          * 为什么做了两次null判断?
12          * A线程和B线程同时进入同步方法0
13          * 然后都在1位置处判断了实例为null
14          * 然后都进入同步块2中
15          * 然后A线程优先进入同步代码块2中(B线程也进入了)然后创建了实例
16          * 此时,如果没有3处的判断,那么A线程创建实例的同时,B线程也会创建一个实例,
17          * 所以需要两次null判断
18          * 
19          */
20         public static SynchronizedSingleton getInstance(){//0
21             if(instance==null){//1
22                 synchronized(SynchronizedSingleton.class){//2
23                     if(instance==null){//3
24                         instance=new SynchronizedSingleton();//4
25                     }
26                 }
27             }
28             return instance;
29         }
30 }

 

 评价:经过两次判定,第一次检测到实例为空时,增加同步,同步后再次检测到实例为空时,才创建对象实例。有效防止了在多线程环境下创建多个实例的问题。

 

5.内部类(推荐:高效)


 1 package sheJiMoShe;
 2 
 3 public class FSingleton {
 4    //静态内部内。(建议使用)
 5     /*
 6      * :定义一个私有的内部类,在第一次用这个嵌套类时,会创建一个实例。
 7      * 而类型为SingletonHolder的类,只有在Singleton.getInstance()中调用,由于私有的属性,他人无法使用SingleHolder,不调用Singleton.getInstance()就不会创建实例。
 8          优点:达到了lazy loading的效果,即按需创建实例。
 9      */
10    private FSingleton(){
11         
12     }
13     private static class SingletonHolder{
14         private final static FSingleton instance=new FSingleton();
15     }
16     public static FSingleton getInstance(){
17         return SingletonHolder.instance;
18     }
19 }

 6.枚举(推荐:高效)

 

在《Effective Java》最后推荐了这样一个写法,简直有点颠覆,不仅超级简单,而且保证了线程安全。这里引用一下,此方法无偿提供了序列化机制,绝对防止多次实例化,及时面对复杂的序列化或者反射攻击。单元素枚举类型已经成为实现Singleton的最佳方法。

1 package sheJiMoShe;
2 
3 public enum MySingleton {
4 
5     INSTANCE;
6 }

 

探究

对枚举法实现的单例模式很不理解的话。这里需要深入理解的是两个点:

  • 枚举类实现其实省略了private类型的构造函数
  • 枚举类的域(field)其实是相应的enum类型的一个实例对象

对于第一点实际上enum内部是如下代码:

1 public enum MySingleton {
2     INSTANCE;
3     // 这里隐藏了一个空的私有构造方法
4     private MySingleton () {}
5 }

 

一个标准的enum单例模式,最优秀的写法还是实现接口的形式:

 1 // 定义单例模式中需要完成的代码逻辑
 2 public interface MySingleton2 {
 3     void doSomething();
 4 }
 5 
 6 public enum Singleton implements MySingleton2 {
 7     INSTANCE {
 8         @Override
 9         public void doSomething() {
10            ...11         }
12     };
13 
14     public static MySingleton2 getInstance() {
15         return Singleton.INSTANCE;
16     }
17 }
18 

 

posted on 2018-11-29 23:19  源无极  阅读(185)  评论(0编辑  收藏  举报