Java 线程基础详解
目录
- 什么是多线程
- 为什么需要多线程
- 线程的基本概念
- 多线程的优势与挑战
- 线程的生命周期
- 多线程编程实践
- 线程安全问题
- 常见的线程同步机制
- 线程间通信详解
- 并发工具类详解
- 线程安全的集合类
- 死锁问题及预防
- 性能优化建议
- 调试与监控
- 实际应用场景示例
- 参考资源
什么是多线程
多线程是指在同一个程序中同时运行多个执行路径的技术。每个线程都是进程中的一个独立执行流,共享进程的资源,但拥有自己的程序计数器、栈和局部变量。
为什么需要多线程
- 提高系统资源利用率
- CPU 利用率的提升
- I/O 等待时间的充分利用
- 任务并行处理
- 提高程序执行效率
- 充分利用多核处理器
线程的基本概念
线程与进程的区别
- 进程:独立的执行环境,拥有独立的内存空间
- 线程:进程内的执行单元,共享进程的内存空间
线程的组成部分
- 线程 ID
- 程序计数器
- 寄存器集合
- 栈空间
线程的生命周期
状态说明
New(新建)
- 线程被创建,但尚未启动的状态
- 调用
new Thread()后的状态
Runnable(可运行)
- 包含 Ready 和 Running 两个子状态
- Ready:等待 CPU 时间片
- Running:获得 CPU 时间片正在执行
Blocked(阻塞)
- 等待获取监视器锁,以进入同步块/方法
- 或者在同步块/方法中调用了阻塞式 I/O 操作
Waiting(等待)
- 无限期等待其他线程执行特定操作
- 通过
wait()、join()等方法进入此状态
Timed Waiting(计时等待)
- 等待指定时间
- 通过
sleep(time)、wait(time)等方法进入此状态
Terminated(终止)
- 线程执行完成
- 或者因异常终止
主要状态转换方法
start(): 启动线程,使其进入 Runnable 状态wait(): 使线程进入 Waiting 状态notify()/notifyAll(): 唤醒等待的线程sleep(): 使线程进入 Timed Waiting 状态join(): 等待其他线程完成interrupt(): 中断线程
多线程编程实践
创建线程的方式
- 继承 Thread 类
class MyThread extends Thread {
public void run() {
// 线程执行代码
}
}
- 实现 Runnable 接口
class MyRunnable implements Runnable {
public void run() {
// 线程执行代码
}
}
- 实现 Callable 接口(可以有返回值)
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
// 线程执行代码
return "执行结果";
}
}
// 使用方式
FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
// 获取返回值
String result = futureTask.get();
- 使用匿名内部类
// 使用 Thread 匿名类
Thread thread1 = new Thread() {
@Override
public void run() {
// 线程执行代码
}
};
// 使用 Runnable 匿名类
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
// 线程执行代码
}
});
- 使用 Lambda 表达式(Java 8+)
// Runnable Lambda 表达式
Thread thread = new Thread(() -> {
// 线程执行代码
});
// 或者直接使用 ExecutorService
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
// 线程执行代码
});
- 使用 CompletableFuture(Java 8+)
// 异步执行任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 线程执行代码
return "执行结果";
});
// 处理执行结果
future.thenAccept(result -> {
System.out.println("处理结果: " + result);
});
线程安全问题
常见的线程安全问题
- 竞态条件
- 内存可见性
- 指令重排序
如何保证线程安全
- 使用同步机制
- 使用线程安全的数据结构
- 避免共享可变状态
常见的线程同步机制
1. synchronized 关键字
synchronized void method() {
// 同步方法
}
synchronized(object) {
// 同步代码块
}
2. Lock 接口
Lock lock = new ReentrantLock();
lock.lock();
try {
// 临界区代码
} finally {
lock.unlock();
}
3. volatile 关键字
- 保证变量的可见性
- 防止指令重排序
4. 线程通信机制
- wait()/notify()
- Condition
- CountDownLatch
- CyclicBarrier
- Semaphore
线程间通信详解
1. wait/notify 机制
class SharedResource {
private boolean flag = false;
synchronized void waitForFlag() throws InterruptedException {
while (!flag) {
wait();
}
// 处理数据
}
synchronized void setFlag() {
flag = true;
notify();
}
}
2. BlockingQueue 的使用
BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
// 生产者
void produce() throws InterruptedException {
queue.put("数据");
}
// 消费者
void consume() throws InterruptedException {
String data = queue.take();
}
并发工具类详解
1. CountDownLatch 示例
CountDownLatch latch = new CountDownLatch(3);
// 工作线程
new Thread(() -> {
// 执行任务
latch.countDown();
}).start();
// 等待所有任务完成
latch.await();
2. CyclicBarrier 示例
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
// 所有线程到达屏障后执行
System.out.println("All threads have reached the barrier");
});
// 在线程中使用
barrier.await();
3. Semaphore 示例
Semaphore semaphore = new Semaphore(5);
void accessResource() throws InterruptedException {
semaphore.acquire();
try {
// 访问受限资源
} finally {
semaphore.release();
}
}
线程安全的集合类
1. ConcurrentHashMap
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key", 1);
map.putIfAbsent("key", 2);
2. CopyOnWriteArrayList
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("item");
3. BlockingQueue 实现类
ArrayBlockingQueue<Task> queue = new ArrayBlockingQueue<>(100);
LinkedBlockingQueue<Task> unboundedQueue = new LinkedBlockingQueue<>();
死锁问题及预防
死锁示例
class DeadlockRisk {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
void method1() {
synchronized(lock1) {
synchronized(lock2) {
// 可能导致死锁
}
}
}
void method2() {
synchronized(lock2) {
synchronized(lock1) {
// 可能导致死锁
}
}
}
}
死锁预防措施
- 固定加锁顺序
- 使用超时锁
- 避免嵌套锁
- 使用 tryLock()
class DeadlockPrevention {
private final Lock lock1 = new ReentrantLock();
private final Lock lock2 = new ReentrantLock();
void safeMethod() {
if (lock1.tryLock()) {
try {
if (lock2.tryLock()) {
try {
// 操作
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
}
}
性能优化建议
1. 合理设置线程数
int threadCount = Runtime.getRuntime().availableProcessors() + 1;
2. 使用线程局部变量
private static ThreadLocal<SimpleDateFormat> dateFormat =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
3. 避免锁竞争
- 减小锁粒度
- 使用乐观锁
- 采用无锁数据结构
4. 正确使用 volatile
public class SafePublication {
private volatile boolean flag = false;
private int data;
public void prepare() {
data = 42;
flag = true;
}
public void use() {
if (flag) {
// 安全地使用 data
}
}
}
调试与监控
1. 线程转储(Thread Dump)分析
Thread.getAllStackTraces().forEach((thread, stackTrace) -> {
System.out.println(thread.getName());
for (StackTraceElement element : stackTrace) {
System.out.println("\t" + element);
}
});
2. JMX 监控
- 线程数量监控
- 线程状态监控
- 死锁检测
3. 性能分析工具
- JVisualVM
- JMC (Java Mission Control)
- Arthas
实际应用场景示例
1. 生产者-消费者模式
class ProducerConsumer {
private final BlockingQueue<Task> queue;
void producer() {
while (true) {
Task task = createTask();
queue.put(task);
}
}
void consumer() {
while (true) {
Task task = queue.take();
processTask(task);
}
}
}
2. 并行计算
class ParallelCalculation {
void calculate(List<Integer> numbers) {
ForkJoinPool pool = new ForkJoinPool();
int result = pool.submit(() ->
numbers.parallelStream()
.mapToInt(i -> i)
.sum()
).get();
}
}
3. 异步任务处理
class AsyncProcessor {
private final ExecutorService executor = Executors.newFixedThreadPool(10);
CompletableFuture<Result> processAsync(Task task) {
return CompletableFuture.supplyAsync(() -> {
// 处理任务
return new Result();
}, executor);
}
}
参考资源
- Java Concurrency in Practice (Brian Goetz)
- Java 并发编程实战
- Oracle Java Documentation
- Java 线程与并发编程实践
总结
多线程编程是现代软件开发中不可或缺的技术,它能够显著提升程序的性能和响应性。但同时,多线程编程也带来了诸多挑战,需要开发者深入理解其原理,并谨慎使用各种同步机制来确保程序的正确性和性能。
相关文档:
- 线程池详解 - 深入了解线程池的使用和配置
