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时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

如下图:

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

package modules;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class CountExample {
  // 请求总数
  public static int clientTotal = 5000;
  // 同时并发执行的线程数
  public static int threadTotal = 200;
  public static int count = 0;
  public static void main(String[] args) throws Exception {
    ExecutorService executorService = Executors.newCachedThreadPool();
    //信号量,此处用于控制并发的线程数
    final Semaphore semaphore = new Semaphore(threadTotal);
    //闭锁,可实现计数器递减
    final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
    for (int i = 0; i < clientTotal ; i++) {
      executorService.execute(() -> {
        try {
         //执行此方法用于获取执行许可,当总计未释放的许可数不超过200时,
         //允许通行,否则线程阻塞等待,直到获取到许可。
          semaphore.acquire();
          add();
          //释放许可
          semaphore.release();
        } catch (Exception e) {
          //log.error("exception", e);
          e.printStackTrace();
        }
        //闭锁减一
        countDownLatch.countDown();
      });
    }
    countDownLatch.await();//线程阻塞,直到闭锁值为0时,阻塞才释放,继续往下执行
    executorService.shutdown();
    log.info("count:{}", count);
  }
  private static void add() {
    count++;
  }
}

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

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

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

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

private static void add() {
   count.incrementAndGet();
}

执行结果如下:

22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:5000

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

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

    Java通过代码模拟高并发可以以最快的方式发现我们系统中潜在的线程安全性问题,此处使用Semaphore(信号量)和 CountDownLatch(闭锁)搭配ExecutorService(线程池)来进行模拟,主要介绍如下: 1.Semaphore JDK 1.5之后会提供这个类 Semaphore是一种基于计数的信号量.它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做完自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞.Semaphore可以用来构建一些对象池,资源池之类的,比如

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

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

  • Java利用Redis实现高并发计数器的示例代码

    业务需求中经常有需要用到计数器的场景:譬如一个手机号一天限制发送5条短信.一个接口一分钟限制多少请求.一个接口一天限制调用多少次等等.使用Redis的Incr自增命令可以轻松实现以上需求.以一个接口一天限制调用次数为例: /** * 是否拒绝服务 * @return */ private boolean denialOfService(String userId){ long count=JedisUtil.setIncr(DateUtil.getDate()+"&"+user

  • Java使用JMeter进行高并发测试

    目录 常见的压力测试工具: 使用JMeter进行高并发测试 1.下载并安装JMeter 2.使用JMeter进行测试 3.生成测试报告 4.分析测试报告 软件的压力测试是一种保证软件质量的行为.在金融,电商等领域应用比较普遍.通俗的讲,压力测试即在一定的硬性条件下,模拟大批量用户对软件系统进行高负荷测试.需要注意的是,压力测试的目的不是为了让软件变得完美无瑕,而是通过压力测试,测试出软件的负荷极限,进而重新优化性能或在实际的应用环境中控制风险. 常见的压力测试工具: 1. LoadRunner:

  • Java 高并发五:JDK并发包1详细介绍

    在[高并发Java 二] 多线程基础中,我们已经初步提到了基本的线程同步操作.这次要提到的是在并发包中的同步控制工具. 1. 各种同步控制工具的使用 1.1 ReentrantLock ReentrantLock感觉上是synchronized的增强版,synchronized的特点是使用简单,一切交给JVM去处理,但是功能上是比较薄弱的.在JDK1.5之前,ReentrantLock的性能要好于synchronized,由于对JVM进行了优化,现在的JDK版本中,两者性能是不相上下的.如果是简

  • java的多线程高并发详解

    1.JMM数据原子操作 read(读取)∶从主内存读取数据 load(载入):将主内存读取到的数据写入工作内存 use(使用):从工作内存读取数据来计算 assign(赋值):将计算好的值重新赋值到工作内存中 store(存储):将工作内存数据写入主内存 write(写入):将store过去的变量值赋值给主内存中的变量 lock(锁定):将主内存变量加锁,标识为线程独占状态 unlock(解锁):将主内存变量解锁,解锁后其他线程可以锁定该变量 2.来看volatile关键字 (1)启动两个线程

  • Java高并发测试框架JCStress详解

    前言 如果要研究高并发,一般会借助高并发工具来进行测试.JCStress(Java Concurrency Stress)它是OpenJDK中的一个高并发测试工具,它可以帮助我们研究在高并发场景下JVM,类库以及硬件等状况. JCStress学起来很简单,而且官方也提供了许多高并发场景下的测试用例,只要引入一个jar包,即可运行研究. 如何使用JCStress 此演示用maven工程,首先需要引入jar包,核心包是必须要的,样例包非必须要,此是为了演示其中的例子. <dependencies>

  • Redis锁完美解决高并发秒杀问题

    目录 1 单机环境下的锁 2 分布式情况下使用Redis锁. 3 一台服务宕机,导致无法释放锁 4 给每一把锁加上过期时间 5延长锁的过期时间,解决锁失效 6 使用Redisson简化代码 场景:一家网上商城做商品限量秒杀. 1 单机环境下的锁 将商品的数量存到Redis中.每个用户抢购前都需要到Redis中查询商品数量(代替mysql数据库.不考虑事务),如果商品数量大于0,则证明商品有库存.然后我们在进行库存扣减和接下来的操作.因为多线程并发问题,我们不得不在get()方法内部使用同步代码块

  • PHP+MySQL高并发加锁事务处理问题解决方法

    本文实例讲述了PHP+MySQL高并发加锁事务处理问题解决方法.分享给大家供大家参考,具体如下: 1.背景: 现在有这样的需求,插入数据时,判断test表有无username为'mraz'的数据,无则插入,有则提示"已插入",目的就是想只插入一条username为'mraz'的记录. 2.一般程序逻辑如下: $conn = mysqli_connect('127.0.0.1', 'root', '111111') or die(mysqli_error()); mysqli_selec

  • php处理抢购类功能的高并发请求

    本文以抢购.秒杀为例.介绍如何在高并发状况下确保数据正确. 在高并发请求下容易参数两个问题 1.数据出错,导致产品超卖. 2.频繁操作数据库,导致性能下降. 测试环境 Windows7 apache2.4.9 php5.5.12 php框架 yii2.0 工具 apache bench (apache自带高并发请求工具). 通常处理方法 从控制器可以看出代码思路.先查询商品库存.如果库存大于0 ,则库存减少1,同时生产订单,录入抢购者数据. // 常规代码处理高并发 public functio

随机推荐