工具类概览
下面我们一个个直接通过案例代码来看我们这些工具类可以用来做什么事情
CountDownLatch
final CountDownLatch countDownLatch = new CountDownLatch(10);
final CountDownLatch countDownLatchNoStop = new CountDownLatch(10);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10; i ++) {
new Thread(() -> {
try {
Thread.sleep(1000);
// 计数器减一
countDownLatch.countDown();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
// 等待所有线程执行完毕
countDownLatch.await();
System.out.println("countDownLatch cost time : " + (System.currentTimeMillis() - startTime));
for (int i = 0; i < 5; i ++) {
new Thread(() -> {
try {
Thread.sleep(1000);
countDownLatchNoStop.countDown();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
countDownLatchNoStop.await();
// 永远也不会输出 还有五个任务没有执行完
System.out.println("countDownLatchNoStop cost time : " + (System.currentTimeMillis() - startTime));
}
运行结果:
我们输出countDownLatch cost time以后永远阻塞了,因为第二个countDownLatchNoStop还没有执行完毕,始终抵达不了await。
因为这个工具类适合分批次去处理任务最后汇总到一起继续执行一段逻辑,比如拼团,比如我们在地图中的层级瓦片生成,1-18每个层级生成成功以后记录成功状态
Semaphore
用来限制或管理数量有限资源的使用情况,也就是用来限流。
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 9; i ++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println("当前线程:" + Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
} finally {
semaphore.release();
}
}).start();
}
new Thread(() ->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("------------------------------");
}).start();
new Thread(() ->{
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("------------------------------");
}).start();
}
我们可以看到,运行结果哦几乎是3个为一组进行输出,是因为这个工具类定义了就只有三个位置,如果三个位置都满了剩下的线程就只能等待,等到有资源释放了继续执行。
CyclicBarrier
线程会等待,直到线程到了事先规定的数目,然后触发执行条件进行下一步动作
public static void main(String[] args) {
final CyclicBarrier barrier = new CyclicBarrier(7, () -> {
System.out.println("都拿到年终奖了吧,其实刚刚数的都是假钱,怎么可能有给你发年终奖");
System.out.println("==========================================");
});
for (int i = 0; i < 7; i ++) {
new Thread(() -> {
System.out.println( "开始给" + Thread.currentThread().getName() +",数10W块钱要数一整子");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
barrier.await();
System.out.println(Thread.currentThread().getName() + "哦豁我上当了");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
}).start();
}
}
运行结果:
和CountDownLatch对比,其实前部分的非常的相似,不同的就是CountDownLatch减为0后统一放行,后面的逻辑也是统一了。但是CyclicBarrier就是每个线程执行到栅栏这个地方进行阻塞,然后数量到设定的值以后统一执行一段逻辑,但是之后的逻辑还是分别在各自的线程中执行。
Condition
当线程1需要等待某个条件时就去执行condition.await()方法,一旦执行await()方法,线程就会进入阻塞状态。通常会有另一个线程2去执行对应条件,直到这个条件达成时,线程2就会执行condition.signal()方法,此时JVM就会从被阻塞的线程里找到那些等待该condition的线程,当线程1收到可执行信号时,它的线程状态就会变成Runnable可执行状态。
- signalAll()会唤起所有正在等待的线程。
- signal()是公平的,只会唤起那个等待时间最长的线程。
下面这个例子是两个线程交替打印:
public class ConditionDemo {
public static volatile int cnt = 0;
public static void add() {
cnt ++;
}
public static boolean judge() {
return cnt == 100;
}
public static void main(String[] args) throws InterruptedException {
Lock lock = new ReentrantLock();
final Condition c1 = lock.newCondition();
final Condition c2 = lock.newCondition();
new Thread(() -> {
lock.lock();
try {
while (!judge()) {
add();
System.out.println(Thread.currentThread().getName() + "---->" + + cnt);
c2.signal();
c1.await();
}
System.out.println("1 over");
c2.signal();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}).start();
Thread.sleep(100);
new Thread(() -> {
lock.lock();
try {
while (!judge()) {
add();
System.out.println(Thread.currentThread().getName() + "---->" + cnt);
c1.signal();
c2.await();
}
System.out.println("2 over");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}).start();
}
}