CoutDownLatch
-
让一些线程阻塞直到另一些线程完成一些列操作后才被唤醒
-
DountDownLatch主要有两个方法,当一个或多个线程调用await方法时,调用线程会被阻塞。其他线程
调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),
当计数器的之变成零时,因调用await方法被阻塞的线程会被唤醒,继续执行。 -
CountDownLatchDemo
/** * 枚举类的用处: 节省了很多 if判断 * 使代码一处跟新 处处变化 * java.util.concurrent.CountDownLatchDemo countDownLatch: * 倒计时: * countDownLatch.countDown(); 自减一 * countDownLatch.await(); 别的线程没有执行完毕 当前线程不会执行 */ public class CountDownLatchDemo { public static void main(String[] args) { CountDownLatch countDownLatch=new CountDownLatch(6); for (int i=1;i<=6;i++){ new Thread(()->{ System.out.println(Thread.currentThread().getName()+"国,灭亡"); countDownLatch.countDown(); },CountryEnum.forEach_CountryEnum(i).getRetMessage()).start(); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("************秦帝国,一统华夏"); }
CyclicBarrier
-
CyclicBarrier的字面意思是可循环(Cyclic)使用的屏障(Barrier)。他要做的事情是,
让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,
屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CycliBarrier的await()方法。 -
CycliBarrierDemo
-
集齐7颗龙珠就能召唤神龙
-
代码
public class CyclicBarrierDemo { public static void main(String[] args) { CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{ System.out.println("*******召唤神龙"); }); for (int i=1;i<=7;i++){ final int tempInt=i; new Thread(()->{ //这里的睡眠 只会触发一次 因为线程并没有执行(都被挡在屏障外面了) 所以不会睡眠 //而是在集齐7颗龙珠后 才会一起执行 所以循环可以正常进行 而睡眠不会 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"召唤了第:"+tempInt+"颗龙珠"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } },String.valueOf(i)).start(); } } }
-
-
Semaphore:
-
信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。
-
SemaphoreDemo
-
争车位
-
代码
public class SemaphoreDemo { public static void main(String[] args) { Semaphore semaphore=new Semaphore(30); for (int i=1;i<=60;i++){ final int tempInt=i; new Thread(()->{ try { semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"抢到了车位"); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"\t 3秒后离开车位"); } catch (InterruptedException e) { e.printStackTrace(); }finally { semaphore.release(); } },String.valueOf(i)).start(); } } }
-
一个信号量有且仅有 3 种操作,且它们全部是原子的。
初始化、增加和减少。
增加可以为一个进程解除阻塞。
减少可以让一个进程进入阻塞。
Semaphore 管理一系列许可证。
每个 acquire() 方法阻塞,直到有一个许可证可以获得然后拿走一个许可证。
每个 release() 方法增加一个许可证,这可能会释放一个阻塞的 acquire() 方法。
不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。
Semaphore 在计数器不为 0 的时候对线程就放行,一旦达到 0,那么所有请求资源的新线程都会被阻塞,包括增加请求到许可的线程,Semaphore 是不可重入的。
每一次请求一个许可都会导致计数器减少 1,同样每次释放一个许可都会导致计数器增加 1,一旦达到 0,新的许可请求线程将被挂起。
Semaphore 有两种模式,公平模式 和 非公平模式 ,默认是非公平模式。
公平模式就是调用 acquire 的顺序就是获取许可证的顺序,遵循 FIFO。
非公平模式是抢占式的,也就是有可能一个新的获取线程恰好在一个许可证释放时得到了这个许可证,而前面还有等待的线程。
应用场景
Semaphore可以用来做流量限制,特别是公共资源有限的应用场景,比如说数据库连接。
由于 release() 释放许可时,未对释放许可数做限制,所有可以通过该方法增加总的许可数量; reducePermits() 方法可以减少总的许可数量,通过这两个方法可以到达动态调整许可的
分析:假如有一个需求,需读取几个万个文件的数据,因为都是IO密集型,我们可以启动几十个线程并发的读取,但是如果读取到内存后,还需要存储到数据库,而数据库的连接数只有10个,这时候我们就必须要控制只有10个线程同时获取到数据库连接,否则会抛出异常提示无法连接数据库。针对这种情况,我们就可以使用Semaphore来做流量控制。