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

  

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

  

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

  

<强> 1,信号量

  

JDK 1.5之后会提供这个类

  

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

  

<强> 2,CountDownLatch

  

, JDK 1.5之后会提供这个类,

  

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

  

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

  

如下图:

  

癑ava使用代码模拟高并发操作的示例"

  

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

        包模块;   进口java.util.concurrent.CountDownLatch;   进口java.util.concurrent.ExecutorService;   进口java.util.concurrent.Executors;   进口java.util.concurrent.Semaphore;   公开课CountExample {//请求总数   公共静态int clientTotal=5000;//同时并发执行的线程数   公共静态int threadTotal=200;   公共静态int数=0;   公共静态void main (String [] args){抛出异常   ExecutorService ExecutorService=Executors.newCachedThreadPool ();//信号量,此处用于控制并发的线程数   最终信号信号量=new信号量(threadTotal);//闭锁,可实现计数器递减   最后CountDownLatch CountDownLatch=new CountDownLatch (clientTotal);   for (int i=0;我& lt;clientTotal;我+ +){   executorService.execute (()→{   尝试{//执行此方法用于获取执行许可,当总计未释放的许可数不超过200时,//允许通行,否则线程阻塞等待,直到获取到许可。   semaphore.acquire ();   add ();//释放许可   semaphore.release ();   }捕捉(异常e) {//日志。错误(“例外”,e);   e.printStackTrace ();   }//闭锁减一   countDownLatch.countDown ();   });   }   countDownLatch.await();//线程阻塞,直到闭锁值为0时,阻塞才释放,继续往下执行   executorService.shutdown ();   log.info(“数:{}”,数);   }   私有静态孔隙add () {   数+ +;   }   }      之前      

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

  
  

22:18:26.449[主要]信息模块。CountExample -数:4997
  22:18:26.449[主要]信息模块。CountExample -数:5000
  22:18:26.449[主要]信息模块。CountExample -数:4995
  22:18:26.449[主要]信息模块。CountExample -数:4998
  

     

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

  

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

        私有静态孔隙add () {   count.incrementAndGet ();   }      

执行结果如下:

  
  

22:18:26.449[主要]信息模块。CountExample -数:5000
  22:18:26.449[主要]信息模块。CountExample -数:5000
  22:18:26.449[主要]信息模块。CountExample -数:5000
  22:18:26.449[主要]信息模块。CountExample -数:5000

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