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();
}
}
}
最后得到结果如下
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 )