CountDownLatch
-
CountDownLatch 主要有两个方法,当一个或多个线程调用 await 方法时,这些线程会阻塞。
-
其他线程调用 countDown 方法会将计数器减一(调用 countDown 方法的线程不会阻塞)。
-
当计数器的值变为 0 时,因 await 方法阻塞的线程会被唤醒,继续执行。
举个栗子:
package juc; /** * @author : 雪飞oubai * @date : 2020/3/26 13:08 * 例:6个同学陆续离开教室后 值班的班长才可以关门离开。 */ public class CountDownLatchDemo { public static void main(String[] args) { for (int i = 0; i < 6; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName()+"离开了教室"); },String.valueOf(i)).start(); } System.out.println("main 班长关门走人"); } }
这样的结果显然不是我们想要的,那么我们改怎么让进程按我们想的顺序执行呢?这就要用到 CountDownLatch
package juc; import java.util.concurrent.CountDownLatch; /** * @author : 雪飞oubai * @date : 2020/3/26 13:08 */ public class CountDownLatchDemo { public static void main(String[] args) { CountDownLatch countDownLatch = new CountDownLatch(6); try { for (int i = 0; i < 6; i++) { new Thread(() -> { countDownLatch.countDown(); System.out.println(Thread.currentThread().getName()+"离开了教室"); },String.valueOf(i)).start(); } countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("main 班长关门走人"); } }
CyclicBarrier
-
它的字面意思是可循环(Cyclic)使用的屏障(Barrier),用法像 集齐七颗龙珠可以许愿一样。
-
让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被拦截的线程才会继续干活。
-
线程进入屏障通过 CyclicBarrier 的 await 方法。
举个栗子:
package juc; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * @author : 雪飞oubai * @date : 2020/3/26 14:14 */ public class CyclicBarrierDemo { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(7,() -> { System.out.println("七龙珠收集齐了,召唤神龙,你可以许愿了"); }); for (int i = 0; i < 7; i++) { new Thread(() -> { try { System.out.println("拿到第"+Thread.currentThread().getName()+"颗龙珠"); cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } },String.valueOf(i)).start(); } } }
CountDownLatch 和 CyclicBarrier 区别:
-
CountDownLatch 是一个计数器,线程完成一个记录一个,计数器递减,只能只用一次
-
CyclicBarrier的计数器更像一个阀门,需要所有线程都到达,然后继续执行,计数器递增,提供 reset 功能,可以多次使用。
Semaphore
在信号量上我们可以定义两种操作:
-
acquire(获取)当一个线程调用 acquire 操作时,它要么通过,成功回去信号量(信号量减一),要么一直等下去,直到有线程释放信号量,或超时。
-
release(释放)实际上会将信号量的值加一,然后唤醒等待的线程。
-
信号量主要用于两个目的,一个用于多个共享资源的互斥访问,另一个用于并发线程数的控制。
举个例子:(抢车位)
package juc; import java.util.concurrent.; import java.util.concurrent.TimeUnit; /** * @author : 雪飞oubai * @date : 2020/3/26 22:58 */ public class SemaphoreDemo { public static void main(String[] args) { Semaphore semaphore = new Semaphore(3); // 模拟资源类 for (int i = 0; i < 6; i++) { new Thread(() -> { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + "\t抢到车位!"); TimeUnit.SECONDS.sleep(3); // 暂停一会线程 System.out.println(Thread.currentThread().getName() + "\t离开了到车位!"); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }, String.valueOf(i)).start(); } } }
注意:
//Semaphore semaphore = new Semaphore(3); // 模拟资源类 // 若我们写成 Semaphore semaphore = new Semaphore(1); // 模拟资源类 //这个就相当于 在方法上加上 synchronized //若有要求:一个线程抢占资源后,停几秒钟,我们就可以这样。