前几天去某云面试,面试官问到单例模式,天然陈词滥调的,最初面试官问我,有没有理解过多线程模式下的单例模式?我就懵了,接触最多的PHP
和JS
都是单线程的,之前也没思考过线程平安的问题,回来查了查资料,特总结如下:(PS:下文java
代码来自安卓共事解说)
1.线程平安
多个线程拜访同一个对象时,如果不必思考这些线程在运行时环境下的调度和交替执行,也不须要进行额定的同步,或者在调用方进行任何其余操作,调用这个对象的行为都能够取得正确的后果,那么这个对象就是线程平安的。
或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行后果存在二义性,也就是说咱们不必思考同步的问题。
线程平安问题大多是由全局变量及动态变量引起的,局部变量逃逸也可能导致线程平安问题。
若每个线程中对全局变量、动态变量只有读操作,而无写操作,一般来说,这个全局变量是线程平安的;若有多个线程同时执行写操作,个别都须要思考线程同步,否则的话就可能影响线程平安。
2.线程不平安的单例
不平安的单例就是动态单例在多线程同时首次援用此类时,可能创立多个实例,咱们平时写的最多的单例模式是懒汉式单例,即只有首次执行getInstance()
时,才创立单例。例如以下代码:
//javascriptvar H5SdkLog = (() => { function _module() { this.instance_id = Math.floor(Math.random()*100); } var _instance = null; return { getInstance:() => { if(!_instance){ _instance = new _module(); } return _instance; } }})();
//phpclass Utils{ private static $instance; //构造方法私有化,避免内部创立实例 private function __construct(){} public static function getInstance() { if(!(self::$instance instanceof self)){ self::$instance = new self(); } return self::$instance; } //克隆办法私有化,避免复制实例 private function __clone(){}}
//javapublic class SdkAppManagerUtils { private volatile static SdkAppManagerUtils instance; public static SdkAppManagerUtils getInstance() { if (instance == null) { instance = new SdkAppManagerUtils(); } return instance; }}
通常来讲,PHP
和JS
都是单线程的,应用懒汉式单例没有啥,毕竟未必用失去这个类,推延到应用时候再生成单例的思路是正确的,合乎编程思维的。但对于java
这种反对多线程的语言,就存在线程不平安,所以须要非凡解决。
3.线程平安的单例-饿汉式/同步锁
//饿汉式public class SdkAppManagerUtils { private volatile static SdkAppManagerUtils instance = new SdkAppManagerUtils(); public static SdkAppManagerUtils getAppManager() { return instance; }}
//同步锁public class SdkAppManagerUtils { private volatile static SdkAppManagerUtils instance; public static SdkAppManagerUtils getAppManager() { if (instance == null) { synchronized (SdkAppManagerUtils.class) { if (instance == null) { instance = new SdkAppManagerUtils(); } } } return instance; }}