懒汉单例模式线程安全

简介

一个类中只有一个实例, 且能够自行实例化提供这个实例, 同时提供全局访问的方法.

结构

1.构造私有化: 确保外部不能使用new直接创建对象
2.内部静态属性创建实例
3.对外公共静态获取对象方法

demo

/**
 * 单例模式
 * 1. 构造私有化: 确保外部不能使用new直接创建对象
 * 2. 内部静态属性创建实例
 * 3. 对外公共静态获取对象方法
 *
 * @author liuzhihang
 * @date 2018/3/27 17:45
 */
public class SingletonPattern {

    private SingletonPattern() {
    }

    private static SingletonPattern singletonPattern = null;

    public static SingletonPattern getSingletonPattern() {

        if (singletonPattern == null) {
            singletonPattern = new SingletonPattern();
        }

        return singletonPattern;
    }

}

分类

1.懒汉式: 懒汉模式, 项目启动时不生成对象, 而是在首次创建该对象的时候生成唯一实例

/**
 * 懒汉模式, 项目启动时不生成对象, 而是在首次创建该对象的时候生成唯一实例
 *
 * @author liuzhihang
 * @date 2018/4/2 16:24
 */
public class LazyPattern {

    private LazyPattern() {
    }

    private static LazyPattern lazyPattern = null;

    public static LazyPattern getLazyPattern() {

        try {
            if (lazyPattern == null) {
                // 模拟一系列耗时操作
                Thread.sleep(50);
                lazyPattern = new LazyPattern();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return lazyPattern;
    }

}

2.饿汉式: 项目启动时, 进行加载, 会导致项目启动较慢, 并且无论后面是否用到都会进行加载

/**
 *
 * 饿汉式单例模式
 * 项目启动时, 进行加载, 会导致项目启动较慢, 并且无论后面是否用到都会进行加载
 *
 * @author liuzhihang
 * @date 2018/4/2 18:44
 */
public class HungerPattern {

    private HungerPattern() {
    }

    private static HungerPattern hungerPattern = new HungerPattern();

    public static HungerPattern getHungerPattern() {
        return hungerPattern;
    }
}

测试用例

在多线程情况下对单例模式进行测试:

/**
 * @author liuzhihang
 * @date 2018/3/27 19:02
 */
public class SingletonTest {

    public static void main(String[] args) {

        ThreadTest[] threadTests = new ThreadTest[10];
        for (int i = 0; i < threadTests.length; i++) {
            threadTests[i] = new ThreadTest();
        }

        for (int i = 0; i < threadTests.length; i++) {
            threadTests[i].start();
        }
    }
}

class ThreadTest extends Thread {

    @Override
    public void run() {
        // 懒汉模式
        System.out.println(LazyPattern.getLazyPattern().hashCode());
        // 饿汉模式
        // System.out.println(HungerPattern.getHungerPattern().hashCode());
    }
}

结果:

1.饿汉模式

D:\jdk1.8\bin\java.exe . . .
1294123621
1294123621
1294123621
1294123621
1294123621
1294123621
1294123621
1294123621
1294123621
1294123621

Process finished with exit code 0

2.懒汉模式

D:\jdk1.8\bin\java.exe . . .
140919816
1359128134
1385166630
924507082
67641385
508832262
574926395
140919816
1442414714
896298396

Process finished with exit code 0

结论: 在懒汉单例模式下不能保证线程的安全性

懒汉模式的线程安全优化

饿汉模式会造成资源浪费, 启动慢等结果, 下面对懒汉模式进行线程安全优化.

synchronized 锁住静态方法

锁住静态方法 类级锁 影响范围较大, 导致效率相对较低

/**
 * 懒汉式
 * 在方法上添加 synchronized 关键字 锁类
 * 同步方法的方式, 导致效率相对较低
 *
 * @author liuzhihang
 * @date 2018/4/3 14:27
 */
public class SyncLazyPattern {

    private SyncLazyPattern() {
    }

    private static SyncLazyPattern syncLazyPattern = null;

    public static synchronized SyncLazyPattern getSyncLazyPattern() {

        try {
            if (syncLazyPattern == null) {
                Thread.sleep(100);
                syncLazyPattern = new SyncLazyPattern();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return syncLazyPattern;
    }
}

synchronized 锁住代码块

package com.liuzhihang.demo.singleton;

/**
 * 锁代码块的方式虽然可以保证结果一致性
 * 但锁住很多操作, 同样会导致效率低下
 *
 * @author liuzhihang
 * @date 2018/4/3 15:22
 */
public class SyncCodeBlockLazyPattern {

    private SyncCodeBlockLazyPattern() {
    }

    private static SyncCodeBlockLazyPattern syncCodeBlockLazyPattern = null;

    public static SyncCodeBlockLazyPattern getSyncCodeBlockLazyPattern() {

        try {
            // 锁住具体执行业务逻辑的代码
            synchronized (SyncCodeBlockLazyPattern.class) {
                if (syncCodeBlockLazyPattern == null) {
                    Thread.sleep(100);
                    syncCodeBlockLazyPattern = new SyncCodeBlockLazyPattern();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return syncCodeBlockLazyPattern;
    }
}

双重检查锁机制(推荐)

package com.liuzhihang.demo.singleton;

/**
 * 双重锁检查机制, 仅锁住创建对象的部分代码
 * 注意: 在对象前 添加 volatile 关键字 确保可见性, 即 每次获取值从主内存中获取, 同时防止指令重排序
 *
 * @author liuzhihang
 * @date 2018/4/3 15:29
 */
public class DubboCheckLockLazyPattern {

    private DubboCheckLockLazyPattern() {
    }

    private static volatile DubboCheckLockLazyPattern dubboCheckLockLazyPattern = null;

    public static DubboCheckLockLazyPattern getDubboCheckLockLazyPattern() {

        try {
            if (dubboCheckLockLazyPattern == null) {
                // 一系列操作
                Thread.sleep(100);
                synchronized (DubboCheckLockLazyPattern.class) {
                    // 二次检查
                    if (dubboCheckLockLazyPattern == null) {
                        dubboCheckLockLazyPattern = new DubboCheckLockLazyPattern();
                    }
                }

            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return dubboCheckLockLazyPattern;
    }
}

   转载规则


《懒汉单例模式线程安全》 liuzhihang 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录