2025年Java秋招面试必看的 | Java并发编程 面试题(实操篇)
四、JUC包下的并发工具类
4.1 ReentrantLock与Condition
4.1.1 基本使用
ReentrantLock是一个可重入的互斥锁,相比synchronized具有更灵活的锁机制。以下是一个使用ReentrantLock实现的生产者-消费者示例:
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerConsumerExample {
private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final Queue<Integer> queue = new LinkedList<>();
private final int capacity = 5;
public void produce(int item) throws InterruptedException {
lock.lock();
try {
while (queue.size() == capacity) {
notFull.await(); // 队列满时等待
}
queue.offer(item);
System.out.println(Thread.currentThread().getName() + " 生产: " + item);
notEmpty.signal(); // 通知消费者队列非空
} finally {
lock.unlock();
}
}
public int consume() throws InterruptedException {
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await(); // 队列空时等待
}
int item = queue.poll();
System.out.println(Thread.currentThread().getName() + " 消费: " + item);
notFull.signal(); // 通知生产者队列未满
return item;
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ProducerConsumerExample pc = new ProducerConsumerExample();
// 生产者线程
Thread producer = new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
pc.produce(i);
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}, "Producer");
// 消费者线程
Thread consumer = new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
pc.consume();
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}, "Consumer");
producer.start();
consumer.start();
}
}
4.1.2 公平锁与非公平锁
ReentrantLock可以创建公平锁和非公平锁。公平锁会按照线程请求锁的顺序来获取锁,而非公平锁则允许线程在锁释放时直接竞争锁,不考虑请求顺序。以下是创建公平锁的示例:
ReentrantLock fairLock = new ReentrantLock(true); // 创建公平锁
4.2 原子类(AtomicInteger、AtomicLong等)
4.2.1 原子操作示例
Java的原子类提供了高效的原子操作,避免了使用锁的开销。以下是一个使用AtomicInteger实现计数器的示例:
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicCounter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
public static void main(String[] args) throws InterruptedException {
AtomicCounter counter = new AtomicCounter();
int threadCount = 10;
Thread[] threads = new Thread[threadCount];
// 创建并启动多个线程
for (int i = 0; i < threadCount; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
counter.increment();
}
});
threads[i].start();
}
// 等待所有线程完成
for (Thread thread : threads) {
thread.join();
}
System.out.println("最终计数: " + counter.getCount()); // 输出应为10000
}
}
4.2.2 CAS操作原理
原子类的底层实现基于CAS(Compare - And - Swap)操作。CAS是一种无锁算法,包含三个操作数:内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。以下是一个简单的CAS操作示例:
import java.util.concurrent.atomic.AtomicInteger;
public class CASExample {
private AtomicInteger value = new AtomicInteger(10);
public void compareAndSet(int expected, int newValue) {
boolean success = value.compareAndSet(expected, newValue);
System.out.println("操作结果: " + success);
}
public static void main(String[] args) {
CASExample example = new CASExample();
example.compareAndSet(10, 20); // 操作成功,值变为20
example.compareAndSet(10, 30); // 操作失败,值仍为20
}
}
4.3 并发集合(ConcurrentHashMap、CopyOnWriteArrayList等)
4.3.1 ConcurrentHashMap使用
ConcurrentHashMap是线程安全的哈希表实现,在多线程环境下可以高效地进行读写操作。以下是一个使用ConcurrentHashMap的示例:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
public void add(String key, int value) {
map.put(key, value);
}
public int get(String key) {
return map.getOrDefault(key, 0);
}
public void remove(String key) {
map.remove(key);
}
public static void main(String[] args) {
ConcurrentHashMapExample example = new ConcurrentHashMapExample();
// 多个线程并发操作map
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.add("key" + i, i);
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
int value = example.get("key" + i);
System.out.println("key" + i + ": " + value);
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4.3.2 CopyOnWriteArrayList原理
CopyOnWriteArrayList是一个线程安全的列表实现,它在修改操作时会创建底层数组的一个副本,从而避免了在迭代过程中出现ConcurrentModificationException。以下是一个使用CopyOnWriteArrayList的示例:
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListExample {
private CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
public void add(String element) {
list.add(element);
}
public void iterate() {
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
public static void main(String[] args) {
CopyOnWriteArrayListExample example = new CopyOnWriteArrayListExample();
// 线程1添加元素
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
example.add("element" + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 线程2迭代列表
Thread t2 = new Thread(() -> {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
example.iterate();
});
t1.start();
t2.start();
}
}
4.4 线程池(ExecutorService、ScheduledExecutorService等)
4.4.1 线程池的创建与使用
线程池可以有效地管理和复用线程,提高系统性能。以下是一个使用Executors工厂类创建线程池的示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建一个固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交任务到线程池
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("任务 " + taskId + " 由线程 " + Thread.currentThread().getName() + " 执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("任务 " + taskId + " 执行完成");
});
}
// 关闭线程池
executor.shutdown();
try {
// 等待所有任务完成
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
// 超时后强制关闭
executor.shutdownNow();
}
} catch (InterruptedException e) {
// 再次调用shutdownNow
executor.shutdownNow();
}
}
}
4.4.2 自定义线程池
实际生产环境中,推荐使用ThreadPoolExecutor类来自定义线程池,以满足特定需求。以下是一个自定义线程池的示例:
import java.util.concurrent.*;
public class CustomThreadPoolExample {
public static void main(String[] args) {
// 创建自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
60, // 线程空闲时间
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10), // 任务队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 提交任务
for (int i = 0; i < 20; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("任务 " + taskId + " 由线程 " + Thread.currentThread().getName() + " 执行");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 关闭线程池
executor.shutdown();
}
}
4.5 信号量(Semaphore)
4.5.1 基本使用
Semaphore是一个计数信号量,用于控制同时访问某个资源的线程数量。以下是一个使用Semaphore实现资源池的示例:
import java.util.concurrent.Semaphore;
public class ResourcePool {
private final Semaphore semaphore;
private final int maxResources;
private final boolean[] resources;
public ResourcePool(int maxResources) {
this.maxResources = maxResources;
this.resources = new boolean[maxResources];
this.semaphore = new Semaphore(maxResources, true); // 创建公平信号量
}
public int acquire() throws InterruptedException {
semaphore.acquire(); // 获取许可
return getResource();
}
public void release(int resourceId) {
markResourceAsFree(resourceId);
semaphore.release(); // 释放许可
}
private synchronized int getResource() {
for (int i = 0; i < maxResources; i++) {
if (!resources[i]) {
resources[i] = true;
return i;
}
}
return -1; // 不会发生,因为已经获取了许可
}
private synchronized void markResourceAsFree(int resourceId) {
resources[resourceId] = false;
}
public static void main(String[] args) {
ResourcePool pool = new ResourcePool(3);
// 创建10个线程竞争资源
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
int resourceId = pool.acquire();
System.out.println(Thread.currentThread().getName() + " 获取资源 " + resourceId);
Thread.sleep(1000);
pool.release(resourceId);
System.out.println(Thread.currentThread().getName() + " 释放资源 " + resourceId);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Thread-" + i).start();
}
}
}
4.6 倒计时门闩(CountDownLatch)
4.6.1 基本使用
CountDownLatch用于让一个或多个线程等待其他线程完成操作。以下是一个使用CountDownLatch的示例:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int workerCount = 5;
CountDownLatch latch = new CountDownLatch(workerCount);
// 创建并启动多个工作线程
for (int i = 0; i < workerCount; i++) {
final int workerId = i;
new Thread(() -> {
System.out.println("工作线程 " + workerId + " 开始工作");
try {
// 模拟工作
Thread.sleep((long) (Math.random() * 5000));
System.out.println("工作线程 " + workerId + " 完成工作");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown(); // 计数减1
}
}).start();
}
// 主线程等待所有工作线程完成
System.out.println("主线程等待工作线程完成...");
latch.await();
System.out.println("所有工作线程已完成,主线程继续执行");
}
}
4.7 循环屏障(CyclicBarrier)
4.7.1 基本使用
CyclicBarrier用于让一组线程到达一个屏障(同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续执行。以下是一个使用CyclicBarrier的示例:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public static void main(String[] args) {
int threadCount = 3;
CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
System.out.println("所有线程都到达屏障,继续执行");
});
// 创建并启动多个线程
for (int i = 0; i < threadCount; i++) {
final int threadId = i;
new Thread(() -> {
System.out.println("线程 " + threadId + " 开始执行");
try {
// 模拟工作
Thread.sleep((long) (Math.random() * 3000));
System.out.println("线程 " + threadId + " 到达屏障");
barrier.await(); // 等待其他线程到达屏障
System.out.println("线程 " + threadId + " 继续执行");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
4.8 交换器(Exchanger)
4.8.1 基本使用
Exchanger用于两个线程之间交换数据。以下是一个使用Exchanger的示例:
import java.util.concurrent.Exchanger;
public class ExchangerExample {
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
// 第一个线程
Thread thread1 = new Thread(() -> {
try {
String data1 = "线程1的数据";
System.out.println("线程1发送: " + data1);
String receivedData = exchanger.exchange(data1);
System.out.println("线程1接收: " + receivedData);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 第二个线程
Thread thread2 = new Thread(() -> {
try {
String data2 = "线程2的数据";
System.out.println("线程2发送: " + data2);
String receivedData = exchanger.exchange(data2);
System.out.println("线程2接收: " + receivedData);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
thread1.start();
thread2.start();
}
}
五、高级并发编程技术
5.1 CompletableFuture
5.1.1 异步任务链
CompletableFuture是Java 8引入的用于处理异步计算的类,它可以方便地构建异步任务链。以下是一个使用CompletableFuture的示例:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class CompletableFutureExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建自定义线程池
var executor = Executors.newFixedThreadPool(2);
// 异步任务1:获取用户ID
CompletableFuture<String> userIdFuture = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("获取用户ID");
return "user123";
}, executor);
// 异步任务2:根据用户ID获取订单信息
CompletableFuture<String> orderFuture = userIdFuture.thenApplyAsync(userId -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("获取用户 " + userId + " 的订单信息");
return "order456";
}, executor);
// 异步任务3:根据订单信息获取物流信息
CompletableFuture<String> logisticsFuture = orderFuture.thenApplyAsync(orderId -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("获取订单 " + orderId + " 的物流信息");
return "物流信息:已发货";
}, executor);
// 最终结果处理
logisticsFuture.thenAccept(logisticsInfo -> {
System.out.println("最终结果:" + logisticsInfo);
executor.shutdown();
});
// 主线程不需要等待,可以继续执行其他任务
System.out.println("主线程继续执行");
}
}
5.1.2 组合多个CompletableFuture
CompletableFuture提供了多种方法来组合多个异步任务。以下是一个组合多个CompletableFuture的示例:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureCombinationExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 任务1:计算两个数的和
CompletableFuture<Integer> task1 = CompletableFuture.supplyAsync(() -> 2 + 3);
// 任务2:计算两个数的乘积
CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> 4 * 5);
// 组合两个任务的结果
CompletableFuture<Integer> combinedFuture = task1.thenCombine(task2, (sum, product) -> sum + product);
// 获取最终结果
System.out.println("最终结果: " + combinedFuture.get()); // 输出: 25
}
}
5.2 响应式编程(Reactor)
5.2.1 基本使用
Reactor是Java生态中主流的响应式编程框架,基于Reactive Streams规范。以下是一个使用Reactor的简单示例:
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import java.time.Duration;
public class ReactorExample {
public static void main(String[] args) throws InterruptedException {
// 创建一个Flux,发出1到5的整数
Flux<Integer> numbers = Flux.range(1, 5);
// 对每个元素进行平方操作,并在另一个线程上执行
Flux<Integer> squaredNumbers = numbers
.publishOn(Schedulers.boundedElastic())
.map(n -> {
System.out.println("处理元素 " + n + " 在线程 " + Thread.currentThread().getName());
return n * n;
});
// 订阅并处理结果
squaredNumbers.subscribe(
num -> System.out.println("收到元素: " + num),
error -> System.err.println("错误: " + error),
() -> System.out.println("处理完成")
);
// 创建一个Mono,延迟1秒后发出一个值
Mono.just("Hello, Reactor!")
.delayElement(Duration.ofSeconds(1))
.subscribe(System.out::println);
// 主线程等待,以便异步操作有时间完成
Thread.sleep(2000);
}
}
5.2.2 背压处理
响应式编程中的背压是处理生产者与消费者速度不匹配的机制。以下是一个使用背压的示例:
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
import java.time.Duration;
public class BackpressureExample {
public static void main(String[] args) throws InterruptedException {
// 创建一个快速生产者,每秒发出1000个元素
Flux<Integer> fastProducer = Flux.interval(Duration.ofMillis(1))
.map(i -> {
System.out.println("生产者发出: " + i);
return i.intValue();
});
// 创建一个慢速消费者,每500毫秒处理一个元素
fastProducer
.onBackpressureBuffer(100) // 设置缓冲区大小为100
.publishOn(Schedulers.boundedElastic(), 1) // 每次只请求1个元素
.subscribe(
item -> {
try {
Thread.sleep(500);
System.out.println("消费者处理: " + item);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
},
error -> System.err.println("错误: " + error.getMessage())
);
// 主线程等待
Thread.sleep(10000);
}
}
5.3 协程(Project Loom)
5.3.1 基本使用
Project Loom是Java平台上的一个重大改进,引入了轻量级线程(协程)的概念。以下是一个使用协程的示例:
import java.time.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class VirtualThreadExample {
public static void main(String[] args) throws InterruptedException {
// 创建一个虚拟线程执行器
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
// 提交大量任务
for (int i = 0; i < 10000; i++) {
final int taskId = i;
executor.submit(() -> {
try {
// 模拟IO操作
Thread.sleep(Duration.ofMillis(100));
System.out.println("任务 " + taskId + " 由线程 " + Thread.currentThread().getName() + " 执行");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 关闭执行器
executor.shutdown();
executor.awaitTermination(1, java.util.concurrent.TimeUnit.MINUTES);
}
}
5.3.2 协程与传统线程对比
以下是一个简单的对比示例,展示协程在处理大量并发任务时的优势:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadVsVirtualThreadComparison {
private static final int TASK_COUNT = 100000;
public static void main(String[] args) throws InterruptedException {
// 测试传统线程
testTraditionalThreads();
// 测试虚拟线程
testVirtualThreads();
}
private static void testTraditionalThreads() throws InterruptedException {
long startTime = System.currentTimeMillis();
ExecutorService executor = Executors.newFixedThreadPool(200);
for (int i = 0; i < TASK_COUNT; i++) {
final int taskId = i;
executor.submit(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
long endTime = System.currentTimeMillis();
System.out.println("传统线程耗时: " + (endTime - startTime) + " ms");
}
private static void testVirtualThreads() throws InterruptedException {
long startTime = System.currentTimeMillis();
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
for (int i = 0; i < TASK_COUNT; i++) {
final int taskId = i;
executor.submit(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
long endTime = System.currentTimeMillis();
System.out.println("虚拟线程耗时: " + (endTime - startTime) + " ms");
}
}
六、并发编程最佳实践
6.1 线程安全的设计原则
- 不可变对象:使用不可变对象可以从根本上避免线程安全问题。例如,String类就是不可变的,在多线程环境下可以安全使用。
- 线程封闭:将对象限制在单个线程中,避免多个线程访问共享资源。例如,使用ThreadLocal存储线程私有的数据。
- 使用线程安全的类:优先使用JDK提供的线程安全类,如ConcurrentHashMap、CopyOnWriteArrayList等。
- 同步机制:在必要时使用synchronized关键字或ReentrantLock等同步机制来保护共享资源。
6.2 性能优化策略
- 减少锁的粒度:只在必要的代码块上使用锁,避免锁住整个方法或类。
- 使用无锁数据结构:如原子类(AtomicInteger、AtomicLong等),利用CAS操作避免锁的使用。
- 线程池的合理配置:根据业务场景合理配置线程池的核心线程数、最大线程数和任务队列大小。
- 异步编程:使用CompletableFuture、Reactor等框架进行异步编程,提高系统的吞吐量。
6.3 死锁的预防与检测
- 死锁预防:
- 按顺序获取锁:确保所有线程按照相同的顺序获取锁。
- 设置锁超时:使用ReentrantLock的tryLock(long timeout, TimeUnit unit)方法。
- 避免嵌套锁:尽量避免在持有一个锁的情况下获取另一个锁。
- 死锁检测:
- 使用jstack命令:可以查看线程的堆栈信息,找出可能的死锁。
- 使用工具:如VisualVM、YourKit等工具可以检测死锁。
以下是一个检测死锁的示例代码:
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
public class DeadlockDetector {
private static final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
public static void start() {
Thread detector = new Thread(() -> {
while (true) {
long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
if (deadlockedThreads != null) {
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlockedThreads);
System.err.println("发现死锁!");
for (ThreadInfo info : threadInfos) {
System.err.println(info);
}
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
detector.setDaemon(true);
detector.start();
}
public static void main(String[] args) {
// 启动死锁检测器
start();
// 模拟死锁
Object lock1 = new Object();
Object lock2 = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock1) {
System.out.println("线程1获取了锁1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
synchronized (lock2) {
System.out.println("线程1获取了锁2");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (lock2) {
System.out.println("线程2获取了锁2");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
synchronized (lock1) {
System.out.println("线程2获取了锁1");
}
}
});
t1.start();
t2.start();
}
}
七、面试题实战
7.1 高频面试题解答
7.1.1 如何实现一个线程安全的单例模式?
// 双重检查锁定实现线程安全的单例模式
public class Singleton {
private static volatile Singleton instance; // 使用volatile保证可见性和有序性
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
// 第一次检查
synchronized (Singleton.class) {
if (instance == null) {
// 第二次检查
instance = new Singleton();
}
}
}
return instance;
}
}
7.1.2 如何实现生产者-消费者模式?
可以使用多种方式实现生产者-消费者模式,以下是使用BlockingQueue的实现:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ProducerConsumerWithBlockingQueue {
private static final BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10);
public static void main(String[] args) {
// 生产者线程
Thread producer = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
queue.put(i);
System.out.println("生产者生产: " + i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 消费者线程
Thread consumer = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
Integer item = queue.take();
System.out.println("消费者消费: " + item);
Thread.sleep(200);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
}
}
7.1.3 如何优化高并发场景下的性能?
优化高并发场景下的性能可以从以下几个方面入手:
- 减少锁的使用:使用无锁数据结构,如ConcurrentHashMap、原子类等。
- 异步处理:使用CompletableFuture、Reactor等框架处理异步任务,避免阻塞线程。
- 缓存:使用本地缓存(如Caffeine)或分布式缓存(如Redis)减少数据库访问。
- 限流:使用令牌桶、漏桶等算法进行限流,防止系统过载。
- 分库分表:在数据库层面进行分库分表,提高数据库的并发处理能力。
7.1.4 如何处理分布式系统中的并发问题?
处理分布式系统中的并发问题可以采用以下方法:
- 分布式锁:使用Redis、ZooKeeper等实现分布式锁,保证同一时刻只有一个线程可以访问共享资源。
- 乐观锁:在数据库表中添加版本号字段,通过CAS操作实现乐观锁。
- 幂等设计:确保服务的接口具有幂等性,避免重复操作导致的数据不一致。
- 最终一致性:采用异步通信和补偿机制,保证系统的最终一致性。
7.2 实战演练
7.2.1 实现一个简单的线程池
import java.util.LinkedList;
import java.util.Queue;
public class SimpleThreadPool {
private final int poolSize;
private final WorkerThread[] workers;
private final Queue<Runnable> taskQueue;
private boolean isShutdown = false;
public SimpleThreadPool(int poolSize) {
this.poolSize = poolSize;
this.taskQueue = new LinkedList<>();
this.workers = new WorkerThread[poolSize];
// 初始化工作线程
for (int i = 0; i < poolSize; i++) {
workers[i] = new WorkerThread();
workers[i].start();
}
}
public synchronized void execute(Runnable task) {
if (isShutdown) {
throw new IllegalStateException("线程池已关闭");
}
taskQueue.add(task);
notify(); // 唤醒等待的工作线程
}
public synchronized void shutdown() {
isShutdown = true;
for (WorkerThread worker : workers) {
worker.interrupt();
}
}
private class WorkerThread extends Thread {
@Override
public void run() {
while (!isInterrupted()) {
Runnable task;
synchronized (SimpleThreadPool.this) {
while (taskQueue.isEmpty() && !isShutdown) {
try {
wait(); // 没有任务时等待
} catch (InterruptedException e) {
interrupt();
}
}
if (isShutdown && taskQueue.isEmpty()) {
break;
}
task = taskQueue.poll();
}
if (task != null) {
try {
task.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
SimpleThreadPool threadPool = new SimpleThreadPool(3);
// 提交任务
for (int i = 0; i < 10; i++) {
final int taskId = i;
threadPool.execute(() -> {
System.out.println("任务 " + taskId + " 由线程 " + Thread.currentThread().getName() + " 执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 关闭线程池
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPool.shutdown();
}
}
7.2.2 使用CompletableFuture实现异步数据聚合
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AsyncDataAggregation {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(3);
// 模拟从不同服务获取数据
CompletableFuture<String> userInfoFuture = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "用户信息:ID=123,姓名=张三";
}, executor);
CompletableFuture<String> orderInfoFuture = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "订单信息:订单号=456,金额=100.00元";
}, executor);
CompletableFuture<String> paymentInfoFuture = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(800);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "支付信息:支付方式=支付宝,状态=已支付";
}, executor);
// 聚合所有数据
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
userInfoFuture, orderInfoFuture, paymentInfoFuture
);
// 当所有任务完成后,处理结果
CompletableFuture<String> combinedFuture = allFutures.thenApply(v -> {
try {
return userInfoFuture.get() + "\n" +
orderInfoFuture.get() + "\n" +
paymentInfoFuture.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
});
// 获取最终结果
System.out.println("聚合结果:\n" + combinedFuture.get());
// 关闭线程池
executor.shutdown();
}
}
八、总结
Java并发编程是Java技术栈中非常重要的一部分,也是面试中的高频考点。本文从基础概念、关键机制、工具类、高级技术等多个方面进行了介绍,并提供了丰富的实操示例。希望通过本文的学习,你能够掌握Java并发编程的核心知识,在面试中取得好成绩。同时,在实际工作中,也能够运用这些知识设计和实现高效、稳定的并发系统。
---2025 年 Java 秋招,Java 秋招面
代码获取方式
https://pan.quark.cn/s/14fcf913bae6