一、线程基础
在 Java 中创建线程有三种方式:
// 方式一:继承 Thread 类
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程运行中...");
}
}
new MyThread().start();
// 方式二:实现 Runnable 接口
new Thread(() -> {
System.out.println("Lambda 方式创建线程");
}).start();
// 方式三:实现 Callable 接口(有返回值)
FutureTask<String> task = new FutureTask<>(() -> "结果");
new Thread(task).start();
String result = task.get();
二、线程池
频繁创建销毁线程开销很大,线程池通过复用线程来提升性能。推荐使用 ThreadPoolExecutor 手动创建:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, TimeUnit.SECONDS, // 空闲线程存活时间
new LinkedBlockingQueue<>(100), // 任务队列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
executor.execute(() -> {
System.out.println("执行任务");
});
executor.shutdown(); // 优雅关闭
| 参数 | 说明 |
|---|---|
| corePoolSize | 核心线程数,即使空闲也不回收 |
| maximumPoolSize | 最大线程数 |
| keepAliveTime | 非核心线程空闲存活时间 |
| workQueue | 任务队列,缓存待执行的任务 |
| handler | 拒绝策略:Abort(抛异常)、CallerRuns(调用者执行)、Discard(丢弃)、DiscardOldest(丢弃最旧) |
三、锁机制
1. synchronized
// 同步方法
public synchronized void method() { }
// 同步代码块
synchronized (obj) {
// 临界区代码
}
2. Lock 接口
private final ReentrantLock lock = new ReentrantLock();
public void method() {
lock.lock();
try {
// 临界区
} finally {
lock.unlock(); // 必须在 finally 中释放
}
}
| 特性 | synchronized | ReentrantLock |
|---|---|---|
| 获取锁方式 | 阻塞等待 | 可尝试非阻塞获取(tryLock) |
| 公平性 | 非公平 | 可选公平/非公平 |
| 可中断 | 不可中断 | 可中断等待(lockInterruptibly) |
| 条件变量 | wait/notify | Condition(可多条件) |
四、JUC 常用工具类
- CountDownLatch:等待一组线程完成后再继续
- CyclicBarrier:多个线程互相等待,到达屏障后一起执行
- Semaphore:控制同时访问资源的线程数量
- ConcurrentHashMap:线程安全的 HashMap,分段锁实现
- CopyOnWriteArrayList:写时复制,适合读多写少场景
// CountDownLatch 示例
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 执行任务
latch.countDown();
}).start();
}
latch.await(); // 等待三个线程全部完成
System.out.println("所有任务完成");
五、总结
并发编程的核心是正确地管理共享资源。记住:优先使用并发工具类而非底层锁,正确使用线程池避免资源耗尽,编写并发代码时先保证正确性再优化性能。