Java使用代码模拟高并发操作

摘要:在java中,使用了synchronized关键字和Lock锁实现了资源的并发访问控制,在同一时间只允许唯一了线程进入临界区访问资源(读锁除外),这样子控制的主要目的是为了解决多个线程并发同一资源造成的数据不一致的问题。在另外一种场景下,一个资源有多个副本可供同时使用,比如打印机房有多个打印机、厕所有多个坑可供同时使用,这种情况下,Java提供了另外的并发访问控制--资源的多副本的并发访问控制,今天使用的Semaphore即是其中的一种。

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

1、Semaphore

JDK 1.5之后会提供这个类

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

2、CountDownLatch

JDK 1.5之后会提供这个类,

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

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

如下图:

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

packagemodules;importjava.util.concurrent.CountDownLatch;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.Semaphore;publicclassCountExample{//请求总数

publicstaticintclientTotal=5000;//同时并发执行的线程数

publicstaticintthreadTotal=200;publicstaticintcount=0;publicstaticvoidmain(String[]args)throwsException{

ExecutorServiceexecutorService=Executors.newCachedThreadPool();//信号量,此处用于控制并发的线程数

finalSemaphoresemaphore=newSemaphore(threadTotal);//闭锁,可实现计数器递减

finalCountDownLatchcountDownLatch=newCountDownLatch(clientTotal);for(inti=0;iclientTotal;i++){

executorService.execute(()-{try{ //执行此方法用于获取执行许可,当总计未释放的许可数不超过200时,

//允许通行,否则线程阻塞等待,直到获取到许可。

semaphore.acquire();

add();//释放许可

semaphore.release();

}catch(Exceptione){//log.error(exception,e);

e.printStackTrace();

}//闭锁减一

countDownLatch.countDown();

});

}

countDownLatch.await();//线程阻塞,直到闭锁值为0时,阻塞才释放,继续往下执行

executorService.shutdown();

log.info(count:{},count);

}privatestaticvoidadd(){

count++;

}

}

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

22:18:26.449[main]INFOmodules.CountExample-count:499722:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:499522:18:26.449[main]INFOmodules.CountExample-count:4998

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

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

privatestaticvoidadd(){

count.incrementAndGet();

}

执行结果如下:

22:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:5000

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


转载于:https://juejin.im/post/5bc6efbce51d451a3f4c6b6d

Java中,可以使用JUnit和JMeter等工具来测试单元并模拟高并发代码使用JUnit进行测试时,可以使用线程池或者多线程来模拟高并发场景。例如,使用Java中的ExecutorService和Callable接口来实现多线程并发测试。首先,定义一个Callable任务,然后将任务提交到线程池中执行,最后等待所有任务执行完毕并获取执行结果。具体代码如下所示: ``` public class MyCallable implements Callable<Integer> { @Override public Integer call() throws Exception { // 模拟业务逻辑 // ... return 1; } } public class MyTest { @Test public void testConcurrency() throws InterruptedException, ExecutionException { // 创建线程池 ExecutorService executorService = Executors.newFixedThreadPool(10); List<Future<Integer>> futureList = new ArrayList<>(); // 提交任务到线程池 for (int i = 0; i < 100; i++) { Future<Integer> future = executorService.submit(new MyCallable()); futureList.add(future); } // 等待所有任务执行完毕 for (Future<Integer> future : futureList) { future.get(); } // 关闭线程池 executorService.shutdown(); } } ``` 使用JMeter进行测试时,可以创建多个线程组和多个HTTP请求来模拟高并发场景。例如,创建10个线程组,每个线程组中有100个用户并发访问某个接口。具体步骤如下: 1. 创建一个Thread Group,设置线程数为100。 2. 添加一个HTTP请求,默认访问某个接口。 3. 复制Thread Group,创建10个线程组。 4. 启动测试。 在JMeter的测试报告中,可以查看每个接口的响应时间、吞吐量等指标。根据测试结果,可以优化代码,提高系统的并发处理能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值