java并发测试:线程安全与线程不安全,使用AtomicLong和LongAdder演示代码保证线程安全

  • Post author:
  • Post category:java




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 版权协议,转载请附上原文出处链接和本声明。