CountDownLatch

        有这样一种情况,需要实现多个线程并发工作,但有一个线程必须要在其他线程执行结束之后才能执行。比如一个生活的例子,自习室有很多同学在自习,门卫关门必须要等到里面的人都离开完了才能关门!         CountDownLatch是Java并发包下的一个辅助类,是一种灵活的闭锁实现。可以把它看成是一个计数器,其内部维护着一个count计数,只不过对这个计数器的操作都是原子操作,同时只能有一个线程去操作这个计数器,CountDownLatch通过构造函数传入一个初始计数值,调用者可以通过调用CounDownLatch对象的cutDown()方法,来使计数减1;如果调用对象上的await()方法,那么调用者就会一直阻塞在这里,直到别人通过cutDown方法,将计数减到0,才可以继续执行。

示例

import java.util.concurrent.CountDownLatch;

/**
 * @Date 2020/7/19 0:04
 * @Author luopeng
 */
public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t离开!");
                countDownLatch.countDown();
            }, String.valueOf(i+1)).start();
        }
        countDownLatch.await();
        System.out.println("锁门------------------!");
    }
}

得到结果如下图

在这里插入图片描述

CyclicBarrier

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @Date 2020/7/19 0:11
 * @Author luopeng
 */
public class CyclicBarrierTest {
    public static void main(String[] args) {
        //定义一个CyclicBarrier, 等待的线程数为7个, 并且自带一个Runnable方法
        //所有线程执行await()后, 执行Runnable中的run方法
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7/*8*/, () -> {
            System.out.println("召唤神龙!");
        });
        for (int i = 0; i < 7; i++) {
            int finalI = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "收集第" + finalI + "颗龙珠!");
                try {
//                    cyclicBarrier.await();
                    //调用await() 方法,这个方法是阻塞方法,直到所有线程执行完
                    //如果对await的调用超时,或者await阻塞的线程被中断,那么Barrier就认为是打破了,
                    // 所有阻塞的await调用都将终止并抛出BrokenBarrierException。
                    cyclicBarrier.await(2, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i + 1)).start();
        }
    }

}

最终效果如图

在这里插入图片描述

如果等待超时则会抛出BrokenBarrierException的异常

在这里插入图片描述

Semaphore

Semaphore中管理着一组虚拟的许可(permit),许可的初始数量可通过构造函数来指定。在执行操作时可以首先获得许可(只要还有剩余的许可),并在使用以后释放许可。如果没有许可,那么acquire将阻塞直到有许可(或者直到被中断或者操作超时)。release方法将返回一个许可给信号量。计算信号量的一种简化形式是二值信号量,即初始值为1的Semaphore。二值信号量可以用作互斥体(mutex),并具备不可重入的加锁语义:谁拥有这个唯一的许可,谁就拥有可互斥锁。 下面是8辆汽车抢占三个车位的案例

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * @Date 2020/7/19 19:05
 * @Author luopeng
 */
public class SemaphoreTest {
    public static void main(String[] args) {
        //创建一个Semaphore对象,给定一个permits许可,当许可为0时阻塞线程
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 8; i++) {
            new Thread(() -> {
                try {
                    //尝试获取一个许可,获取成功许可减少1,
                    // 获取失败则阻塞直至许可不为0
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "\t抢占到了车位!");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"\t离开......");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //释放许可
                    semaphore.release();
                }
            }, String.valueOf(i+1)).start();
        }

    }
}

最后得到结果如下 8辆汽车抢占三个车位

TimeUnit

TimeUnit是Java并发包下面的一个类,表示给定单元粒度的时间段 主要作用是时间颗粒度转换、延时

常用的时间颗粒度

		TimeUnit.DAYS          //天
		TimeUnit.HOURS         //小时
		TimeUnit.MINUTES       //分钟
		TimeUnit.SECONDS       //秒
		TimeUnit.MILLISECONDS  //毫秒

时间颗粒度转换

        public long toMillis(long d)    //转化成毫秒
        public long toSeconds(long d)  //转化成秒
        public long toMinutes(long d)  //转化成分钟
        public long toHours(long d)    //转化成小时
        public long toDays(long d)     //转化天

例如:

		//1天有24个小时    1代表1天:将1天转化为小时
		System.out.println( TimeUnit.DAYS.toHours( 1 ) );

延时

		//延时五秒
		TimeUnit.SECONDS.sleep( 5 )