avatar

懒汉单例模式线程安全

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

结构

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
文章链接: https://liuzhihang.com/2018/02/21/lazy-singleton-mode-thread-safe.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Notes

评论