一、CountDownLatch
1.原理
它内部维护了一个计数器,该计数器初始化时设定一个数值,表示需要等待的线程数量。每个线程执行完特定任务后会调用CountDownLatch的countDown()方法,该方法会将计数器减一。同时,另外一个或多个线程可以通过await()方法来等待计数器变为0。一旦计数器为0,所有等待的线程就会被唤醒,继续执行。
2.主要方法
3.使用步骤
1.创建CountDownLatch对象,并指定需要等待的线程数量。
2.在需要等待的线程中调用await()方法进行等待。
3.在其他线程中执行任务完成后,调用countDown()方法减少计数器。
//定时计数器大小6 当计数器为0的时间就会释放所有等待线程
final CountDownLatch latch = new CountDownLatch(6);
for (int i = 1; i < 20; i++) {
int finalI = i;
new Thread(()->{
if (finalI % 2 == 0){
try {
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else {
//减小计数器
latch.countDown();
}
System.out.println(Thread.currentThread().getName() + "进门");
},String.valueOf(i)).start();
}
try {
//线程等待
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "出门购物");
二、CvclicRarrier
1.原理
它允许一组线程在达到一个共同的屏障点(barrier point)之前互相等待。它的工作原理是,当所有线程都到达屏障点时,所有线程被释放并继续执行。
与CountDownLatch不同,CyclicBarrier可以被重用,因为它的计数器在达到零后会被重置为初始值,并且可以在构造函数中指定一个Runnable任务,当计数器达到零时,该任务会被执行。
2.主要方法
3.使用步骤
1.创建CyclicBarrier对象:构造函数需要传入两个参数:参与同步的线程数量以及可选的Runnable任务(当所有线程到达屏障点时执行)
2.创建并启动参与同步的线程: 这些线程应该在某个阶段需要等待其他线程一起执行后续操作时调用CyclicBarrier的await()方法
3.等待所有线程到达屏障点: 当所有参与同步的线程都调用了await()方法后,它们会在屏障点处被阻塞,直到所有线程都到达屏障点。一旦所有线程都到达屏障点,CyclicBarrier将会执行可选的Runnable任务(如果提供了),然后释放所有被阻塞的线程继续执行后续操作。
CyclicBarrier cyclicBarrier = new CyclicBarrier(8, () -> {
System.out.println("召唤神龙成功");
});
for (int i=1;i<8;i++){
int finalI = i;
new Thread(()->{
System.out.println(Thread.currentThread().getName() + "收集" + finalI + "龙魂珠");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
},String.valueOf(i)).start();
}
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
System.out.println("孙悟空开始许愿!!!");
三、Semaphore
1.原理
一种用于控制对共享资源的访问的同步原语。它可以用于限制同时访问某一资源的线程数量,或者用于在多个线程之间进行流量控制。
acquire(): 当一个线程希望访问共享资源时,它需要调用 acquire() 方法来获取信号量。如果当前信号量计数为正,则线程可以继续执行,并且信号量计数减一;如果当前计数为零,则线程会被阻塞,直到有其他线程释放信号量。
release(): 当一个线程完成对共享资源的访问时,它需要调用 release() 方法来释放信号量。这会将信号量计数加一,并且唤醒等待的线程。
2.主要方法
3.使用步骤(可以作用于限流)
1.创建 Semaphore 对象:创建一个 Semaphore 对象。在创建 Semaphore 对象时,您需要指定信号量的初始数量,这决定了能够同时访问共享资源的线程数量。
2.获取信号量:当一个线程希望访问共享资源时,它需要调用 acquire() 方法来获取信号量。如果当前信号量计数为正,则线程可以继续执行,并且信号量计数减一;如果当前计数为零,则线程会被阻塞,直到有其他线程释放信号量。
3.访问共享资源:一旦线程成功获取了信号量,它就可以访问共享资源了。在这个阶段,线程可以执行需要访问共享资源的操作。
4.释放信号量:当一个线程完成对共享资源的访问时,它需要调用 release() 方法来释放信号量。这会将信号量计数加一,并且唤醒等待的线程。
//线程数量 三个停车位
Semaphore semaphore = new Semaphore(3);
//六辆车
for (int i = 1; i < 7; i++) {
new Thread(()->{
//acquire() 获取车位
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "找到停车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "离开停车位");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
//release()释放 离开车位 只有释放才可以让其他线程进入
semaphore.release();
}
},String.valueOf(i)).start();
}