这里我们介绍一下在多线程中如何安全正确的编写单例模式的代码。不知为何,恰如其分的话总是姗姗来迟,错过最恰当的时机。
多线程中的单例模式
这里面通过代码来体会一下在多线程中如何正确的编写单例模式的代码。相同的代码如下,不同的是Object这个类。
package com.linux.huhx.thread3.singleDesign_1; /** * @Author: huhx * @Date: 2017-10-31 下午 4:28 */ public class SingleDesignTest1 { public static void main(String[] args) { MyThread[] threads = new MyThread[10]; for (int i = 0; i < 10; i++) { threads[i] = new MyThread(); } for (int i = 0; i < 10; i++) { threads[i].start(); } } static class MyThread extends Thread { @Override public void run() { System.out.println(MyObject*.getInstance().hashCode()); } } }
以下的不同测试类的结果,都是基于修改MyThread里面run方法的MyObject*的值。
一、立即加载方式(饿汉模式)
public class MyObject1 { private static MyObject1 myObject = new MyObject1(); private MyObject1() {} public static MyObject1 getInstance() { return myObject; } }
安全:一次的打印结果如下
1508118770 1508118770 1508118770 1508118770 1508118770 1508118770 1508118770 1508118770 1508118770 1508118770
二、延迟加载方式(懒汉模式)
public class MyObject2 { private static MyObject2 myObject; private MyObject2() {} public static MyObject2 getInstance() { try { if (myObject == null) { // 模拟一些准备的耗时操作 TimeUnit.SECONDS.sleep(2); myObject = new MyObject2(); } } catch (InterruptedException e) { e.printStackTrace(); } return myObject; } }
不正确:一次的打印结果
401241377 355159803 1875573029 797782832 1696014491 1060834655 961745937 1060834655 1312341120 985396398
三、延迟加载解决方案之声明synchronized
public class MyObject3 { private static MyObject3 myObject; private MyObject3() {} public synchronized static MyObject3 getInstance() { try { if (myObject == null) { // 模拟一些准备的耗时操作 TimeUnit.SECONDS.sleep(2); myObject = new MyObject3(); } } catch (InterruptedException e) { e.printStackTrace(); } return myObject; } }
安全:一次的打印结果
774263507 774263507 774263507 774263507 774263507 774263507 774263507 774263507 774263507 774263507
效率比较低下:同步运行,下一个线程想要取得对象,则必须等待上一个线程释放锁之后,才可以继续执行。
四、延迟加载解决方案之同步代码块
public class MyObject4 { private static MyObject4 myObject; private MyObject4() {} public static MyObject4 getInstance() { try { synchronized (MyObject4.class) { if (myObject == null) { // 模拟一些准备的耗时操作 TimeUnit.SECONDS.sleep(2); myObject = new MyObject4(); } } } catch (InterruptedException e) { e.printStackTrace(); } return myObject; } }
安全:一次的打印结果如下
1373650392 1373650392 1373650392 1373650392 1373650392 1373650392 1373650392 1373650392 1373650392 1373650392
效率比较低下:和上述的synchronized同步方法一样都是同步运行的。
五、延迟加载解决方案之同步部分代码块
public class MyObject5 { private static MyObject5 myObject; private MyObject5() {} public static MyObject5 getInstance() { try { if (myObject == null) { // 模拟一些准备的耗时操作 TimeUnit.SECONDS.sleep(2); synchronized (MyObject5.class) { myObject = new MyObject5(); } } } catch (InterruptedException e) { e.printStackTrace(); } return myObject; } }
不安全:一次的打印结果如下
401241377 1696014491 797782832 1875573029 1060834655 43626537 1971360294 1312341120 961745937 860826410
六、延迟加载解决方案之DCL双检查锁机制
public class MyObject6 { private volatile static MyObject6 myObject; private MyObject6() {} public static MyObject6 getInstance() { try { if (myObject == null) { // 模拟一些准备的耗时操作 TimeUnit.SECONDS.sleep(2); synchronized (MyObject6.class) { if (myObject == null) { myObject = new MyObject6(); } } } } catch (InterruptedException e) { e.printStackTrace(); } return myObject; } }
安全:一次的打印结果如下
1312341120 1312341120 1312341120 1312341120 1312341120 1312341120 1312341120 1312341120 1312341120 1312341120
使用双重检查锁功能,成功地解决了“懒汉模式”遇到多线程的问题。DCL也是大多数多线程结合单例模式使用的解决方案。
七、使用静态内置类实现单例模式
public class MyObject7 { private static class MyObjectHandler { private static MyObject7 myObject = new MyObject7(); } private MyObject7() {} public static MyObject7 getInstance() { return MyObjectHandler.myObject; } }
安全:一次的打印结果如下
1240673261 1240673261 1240673261 1240673261 1240673261 1240673261 1240673261 1240673261 1240673261 1240673261
八、使用static代码块实现单例模式
public class MyObject8 { private static MyObject8 myObject = null; static { myObject = new MyObject8(); } private MyObject8() {} public static MyObject8 getInstance() { return myObject; } }
安全:一次的打印结果如下
1875573029 1875573029 1875573029 1875573029 1875573029 1875573029 1875573029 1875573029 1875573029 1875573029