这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战
前言
JUC包中给我们提供了很多工具类,CountDownLatch、Semaphore等。
CountDownLatch
字面翻译为倒计时闩锁,结合字面意思,想象一下一个门闩上面记着一个数字,倒计时到
0后打开门闩,放行。我们可以利用这个特性,实现类似计数器的功能。比如有一个任务A,他要等待其他3个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能。
代码示例
public class CountDownSample {
private static int count = 0;
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(100);
//CDL总数和操作数保持一致
CountDownLatch cdl = new CountDownLatch(10000);
for (int i = 1; i <= 10000; i++) {
final int index = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
synchronized (CountDownSample.class) {
try {
count = count + index;
} catch (Exception e) {
e.printStackTrace();
} finally {
//计数器减一
cdl.countDown();
}
}
}
});
}
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(count);
threadPool.shutdown();
}
}
cdl.await()堵塞当前线程,知道cdl=0的时候再继续往下走- 还记得之前验证多线程的时候为了保证循环里的线程都走完,在程序中加了一段延迟睡眠,在这里我们就可以通过
CountDownLatch来控制了。
Semaphore
Semaphore信号量经常用于限制获取某种资源的线程数量。举个例子,游戏厂商在同一时间玩家在线数量过多的时候会限制登录人数,其余的人就得排队,限制在线人数10000,那么这里的信号量就是10000,当在线的人有人退出,排队的人就会立马顶替上去。 代码示例
public class SemaphoreSample1 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
Semaphore semaphore = new Semaphore(5);
for(int i = 1 ; i <= 20 ; i++) {
final int index = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
play();
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
threadPool.shutdown();
}
public static void play(){
try {
System.out.println(new Date() + " " + Thread.currentThread().getName() + ":获取登录名额,登录成功");
Thread.sleep(2000);
System.out.println(new Date() + " " + Thread.currentThread().getName() + ":退出服务器");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Semaphore semaphore = new Semaphore(5)定义5个信号量,也就是说服务器只允许5个人在里面玩semaphore.acquire()获取一个信号量,“占用一个名额”play()模拟玩的动作,2.5s之后退出semaphore.release()执行完成后释放这个信号量,“退出登录,让出名额”
除了排队的场景,我们还碰到过,如果人数已满,直接拒绝登录,请稍后再试。在上面的代码基础上稍加修改,就可以实现这样的功能。 代码示例
public void run() {
try {
if(semaphore.tryAcquire(6, TimeUnit.SECONDS)) {
play();
semaphore.release();
}else{
System.out.println(Thread.currentThread().getName() + ":对不起,服务器已满,请稍后再试");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
semaphore.tryAcquire(6, TimeUnit.SECONDS)尝试获取一次信号量,6秒钟内获取到返回true,否则返回false注意: 这种情况下,获取不到就直接拒绝访问。