java.util.concurrent相关类理解

java.util.concurrent常用类

    • 1.ReenTrantLock
    • 2.Semaphore信号量
    • 3.CountDownLatch计数器
    • 4.CyclicBarrier循环屏障

1.ReenTrantLock

之前我们已经知道加锁有两种方式synchronized自动加锁和Lock手动加锁,手动加锁可以通过Lock的实现类ReentrantLock去实现,那么使用它需要注意什么问题呢?我们再来简单回顾一下哎:

a.lock操作要写在try之前;如果lock写在try里面,可能产生引发的锁状态异常将原本的业务异常覆盖掉,增加了调试困难。
b.使用了lock之后,一定要在finally中释放锁unlock,否则锁被一直占用,会出现死锁问题。

2.Semaphore信号量

首先看一下源码: java.util.concurrent相关类理解_第1张图片

Semaphore应用:如秒杀系统,允许前多少用户,后面的用户阻塞

Semaphore代码演示

public class TestDemo1 {
     
    public static void main(String[] args) {
     
        /*
        这里以停车场作为例子
         */
        //只传入int创建非公平同步锁  传入两个参数int boolean创建公平同步锁
        Semaphore semaphore = new Semaphore(2);  //可以根据实际情况限流,2代表停车位数量

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,10,0, TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100));
        
        for(int i = 0; i < 4; i++){
     
            //创建任务
            threadPoolExecutor.submit(new Runnable() {
     
                @Override
                public void run() {
     
                    System.out.println(Thread.currentThread().getName()+"到达停车场");
                    try {
     
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
     
                        e.printStackTrace();
                    }

                    //试图进入停车场
                    try {
     
                    //acquire会引发一个异常
                        semaphore.acquire(); 
                    } catch (InterruptedException e) {
     
                        e.printStackTrace();
                    }
                    //当代码执行到此处,说明已经获取到锁了
                    System.out.println(Thread.currentThread().getName()+"进入停车场");
                    int num = 1 + new Random().nextInt(5); //1-5
                    try {
     
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName()+"已经离开停车场了...");
                    } catch (InterruptedException e) {
     
                        e.printStackTrace();
                    }finally{
     
                    //必须释放锁,因此放在finally中
                        semaphore.release(); 
                    }
                }
            });
        }

    }
}

执行结果展示
java.util.concurrent相关类理解_第2张图片
对于Semaphore而言比较重要的三步是

//1.创建锁对象
Semaphore semaphore = new Semaphore();
//2.尝试获取锁
semaphore.acquire(); 
//3.释放锁
semaphore.release(); 

3.CountDownLatch计数器

作用:计数器是用来保证一组线程都完成某个操作之后,才会去执行后面的任务。

先根据源码看结构:
java.util.concurrent相关类理解_第3张图片
1)await():执行等待操作;当线程数量不满足CountDownLatch时,执行await()会阻塞等待,直到线程数量满足条件后,才去执行await()之后的代码。

2)countDown():计数

面试题:CountDownLatch是如何实现的?
在CountDownLatch里面存在一个计数器,每次调用countDown()时,计数器数量减1,直到计数器数量位0时,执行后面的代码。

计数器代码演示:

public class TestDemo2 {
     
    public static void main(String[] args) {
     
        CountDownLatch countDownLatch = new CountDownLatch(5); //计数器初始值为5

        for(int i = 1; i < 6; i++){
     
            int id = i;
            new Thread(new Runnable() {
     
                @Override
                public void run() {
     
                    System.out.println(Thread.currentThread().getName()+
                            "开始起跑...");
                    try {
     
                        Thread.sleep(id*1000);  //跑步时间
                    } catch (InterruptedException e) {
     
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+
                            "到达终点...");
                    countDownLatch.countDown(); //每到一个人,计数器减1,计数器为0时才会执行await后面的代码
                }
            }).start();
        }
        try {
     
            countDownLatch.await();
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }
        System.out.println("所有人到达重点,宣布排名");

    }
}

执行结果展示:

java.util.concurrent相关类理解_第4张图片
可以看出所有人都到达终点之后才能去宣布成绩

对于CountDownLatch而言重要的三步是

//1.创建计数器对象,声明计数器初始值
CountDownLatch countDownLatch = new CountDownLatch();
//2.计数器执行减1;减为0才执行await后面代码
countDownLatch.countDown();
//3.阻塞等待
countDown.await();

CountDownLatch缺点:

CountDownLatch的计数器是一次性的,使用完一次之后就不能再使用了。

将上面演示的代码任务数设置为10,而计数器初始值设定为5,我们看一下会发生什么?

java.util.concurrent相关类理解_第5张图片
这就印证了上面所说的CountDownLatch存在的缺点

4.CyclicBarrier循环屏障

CyclicBarrier的出现就是为了解决CountDownLatch计数器不能使用多次的问题

CyclicBarrier代码演示

public class TestDemo3 {
     

    public static void main(String[] args) {
     
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {
     
            @Override
            public void run() {
     
                System.out.println("执行了CyclicBarrier中的Runnable任务");
            }
        });

        for(int i = 1; i < 7; i++){
     
            int id = i;
            new Thread(new Runnable() {
     
                @Override
                public void run() {
     
                    System.out.println(Thread.currentThread().getName()+
                            "开始跑起来...");
                    try {
     
                        Thread.sleep(id*200);
                    } catch (InterruptedException e) {
     
                        e.printStackTrace();
                    }

                    try {
     
                        System.out.println(Thread.currentThread().getName()+"等待其他人。。。");
                        cyclicBarrier.await();   //计数器减1,判断计数器是否为0
                    } catch (InterruptedException e) {
     
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
     
                        e.printStackTrace();
                    }

                    //执行到这里,表明一组线程结束满足了条件
                    System.out.println(Thread.currentThread().getName()+
                            "执行结束");
                }
            }).start();
        }
    }
}

执行结果如下:

java.util.concurrent相关类理解_第6张图片
需要注意的问题

cyclicBarrier.await()作用:
1.让计数器减1
2.判断计数器是否为0,如果为0执行后面的代码,不为0则阻塞等待
PS:当计数器为0时,首先会执行await()之后的代码,然后将计数器重置为初始值

面试题:CyclicBarrier和CountDownLatch的区别是什么?
答:CyclicBarrier的计数器可以重复使用;而CountDownLatch的计数器只能使用一次。

你可能感兴趣的:(java,web,多线程,juc)