这几天写到一个监听session的功能,其中涉及到了单例模式的线程安全,但是最开始的代码同事说有问题,代码如下:
1 private static SessionManager instance; 2 3 public static SessionManager getInstance() { 4 if (instance == null) { 5 instance = new SessionManager(); 6 } 7 return instance; 8 }
这种单例就是我记得的懒汉单例,这种单例模式是线程不安全的,即:在多线程时,并不能保证这个类只被实例化一次。解决这个问题需要加锁,代码如下:
1 private static SessionManager instance; 2 3 public static synchronized SessionManager getInstance() { 4 if (instance == null) { 5 instance = new SessionManager(); 6 } 7 return instance; 8 }
通过增加synchronized关键字到getInstance()方法中,迫使每个线程在进入方法之前,要先等别的线程离开该方法。也就是说,不会有两个线程可以同时进入这个方法。
这种方法存在的问题:当设置好instance变量后,就不再需要同步这个方法了,之后每次调用这个方法,同步都是一种浪费。
所以我们可以在getInstance中进行加锁判断,代码如下:
1 private static SessionManager instance; 2 3 public static SessionManager getInstance() { 4 if (instance == null) { 5 synchronized (SessionManager.class) { 6 if (instance == null) { 7 instance = new SessionManager(); 8 } 9 } 10 } 11 return instance; 12 }
这种方法据说会因为java的无序写入而失效,所以还是建议使用饿汉单例,代码如下:
1 private static SessionManager instance = new SessionManager(); 2 3 public static SessionManager getInstance() { 4 return instance; 5 }
使用这种方法,JVM在加载这个类时会马上创建唯一的单例实例,所以是线程安全的。