java.util.concurrent包,这个包中又很多与多线程相关的工具
Callable是一种函数式接口,与Runnable并列,都可以表示一个任务,但是使用上略有区别。 Callable是一种有返回值的任务,可以通过泛型指定返回类型。此外,Callable接口没法直接作为参数传给Thread,需要一个中间的类FutureTask,应该是因为Thread类中没有直接提供获取返回值的方法,但是FutureTask提供了get()方法。
package Thread_11_25;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class demo37 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int result = 0;
for (int i = 0; i < 10; i++) {
result++;
}
return result;
}
};
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread t = new Thread(futureTask);
t.start();
// get 操作就是获取到 FutureTask 的返回值. 这个返回值就来自于 Callable 的 call 方法.
// get 可能会阻塞. 如果当前 线程 执行完毕, get 拿到返回结果.
// 如果当前线程还没执行完毕, get 会一直阻塞.
System.out.println(futureTask.get());
}
}get方法是FutureTask提供的,并非Thread提供,目的是解耦合,Thread就是执行任务,不必考虑任务是什么。 如果不使用Callable,使用Runnable当然也可以解决,但是使用时会有所不同:
package Thread_11_25;
public class demo38 {
private static int result = 0;
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
result++;
}
}
};
Thread t = new Thread(runnable);
t.start();
t.join();
System.out.println(result);
}
}
在synchronized之前,代码普遍使用ReenTranLock锁,它是一种可重入互斥的锁。 主要方法: lock(): 加锁, 如果获取不到锁就死等. trylock(超时时间): 加锁, 如果获取不到锁, 等待⼀定的时间之后就放弃加锁. unlock(): 解锁 他在使用时需要手动加锁释放锁,所以要注意防止释放锁的代码没有执行:

package Thread_11_25;
import java.util.concurrent.locks.ReentrantLock;
public class demo39 {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
ReentrantLock locker = new ReentrantLock();
Thread t1 = new Thread(()->{
for (int i = 0; i < 5000; i++) {
locker.lock();
count++;
locker.unlock();
}
});
Thread t2 = new Thread(()->{
for (int i = 0; i < 5000; i++) {
locker.lock();
try{
count++;
}finally {
locker.unlock();
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("count = " + count);
}
}
实现与定位 synchronized:Java 关键字,由 JVM 内部通过 C++ 实现(JVM 层面锁)。 ReentrantLock:Java 标准库java.util.concurrent.locks包下的类(API 层面锁)。
加解锁方式 synchronized:自动加解锁(代码块 / 方法结束自动释放),无需手动操作。 ReentrantLock:需手动调用lock()加锁、unlock()解锁(建议配合try-finally保证解锁)。
非阻塞 / 超时加锁能力 ReentrantLock:提供tryLock()(加锁失败不阻塞,返回 false)、tryLock(超时时间)(超时后自动返回)。不阻塞提高线程效率。 synchronized:无此能力,加锁失败会持续阻塞。
公平锁支持 ReentrantLock:可通过构造器指定公平锁(默认非公平),按线程等待顺序分配锁。 synchronized:仅支持非公平锁。
等待通知机制 ReentrantLock:配合Condition类,可创建多个等待队列,支持精细唤醒。 synchronized:依赖Object的wait/notify,仅一个等待队列,唤醒逻辑较粗糙。
• 锁竞争不激烈的时候, 使⽤ synchronized, 效率更⾼, ⾃动释放更⽅便. • 锁竞争激烈的时候, 使⽤ ReentrantLock, 搭配 trylock 更灵活控制加锁的⾏为, ⽽不是死等. • 如果需要使⽤公平锁(根据时间先后调度), 使⽤ ReentrantLock
信号量, ⽤来表⽰ “可⽤资源的个数”. 本质上就是⼀个计数器.
可以把信号量想象成是停⻋场的展⽰牌: 当前有⻋位 100 个. 表⽰有 100 个可⽤资源. 当有⻋开进去的时候, 就相当于申请⼀个可⽤资源, 可⽤⻋位就 -1 (这个称为信号量的 P 操作) 当有⻋开出来的时候, 就相当于释放⼀个可⽤资源, 可⽤⻋位就 +1 (这个称为信号量的 V 操作) 如果计数器的值已经为 0 了, 还尝试申请资源, 就会阻塞等待, 直到有其他线程释放资源.
Semaphore 的 PV 操作中的加减计数器操作都是原⼦的, 可以在多线程环境下直接使⽤。
package Thread_11_25;
import java.util.concurrent.Semaphore;
public class demo40 {
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore = new Semaphore(3);
semaphore.acquire();
System.out.println("进行一次P操作");
semaphore.acquire();
System.out.println("进行一次P操作");
semaphore.acquire();
System.out.println("进行一次P操作");
semaphore.acquire();
System.out.println("进行一次P操作");
}
}
申请三个资源,但是进行四次p操作,程序就会被阻塞。 需要先把资源释放掉才可以继续申请资源。
package Thread_11_25;
import java.util.concurrent.Semaphore;
public class demo40 {
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore = new Semaphore(3);
semaphore.acquire();
System.out.println("进行一次P操作");
semaphore.acquire();
System.out.println("进行一次P操作");
semaphore.acquire();
System.out.println("进行一次P操作");
semaphore.release();
semaphore.acquire();
System.out.println("进行一次PV操作");
}
}
特殊情况:当信号量为1时,取值要么是一要么是零,最多只能连续申请一次,此时和锁的效果差不多,并且PV操作都是原子的。 示例:
package Thread_11_25;
import java.util.concurrent.Semaphore;
public class demo41 {
private static int count = 0;
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(1);
Thread t1 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 50000; i++) {
try {
semaphore.acquire();
count++;
semaphore.release();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 50000; i++) {
try {
semaphore.acquire();
count++;
semaphore.release();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + count);
}
}
有道翻译:countdown倒数,latch门闩 CountDownLatch 是 java.util.concurrent 下的同步工具类,核心作用是让一个或多个线程等待其他线程完成操作后再继续执行,基于 “计数器递减” 实现。
将一个大任务分成几个小任务,所有小任务都完成大任务也结束。
核心原理: 初始时设置计数器的值(构造方法时传参指定) 小任务完成后调用countdown,减少计数器的值 其他线程调用await,等待计数器的值为0时其他线程才可以运行,否则阻塞。
原子性的体现: countdown方法对于计数器值的修改是原子性的,支持多线程操作。

package Thread_11_25;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class demo42 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(10);
ExecutorService executor = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {
int id = i;
executor.submit(()->{
System.out.println("任务" + id + "开始运行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("任务" + id + "结束运行");
latch.countDown();
});
}
latch.await();
System.out.println("所有任务执行完毕");
}
}
在线程池提供的多线程环境下,程序安全的运行,直到结束。 但是此时进程并没有结束,因为线程池中仍然有前台线程处于阻塞状态。
本文围绕 java.util.concurrent(JUC) 核心包,系统介绍了四大并发工具的核心逻辑与实践场景:Callable 作为带返回值的任务载体,需通过 FutureTask 适配 Thread 执行,其 get() 方法可阻塞获取结果,弥补了 Runnable 无返回值的缺陷;ReentrantLock 作为显式可重入锁,提供手动加解锁、超时加锁、公平锁及多条件等待等灵活特性,锁竞争激烈时更具优势,而锁竞争平缓时推荐使用 JVM 优化更充分的 synchronized;Semaphore 基于计数器实现资源控制,通过 PV 原子操作管理可用资源数量,支持非阻塞申请,可用于限流、资源池等场景;CountDownLatch 依靠原子性计数器实现线程同步,让一个或多个线程等待其他任务全部完成后再执行,适用于任务拆分与汇总场景。这些工具从任务封装、锁机制、资源控制、线程协作四个维度,为多线程编程提供了安全高效的解决方案,帮助开发者规避线程安全问题,提升并发程序的可控性与灵活性。