一个线程安全的高并发demo,6000个线程,并发量2000

写一个线程安全的高并发demo,6000个线程,并发量2000

JAVA并发包中有三个类用于同步一批线程的行为,分别是CountDownLatch、Semaphore和CyclicBarrier。

CountDownLatch

CountDownLatch是一个计数器闭锁,通过它可以完成类似于阻塞当前线程的功能,即:一个线程或多个线程一直等待,直到其他线程执行的操作完成。CountDownLatch用一个给定的计数器来初始化,该计数器的操作是原子操作,即同时只能有一个线程去操作该计数器。调用该类await方法的线程会一直处于阻塞状态,直到其他线程调用countDown方法使当前计数器的值变为零,每次调用countDown计数器的值减1。当计数器值减至零时,所有因调用await()方法而处于等待状态的线程就会继续往下执行。这种现象只会出现一次,因为计数器不能被重置,如果业务上需要一个可以重置计数次数的版本,可以考虑使用CycliBarrier。

CyclicBarrier

CyclicBarrier也是一个同步辅助类,它允许一组线程相互等待,直到到达某个公共屏障点(common barrier point)。通过它可以完成多个线程之间相互等待,只有当每个线程都准备就绪后,才能各自继续往下执行后面的操作。类似于CountDownLatch,它也是通过计数器来实现的。当某个线程调用await方法时,该线程进入等待状态,且计数器加1,当计数器的值达到设置的初始值时,所有因调用await进入等待状态的线程被唤醒,继续执行后续操作。因为CycliBarrier在释放等待线程后可以重用,所以称为循环barrier。CycliBarrier支持一个可选的Runnable,在计数器的值到达设定值后(但在释放所有线程之前),该Runnable运行一次,注,Runnable在每个屏障点只运行一个

Semaphore

Semaphore与CountDownLatch相似,不同的地方在于Semaphore的值被获取到后是可以释放的,并不像CountDownLatch那样一直减到底。它也被更多地用来限制流量,类似阀门的 功能。如果限定某些资源最多有N个线程可以访问,那么超过N个主不允许再有线程来访问,同时当现有线程结束后,就会释放,然后允许新的线程进来。有点类似于锁的lock与 unlock过程。相对来说他也有两个主要的方法:
用于获取权限的acquire(),其底层实现与CountDownLatch.countdown()类似;
用于释放权限的release(),其底层实现与acquire()是一个互逆的过程。

下面对上面说的三个辅助类进行一个总结:

  1. CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:
  2. CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;
  3. 另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。
  4. Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。

需求:模拟6000人抢购手机,并发量为2000。手机总共10000台,每人抢1~9台,抢完为止。


import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CyclicBarrierDemo {
    //定义商品总数
    static int HWMate30 = 10000;

    //定义需要等待的线程数(并发量),以及线程等待完成后执行的方法
    static CyclicBarrier barrier = new CyclicBarrier(2000,new Runnable() {

        @Override
        public void run() {
            System.out.println("2000人准备完毕,开始抢购>>>>>>>>>>>>>>>>");

        }
    });


     // 使用同步方法,保证线程安全
    public static synchronized void buy() {
        System.out.println(Thread.currentThread().getName()+"开始抢购...");
        //模拟抢购的数量,1~10台
        int i  = (int)(Math.random() * 9) + 1;
        if(HWMate30 >= i) {
            HWMate30 -= i;
            System.out.println(Thread.currentThread().getName()+":抢购成功"+i+"台,华为mate30还剩"+HWMate30+"台!");
        }else if (HWMate30 > 0 && HWMate30 < i) {
            System.out.println(Thread.currentThread().getName()+":华为mate30还剩"+HWMate30+"台,不够"+i+"台,请重新选择数量!");
        }else {
            System.out.println(Thread.currentThread().getName()+":华为mate30已卖完!");
        }
    }

    public static void main(String[] args) {
        //定义固定大小的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(6000);
        for(int i = 0;i < 6000;i++) {
            //启动线程
            executorService.submit(new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName()+":准备好了!");
                        //等待线程数量达到指定的数量才放行
                        barrier.await();
                    } catch (InterruptedException | BrokenBarrierException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    //调用抢购的方法
                    buy();
                }
            }));
        }
        //关闭线程池
        executorService.shutdown();
    }
}


执行结果:
一个线程安全的高并发demo,6000个线程,并发量2000_第1张图片

2000个线程准备完毕,开始抢购。接下来继续准备2000个线程

一个线程安全的高并发demo,6000个线程,并发量2000_第2张图片

如果剩余商品数量大于抢购的数量,抢购成功,商品数量减少。
如果剩余商品数量小于抢购的数量,抢购失败,需要重新选择数量再抢购。

一个线程安全的高并发demo,6000个线程,并发量2000_第3张图片

商品被抢完后,提示已卖完,不会出现超卖的情况。

你可能感兴趣的:(javaSE)