1.使用基础变量int(线程不安全)
package com.zr.concurrency.test;
import com.zr.concurrency.annotation.ThreadNotSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* @description: 并发测试,线程不安全
* @author: liangrui
* @create: 2019-12-29 12:31
**/
@Slf4j
@ThreadNotSafe
public class ConcurrencyTest {
//请求总数
private static int clientTotal=5000;
//同时并发执行的线程数
private static int threadTotal=200;
//计数
private static int count=0;
/**
* 每个线程执行一次
*/
private static void add(){
count++;
}
public static void main(String[] args) throws InterruptedException {
//创建线程池
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 {
semaphore.acquire();
//执行任务
add();
//线程释放
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 当前线程执行完才释放
countDownLatch.countDown();
}
);
}
//所有线程执行完才释放
countDownLatch.await();
//关闭线程池
executorService.shutdown();
//输出count值
log.info("count:{}",count);
}
}
输出结果:5000才是应该输出的值
18:28:31.377 [main] INFO com.zr.concurrency.test.ConcurrencyTest – count:4957
2.使用AtomicLong和LongAdder的代码(线程安全)
AtomicLong:
使用死循环不断地compareAndSwap到特定的值,从而达到更新数据的目的
缺点
:唯一会制约AtomicLong高效的原因是高并发,高并发意味着CAS的失败几率更高,重试次数更多,越多线程重试,CAS失败几率又越高,变成恶性循环,AtomicLong效率降低
LongAdder:
高并发时
:将对单一变量的CAS操作分散为对数组cells中多个元素的CAS操作,取值时进行求和;
并发较低时
:仅对base变量进行CAS操作,与AtomicLong类原理相同,不得不说这种分布式的设计还是很巧妙的。
缺点
: 在统计的时候,如果有并发更新,可能会导致统计数据有些误差
package com.zr.concurrency.test;
import com.zr.concurrency.annotation.ThreadNotSafe;
import com.zr.concurrency.annotation.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
/**
* @description: 并发测试,使用AtomicLong或者LongAdder,这样是线程安全的
* AtomicLong:使用死循环不断地compareAndSwap到特定的值,从而达到更新数据的目的
* 唯一会制约AtomicLong高效的原因是高并发,高并发意味着CAS的失败几率更高,
* 重试次数更多,越多线程重试,CAS失败几率又越高,变成恶性循环,AtomicLong效率降低
* LongAdder:
* 高并发时:将对单一变量的CAS操作分散为对数组cells中多个元素的CAS操作,取值时进行求和;
* 并发较低时:仅对base变量进行CAS操作,与AtomicLong类原理相同。
* 不得不说这种分布式的设计还是很巧妙的。
* 缺点: 在统计的时候,如果有并发更新,可能会导致统计数据有些误差
* @author: liangrui
* @create: 2019-12-29 12:31
**/
@Slf4j
@ThreadSafe
public class ConcurrencyTest2 {
//请求总数
private static int clientTotal=5000;
//同时并发执行的线程数
private static int threadTotal=200;
//计数从0开始
private static AtomicLong count=new AtomicLong(0);
//也可以使用LongAdder
private static LongAdder count2=new LongAdder();
/**
* 每个线程执行一次
*/
private static void add(){
count.incrementAndGet();
count2.increment();
}
public static void main(String[] args) throws InterruptedException {
//创建线程池
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 {
semaphore.acquire();
//执行任务
add();
//线程释放
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 当前线程执行完才释放
countDownLatch.countDown();
}
);
}
//所有线程执行完才释放
countDownLatch.await();
//关闭线程池
executorService.shutdown();
//输出count值
log.info("count:{}",count);
log.info("count2:{}",count2);
}
}
输出结果:都为5000,线程安全
18:30:12.479 [main] INFO com.zr.concurrency.test.ConcurrencyTest2 – count:5000
18:30:12.485 [main] INFO com.zr.concurrency.test.ConcurrencyTest2 – count2:5000
版权声明:本文为liangruilz原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。