线程
线程:多任务编程的核心机制一、线程的基本概念
线程(Thread)是操作系统调度的最小执行单元,是进程内部独立运行的控制流。每个线程共享进程的内存空间和系统资源,但拥有独立的程序计数器、栈和寄存器集合。线程间通过共享内存进行通信,其上下文切换开销远低于进程切换。
线程与进程的对比:
| 特性 | 进程 | 线程 |
|---------------|--------------------|--------------------|
| 资源开销 | 独立内存空间 | 共享进程资源 |
| 通信机制 | 进程间通信(IPC)| 共享内存 |
| 切换成本 | 高(需切换地址空间)| 低(共享地址空间) |
| 稳定性 | 隔离性强 | 一处崩溃影响全局 |
二、线程生命周期与状态转换
线程的生命周期包含五个核心状态:
1. 新建(New):线程对象创建但未启动
2. 就绪(Runnable):等待CPU调度
3. 运行(Running):执行线程代码
4. 阻塞(Blocked):等待监视器锁或IO操作
5. 终止(Terminated):线程执行完成或异常退出
在Java虚拟机中,线程状态被细化为6种(通过Thread.State枚举):
java
public enum State {
NEW, RUNNABLE, BLOCKED, WAITING, TIMEDWAITING, TERMINATED
}
状态转换示意图:
NEW → RUNNABLE ⇄ BLOCKED/WAITING/TIMEDWAITING → TERMINATED
三、线程同步与通信机制
1. 同步控制
竞态条件(Race Condition):当多个线程同时访问共享资源且结果依赖执行顺序时,会产生数据不一致问题。例如:
java
// 线程不安全的计数器
public class Counter {
private int count = 0;
public void increment() { count++; } // 非原子操作
}
解决方案:
- synchronized关键字:基于对象监视器的隐式锁
java
public synchronized void increment() {
count++; // 原子性保证
}
Lock接口:提供更灵活的锁机制
java
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try { count++; }
finally { lock.unlock(); }
}
2. 线程通信
wait/notify机制:
java
// 生产者-消费者示例
synchronized void produce() {
while (available) {
wait(); // 释放锁并等待
}
// 生产产品
available = true;
notify(); // 唤醒消费者
}
Condition接口:替代传统wait/notify的高级API
java
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
四、线程池与任务调度
1. 线程池优势
- 降低线程创建销毁的开销
控制最大并发数
提供任务队列管理
支持定时/周期性任务
2. Java线程池实现
java
// 固定大小线程池
ExecutorService executor = Executors.newFixedThreadPool(4);
// 缓存线程池(自动扩容)
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 单线程池
ExecutorService singlePool = Executors.newSingleThreadExecutor();
核心参数配置:
java
new ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
);
五、常见并发问题与解决方案
1. 死锁(Deadlock)
四个必要条件:
- 互斥
持有并等待
不可抢占
- 循环等待
检测工具:
- jstack命令
VisualVM
JConsole
预防策略:
java
// 资源有序申请
void transfer(Account from, Account to) {
Account first = from.getId() < to.getId() ? from : to;
Account second = from.getId() < to.getId() ? to : from;
synchronized(first) {
synchronized(second) {
// 安全执行转账
}
}
}
2. 资源饥饿(Starvation)
低优先级线程无法获得CPU时间片
解决方案:合理设置线程优先级,避免使用Thread.MINPRIORITY
3. 线程泄漏(Thread Leak)
避免无限等待:使用带超时的join(3000)或tryLock(1, TimeUnit.SECONDS)
- 正确关闭线程池:
java
executor.shutdown(); // 平滑关闭
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 强制关闭
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
六、并发工具类与最佳实践
1. Java并发包(java.util.concurrent)
原子变量:AtomicInteger, AtomicReference
并发集合:ConcurrentHashMap, CopyOnWriteArrayList
同步器:
- CountDownLatch:倒计时门闩
- CyclicBarrier:循环屏障
- Semaphore:资源访问控制
2. 编码规范
1. 避免过度同步:仅对关键代码加锁
2. 优先使用ReentrantLock替代synchronized
3. 使用volatile保证可见性:
java
private volatile boolean running = true;
4. 采用不可变对象(Immutable)消除同步需求
3. 性能优化
减少锁粒度:使用ReadWriteLock分离读写操作
- 锁粗化:合并相邻同步代码块
- 避免伪共享(False Sharing):使用@Contended注解填充缓存行
七、线程安全的单例模式实现
双重检查锁定(DCL):
java
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // 非原子操作
}
}
}
return instance;
}
}
基于类加载机制的实现:
java
public class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
结语
线程编程是提升系统吞吐量和响应性的关键手段,但需要平衡并发效率与安全性。建议开发者:
1. 优先使用高级并发工具类
2. 遵循最小同步原则
3. 进行充分的并发测试(压力测试+代码审查)
4. 利用现代JVM的诊断工具进行问题排查
通过合理设计线程模型和资源调度策略,可以构建出高效稳定的并发系统。建议结合《Java并发编程实战》等专业书籍深入学习,通过实际项目积累经验。
[本文内容由人工智能AI辅助生成,仅供参考]
页:
[1]