在讲解多线程的单例时,先说一下单例
饿汉单例
饿汉单例相对比较好理解,就是在单例类加载的时候就初始化需要单例的对象
public class Singleton{ private static Stu stuObj = new Stu(); public static Log getInstance(){ return stuObj; } }
懒汉单例
懒汉模式,就是当你用到才初始化
public class Singleton{ private static Stu stuObj = null; public static Log getInstance(){ if(stuObj == null){ stuObj = new Stu(); } return stuObj; } }
1.方法中声明synchronized关键字
出现非线程安全问题,是由于多个线程可以同时进入getInstance()方法,那么只需要对该方法进行synchronized的锁同步即可:
package org.mlinge.s03; public class MySingleton { private static MySingleton instance = null; private MySingleton(){} public synchronized static MySingleton getInstance() { try { if(instance != null){//懒汉式 }else{ //创建实例之前可能会有一些准备性的耗时工作 Thread.sleep(300); instance = new MySingleton(); } } catch (InterruptedException e) { e.printStackTrace(); } return instance; } }
2.同步代码块实现
package org.mlinge.s03; public class MySingleton { private static MySingleton instance = null; private MySingleton(){} //public synchronized static MySingleton getInstance() { public static MySingleton getInstance() { try { synchronized (MySingleton.class) { if(instance != null){//懒汉式 }else{ //创建实例之前可能会有一些准备性的耗时工作 Thread.sleep(300); instance = new MySingleton(); } } } catch (InterruptedException e) { e.printStackTrace(); } return instance; } }
这里的实现能够保证多线程并发下的线程安全性,但是这样的实现将全部的代码都被锁上了,同样的效率很低下。
4. Double Check Locking 双检查锁机制(推荐)
为了达到线程安全,又能提高代码执行效率,我们这里可以采用DCL的双检查锁机制来完成,代码实现如下:
package org.mlinge.s05; public class MySingleton { //使用volatile关键字保其可见性 volatile private static MySingleton instance = null; private MySingleton(){} public static MySingleton getInstance() { try { if(instance != null){//懒汉式 }else{ //创建实例之前可能会有一些准备性的耗时工作 Thread.sleep(300); synchronized (MySingleton.class) { if(instance == null){//二次检查 instance = new MySingleton(); } } } } catch (InterruptedException e) { e.printStackTrace(); } return instance; } }
这里在声明变量时使用了volatile关键字来保证其线程间的可见性;在同步代码块中使用二次检查,以保证其不被重复实例化。集合其二者,这种实现方式既保证了其高效性,也保证了其线程安全性。