线程安全的单例模式与并发模拟测试

## 一、单例模式实现
这里我们通过**懒汉模式**与**Double Check**实现线程安全的单例模式,代码如下:

```
public class Singleton {

    // 定义无参构造器
    private Singleton(){}

    // volatile保证多个线程从主存访问
    private static volatile Singleton singleton;

    public static Singleton getInstance(){
        // 判断对象是否已创建
        if (Objects.isNull(singleton)){
            // 锁住class对象 对象只有一个 因此只有一个线程拥有monitor锁
            synchronized (Singleton.class){
                // 再次判断对象是否已经创建 避免线程等待的时候 其他线程已经创建该对象
                if (Objects.isNull(singleton)){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
```
代码的具体含义已经在文中注释,这里我们补充说下,`synchronized`保证只有一个线程在对象为空时进行创建,`volatile`保证判断单例对象(`Singleton`)是否为空时的可见性,避免对象未初始化完成就返回。

线程安全的单例模式还可以通过**静态内部类**、**枚举**等方式实现,这里我们暂不详述。

## 二、并发模拟测试
这里我们使用线程池进行并发模拟,并通过`CompletableFuture`进行异步任务编排。

首先我们自定义线程池:
```
/**
 * @author winsonWu
 * @Description: thread pool creating util
 * @date Date : 2021.04.11 21:20
 */
@Configuration
public class ThreadPoolCreator {

    /**
     * 核心线程数
     * 如果执行CPU密集任务,则尽量不超过操作系统核数2倍
     * 如果IO密集型可适当加大
     * 具体设置根据压测结果决定
     */
    private static int corePoolSize = Runtime.getRuntime().availableProcessors() * 3;

    /**
     * 最大线程数 避免内存交换 设置为核心核心线程数
     */
    private static int maximumPoolSize = corePoolSize;

    /**
     * 最大空闲时间
     */
    private static long keepAliveTime = 1;

    /**
     * 最大空闲时间单位
     */
    private static TimeUnit unit = TimeUnit.HOURS;

    /**
     * 使用有界队列,避免内存溢出
     */
    private static BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(500);

    /**
     * 默认线程工厂,建议自定义,并设置线程名称获取规则
     */
    private static ThreadFactory threadFactory = Executors.defaultThreadFactory();

    /**
     * 拒绝策略
     */
    private static RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

    @Bean
    public ThreadPoolExecutor threadPoolExecutor(){
        return new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime, unit,
                workQueue,
                threadFactory,
                handler);
    }
}
```
接下来我们编写单元测试:
```
/**
 * @Author winsonWu
 * @Description: TODO
 * @date Date : 2022.04.11 21:57
 **/
@SpringBootTest
public class SingletonTest {

    @Resource
    private ThreadPoolExecutor threadPoolExecutor;

    @BeforeAll
    public static void prepareEnv(){

    }

    @Test
    public void testSingle(){
        List<CompletableFuture<Singleton>> futures = new ArrayList<>(1000);
        List<Singleton> singletons = new ArrayList<>(1000);
        for (int i=0; i<1000; i++){
            CompletableFuture<Singleton> future = CompletableFuture.supplyAsync(Singleton::getInstance, threadPoolExecutor);
            futures.add(future);
        }
        CompletableFuture
                .allOf(futures.toArray(new CompletableFuture[futures.size()]))
                .whenComplete((v, t)-> futures.forEach(eachFuture -> {
                    singletons.add(eachFuture.getNow(null));
                })).join();
        // 放入集合去重后 最终对象数量为1
        Assertions.assertEquals(singletons.stream().collect(Collectors.toSet()).size(), 1);
    }
}
```
执行单元测试:

![1649739965(1).png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c59335d732fa4cf4bda8ac93b4eefd6f~tplv-k3u1fbpfcp-watermark.image?)

断言`Assertions.assertEquals(singletons.stream().collect(Collectors.toSet()).size(), 1);
`通过。
## 三、参考资料
- [单例模式](https://baike.baidu.com/item/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/5946627?fr=aladdin)
- [CompletableFuture](https://nowjava.com/docs/java-jdk-14/api/java.base/java/util/concurrent/CompletableFuture.html)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值