一、多线程概述
1.1 什么是线程
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含多个线程,这些线程共享进程的资源,但每个线程有自己的程序计数器、栈和局部变量。
1.2 为什么需要多线程
- 提高程序响应性:对于图形界面程序,多线程可以防止界面"冻结"
- 提高CPU利用率:在多核处理器上实现真正的并行计算
- 简化建模:某些问题(如仿真)用多线程建模更自然
- 提高吞吐量:服务器程序可以同时处理多个客户端请求
1.3 Java线程模型
Java从语言层面支持多线程编程,主要通过以下两种方式:
- 继承Thread类
- 实现Runnable接口或Callable接口
Java线程生命周期包括以下状态:
- NEW(新建)
- RUNNABLE(可运行)
- BLOCKED(阻塞)
- WAITING(等待)
- TIMED_WAITING(计时等待)
- TERMINATED(终止)
二、创建线程的三种方式
2.1 继承Thread类
public class MyThread extends Thread {
@Override
public void run() {
// 线程执行的代码
System.out.println("线程运行中: " + Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 启动线程
}
}
特点:
- 简单直接,适合简单场景
- Java不支持多重继承,继承Thread类后不能再继承其他类
- 线程代码和业务代码耦合在一起
2.2 实现Runnable接口
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable线程运行: " + Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
// 使用Lambda表达式简化
new Thread(() -> System.out.println("Lambda线程")).start();
}
}
优点:
- 避免了单继承的限制
- 线程和任务分离,更符合面向对象思想
- 便于线程池管理
2.3 实现Callable接口
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("Callable线程计算中...");
Thread.sleep(2000);
return 1 + 1;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
// 获取计算结果(会阻塞直到计算完成)
Integer result = futureTask.get();
System.out.println("计算结果: " + result);
}
}
特点:
- 可以返回计算结果
- 可以抛出异常
- 通常与FutureTask配合使用
三、线程的生命周期与控制
3.1 线程状态转换
graph TD
A[NEW] -->|start()| B[RUNNABLE]
B -->|等待获取锁| C[BLOCKED]
B -->|wait()/join()| D[WAITING]
B -->|sleep(timeout)/wait(timeout)| E[TIMED_WAITING]
B -->|执行完成| F[TERMINATED]
C -->|获取到锁| B
D -->|notify()/notifyAll()| B
E -->|超时/notify()| B
3.2 线程常用方法
Thread thread = new Thread(() -> {
// 线程代码
});
// 启动线程
thread.start();
// 线程休眠(毫秒)
Thread.sleep(1000);
// 等待线程结束
thread.join();
// 获取当前线程
Thread current = Thread.currentThread();
// 设置线程优先级(1-10)
thread.setPriority(Thread.MAX_PRIORITY);
// 设置守护线程
thread.setDaemon(true);
// 中断线程
thread.interrupt();
// 检查中断状态
Thread.interrupted();
四、线程同步与通信
4.1 同步问题示例
public class UnsafeCounter {
private int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
public static void main(String[] args) throws InterruptedException {
UnsafeCounter counter = new UnsafeCounter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("最终计数: " + counter.getCount()); // 可能不是20000
}
}
4.2 使用synchronized实现同步
public class SafeCounter {
private int count = 0;
// 同步方法
public synchronized void increment() {
count++;
}
// 同步代码块
public void increment2() {
synchronized(this) {
count++;
}
}
// 静态同步方法
public static synchronized void staticMethod() {
// 类锁
}
}
4.3 使用Lock接口
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockCounter {
private int count = 0;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
4.4 线程通信
public class ThreadCommunication {
private boolean flag = false;
public synchronized void producer() throws InterruptedException {
while (flag) {
wait(); // 等待消费者消费
}
System.out.println("生产...");
flag = true;
notifyAll(); // 通知消费者
}
public synchronized void consumer() throws InterruptedException {
while (!flag) {
wait(); // 等待生产者生产
}
System.out.println("消费...");
flag = false;
notifyAll(); // 通知生产者
}
}
五、线程池技术
5.1 为什么使用线程池
- 降低资源消耗:重复利用已创建的线程
- 提高响应速度:任务到达时不需要等待线程创建
- 提高线程可管理性:统一分配、调优和监控
5.2 Executor框架
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) {
// 创建固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
executor.execute(() -> {
System.out.println("线程执行: " + Thread.currentThread().getName());
});
}
executor.shutdown(); // 关闭线程池
}
}
5.3 ThreadPoolExecutor详解
import java.util.concurrent.*;
public class CustomThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
60, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(10), // 工作队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
for (int i = 0; i < 15; i++) {
executor.execute(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务执行: " + Thread.currentThread().getName());
});
}
executor.shutdown();
}
}
六、高级并发工具类
6.1 CountDownLatch
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
new Thread(() -> {
System.out.println("任务1完成");
latch.countDown();
}).start();
new Thread(() -> {
System.out.println("任务2完成");
latch.countDown();
}).start();
new Thread(() -> {
System.out.println("任务3完成");
latch.countDown();
}).start();
latch.await();
System.out.println("所有任务完成");
}
}
6.2 CyclicBarrier
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("所有线程到达屏障点");
});
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
System.out.println("线程到达屏障点");
barrier.await();
System.out.println("线程继续执行");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
6.3 Semaphore
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3); // 允许3个线程同时访问
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println("线程获取许可: " + Thread.currentThread().getName());
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
System.out.println("线程释放许可: " + Thread.currentThread().getName());
}
}).start();
}
}
}
七、线程安全集合
7.1 ConcurrentHashMap
public class ConcurrentHashMapDemo {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 线程安全地添加元素
map.put("key1", 1);
map.putIfAbsent("key1", 2);
// 原子操作
map.compute("key1", (k, v) -> v == null ? 1 : v + 1);
}
}
7.2 CopyOnWriteArrayList
public class CopyOnWriteDemo {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("item1");
list.addIfAbsent("item1"); // 不会重复添加
// 迭代时线程安全
for (String item : list) {
System.out.println(item);
}
}
}
八、最佳实践与常见问题
8.1 多线程最佳实践
- 优先使用线程池:避免频繁创建销毁线程
- 使用高级并发工具:如CountDownLatch、CyclicBarrier等
- 减少同步范围:只同步必要的代码块
- 使用不可变对象:避免线程安全问题
- 避免死锁:按固定顺序获取多个锁
- 处理中断:合理响应中断请求
8.2 常见问题及解决方案
- 死锁:使用工具检测,确保锁获取顺序一致
- 活锁:引入随机性避免重复冲突
- 线程饥饿:合理设置线程优先级
- 内存可见性:使用volatile或同步机制
- 上下文切换开销:避免创建过多线程
8.3 性能调优建议
- 合理设置线程池大小:
- CPU密集型:CPU核数+1
- IO密集型:CPU核数 * (1 + 平均等待时间/平均计算时间)
- 使用合适的并发集合
- 减少锁竞争:使用分段锁、读写锁等
- 避免虚假唤醒:始终在循环中检查条件
九、总结
Java多线程编程是Java开发中非常重要的技能,掌握好多线程技术可以显著提高程序的性能和响应能力。本文从线程创建、生命周期管理、线程同步、线程池使用到高级并发工具等多个方面详细介绍了Java多线程编程的核心知识点。
在实际开发中,建议:
- 优先考虑使用Java并发包(java.util.concurrent)提供的工具类
- 理解并遵循Java内存模型(JMM)的规则
- 编写线程安全代码时,优先考虑不可变对象和无锁算法
- 使用合适的工具进行多线程调试和性能分析
通过不断实践和总结,开发者可以掌握Java多线程编程的精髓,编写出高效、可靠的并发程序。