你会用Java代码模拟高并发吗?

开发 后端
Java通过代码模拟高并发可以以最快的方式发现我们系统中潜在的线程安全性问题,此处使用Semaphore(信号量)和 CountDownLatch(闭锁)搭配ExecutorService(线程池)来进行模拟。

 [[271769]]

 

 

Java通过代码模拟高并发可以以最快的方式发现我们系统中潜在的线程安全性问题,此处使用Semaphore(信号量)和 CountDownLatch(闭锁)搭配ExecutorService(线程池)来进行模拟,主要介绍如下:

1、Semaphore

JDK 1.5之后会提供这个类

Semaphore是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做完自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态。

2、CountDownLatch

JDK 1.5之后会提供这个类,

CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。

CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

如下图:

你会用Java代码模拟高并发吗?

 

以上两个类可以搭配使用,达到模拟高并发的效果,以下使用代码的形式进行举例:

  1. package modules; 
  2. import java.util.concurrent.CountDownLatch; 
  3. import java.util.concurrent.ExecutorService; 
  4. import java.util.concurrent.Executors; 
  5. import java.util.concurrent.Semaphore; 
  6. public class CountExample { 
  7.  // 请求总数 
  8.  public static int clientTotal = 5000; 
  9.  // 同时并发执行的线程数 
  10.  public static int threadTotal = 200; 
  11.  public static int count = 0; 
  12.  public static void main(String[] args) throws Exception { 
  13.  ExecutorService executorService = Executors.newCachedThreadPool(); 
  14.  //信号量,此处用于控制并发的线程数 
  15.  final Semaphore semaphore = new Semaphore(threadTotal); 
  16.  //闭锁,可实现计数器递减 
  17.  final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); 
  18.  for (int i = 0; i < clientTotal ; i++) { 
  19.  executorService.execute(() -> { 
  20.  try { 
  21.     //执行此方法用于获取执行许可,当总计未释放的许可数不超过200时, 
  22.     //允许通行,否则线程阻塞等待,直到获取到许可。 
  23.  semaphore.acquire(); 
  24.  add(); 
  25.  //释放许可 
  26.  semaphore.release(); 
  27.  } catch (Exception e) { 
  28.  //log.error("exception", e); 
  29.  e.printStackTrace(); 
  30.  } 
  31.  //闭锁减一 
  32.  countDownLatch.countDown(); 
  33.  }); 
  34.  } 
  35.  countDownLatch.await();//线程阻塞,直到闭锁值为0时,阻塞才释放,继续往下执行 
  36.  executorService.shutdown(); 
  37.  log.info("count:{}"count); 
  38.  } 
  39.  private static void add() { 
  40.  count++; 
  41.  } 

如上方法模拟5000次请求,同时最大200个并发操作,观察最后的结果,发现每次的结果都有差别,和预期不符,得出结果部分如下:

  1. 22:18:26.449 [main] INFO modules.CountExample - count:4997 
  2. 22:18:26.449 [main] INFO modules.CountExample - count:5000 
  3. 22:18:26.449 [main] INFO modules.CountExample - count:4995 
  4. 22:18:26.449 [main] INFO modules.CountExample - count:4998 

最后结论:add 方法 非线程安全

那如何保证add方法 线程安全,将add方法进行如下修改即可:

  1. private static void add() { 
  2.  count.incrementAndGet(); 

执行结果如下:

  1. 22:18:26.449 [main] INFO modules.CountExample - count:5000 
  2. 22:18:26.449 [main] INFO modules.CountExample - count:5000 
  3. 22:18:26.449 [main] INFO modules.CountExample - count:5000 
  4. 22:18:26.449 [main] INFO modules.CountExample - count:5000 
  5. 22:18:26.449 [main] INFO modules.CountExample - count:5000 
  6. 22:18:26.449 [main] INFO modules.CountExample - count:5000 
  7. 22:18:26.449 [main] INFO modules.CountExample - count:5000 
  8. 22:18:26.449 [main] INFO modules.CountExample - count:5000 

最后结论:修改后 的 add 方法 线程安全

责任编辑:庞桂玉 来源: 今日头条
相关推荐

2018-09-29 15:34:34

JavaList接口

2020-06-04 14:15:55

Java中BigDecimal函数

2021-08-11 10:00:51

缓存MyBatis管理

2021-09-06 10:42:18

Linux命令服务器

2023-12-01 11:13:50

JavaTreeSet

2021-05-21 12:36:16

限流代码Java

2024-03-06 08:15:03

@Autowired注入方式Spring

2019-01-28 17:42:33

Python数据预处理数据标准化

2023-12-07 08:13:58

Java开发

2021-01-28 09:55:50

while(1)for(;;)Linux

2021-03-17 11:16:58

while(1)for(;;)语言

2020-11-09 09:03:35

高并发多线程ThreadLocal

2017-02-20 07:47:04

缓存HASH高并发

2020-12-28 08:25:08

Java并发HashMap

2020-06-29 08:32:21

高并发程序员流量

2022-02-10 09:04:50

架构

2024-04-08 00:00:00

asyncawaiPromise

2018-12-20 09:30:59

分布式高并发多线程

2022-11-07 17:50:36

2023-10-24 08:53:24

FutureTas并发编程
点赞
收藏

51CTO技术栈公众号