CAS

Atomic原子类

CountDownLatch

await方法的意义是阻塞当前线程,直到之前创立的所有线程都完成了任务对吧。那为什么还是会出现图二的情况呢?根据提供的两张图片,完整的代码如下:

图一代码(Main类):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {
public static void main(String[] args) throws InterruptedException {
CountDownLatch cLatch = new CountDownLatch(5);
for (int i = 1; i <= 5; i++) {
String name = "task_"+i;
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("完成任务:"+Thread.currentThread().getName());
cLatch.countDown();
}
},name).start();
}
cLatch.await();
System.out.println("5个任务全部完成");
}
}

图二代码(CountDownLatch示例):

1
2
3
4
5
6
7
8
9
10
11
12
CountDownLatch cd = new CountDownLatch(2);
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 6; i >=0; i--) {
System.out.println("倒计时"+i);
cd.countDown();
}
}
}, name: "Thread A").start();
cd.await();
System.out.println("主线程");

关于CountDownLatch.await()方法和图二的情况:

CountDownLatch.await()确实是阻塞当前线程,直到计数器归零。但出现图二情况(先输出”倒计时6”、”倒计时5”,然后是”主线程”,接着是”倒计时4”等)的原因是:

  1. 计数器的初始值:图二中的CountDownLatch初始值是2,不是7。这意味着Thread A在执行了两次countDown()后(输出”倒计时6”和”倒计时5”后),计数器就已经归零。
  2. 线程调度的不确定性:当计数器归零时,主线程被唤醒并继续执行,输出”主线程”。但这并不意味着Thread A会立即停止 - 它会继续执行剩余的循环迭代,输出”倒计时4”直到”倒计时0”。
  3. 线程执行顺序:操作系统的线程调度器决定了哪个线程在特定时刻获得CPU时间片。即使主线程被唤醒,Thread A可能仍然继续执行一段时间后,主线程才真正获得执行机会。

简而言之,CountDownLatch保证了主线程会等待直到计数器归零,但并不保证其他线程会在此时停止执行。两个线程(主线程和Thread A)在计数器归零后都可以继续执行,它们的具体执行顺序取决于线程调度器。

这就是为什么你会看到”倒计时6”、”倒计时5”、”主线程”、”倒计时4”等这样的输出顺序 - 计数器在第二次countDown()后归零,但Thread A继续完成了它的所有循环迭代。

CyclicBarrier

Semaphore

线程池原理

Executor框架

ThreadPoolExecutor

FutureTask