线程
线程状态转换
线程在生命周期中会经历以下六种状态:
- 新建 (New): 线程被创建,但尚未启动 (还未调用
start()
方法)。 - 可运行 (Runnable): 线程可以被 CPU 调度执行。可能正在运行 (
Running
),也可能在等待 CPU 时间片 (Ready
)。 - 阻塞 (Blocking): 线程等待获取排它锁,例如等待
synchronized
关键字或Lock
锁。当线程获得锁后,会从阻塞状态解除。 - 无限期等待 (Waiting): 线程进入等待状态,必须等待其他线程显式地唤醒。
- 进入方法:
- 不带 timeout 参数的
Object.wait()
- 不带 timeout 参数的
Thread.join()
LockSupport.park()
- 不带 timeout 参数的
- 退出方法:
Object.notify() / Object.notifyAll()
(唤醒等待Object
锁的线程)- 被
join()
的线程执行完毕 LockSupport.unpark()
(唤醒被park()
挂起的线程)
- 进入方法:
- 限期等待 (Timed Waiting): 线程等待一段时间后,无需其他线程显式唤醒,系统会自动唤醒。
- 进入方法:
Thread.sleep(timeout)
- 带 timeout 参数的
Object.wait(timeout)
- 带 timeout 参数的
Thread.join(timeout)
LockSupport.parkNanos(timeout)
LockSupport.parkUntil(deadline)
- 退出方法:
- 等待时间结束
Object.notify() / Object.notifyAll()
- 被
join()
的线程执行完毕 LockSupport.unpark()
- 进入方法:
- 死亡 (Terminated): 线程执行完成或因异常终止。
状态对比:
- 阻塞 vs. 等待: 阻塞是被动等待锁,等待是主动等待唤醒或超时。
- 睡眠 vs. 挂起: 睡眠 (
sleep()
) 和 挂起 (wait()
) 都是使线程暂停执行的行为,阻塞和等待是描述线程状态。
线程使用方式
Java 中创建线程的三种方式:
-
实现
Runnable
接口: 将任务逻辑放在run()
方法中。``` public class MyRunnable implements Runnable { public void run() { // 任务逻辑 } }
MyRunnable instance = new MyRunnable(); Thread thread = new Thread(instance); thread.start(); ```
-
实现
Callable
接口: 与Runnable
类似,但call()
方法可以有返回值,通过FutureTask
获取。``` public class MyCallable implements Callable
{ public Integer call() { return 123; } } MyCallable mc = new MyCallable(); FutureTask
ft = new FutureTask<>(mc); Thread thread = new Thread(ft); thread.start(); int result = ft.get(); // 获取返回值 ``` -
继承
Thread
类: 继承Thread
类并重写run()
方法。``` public class MyThread extends Thread { public void run() { // 任务逻辑 } }
MyThread mt = new MyThread(); mt.start(); ```
选择 Runnable/Callable
接口 vs. 继承 Thread
: 推荐实现接口,更灵活,避免单继承限制,降低类耦合性。
基础线程机制
-
Executor 框架: 管理线程池,简化线程生命周期管理,异步执行任务。
CachedThreadPool
: 按需创建线程,线程可重用。FixedThreadPool
: 固定大小线程池,控制并发数。SingleThreadExecutor
: 单线程池,顺序执行任务。
ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(new MyRunnable()); // 提交 Runnable 任务 executorService.shutdown(); // 关闭线程池
-
守护线程 (Daemon Thread): 后台服务线程,程序结束时守护线程会被终止。
main()
线程是非守护线程。使用thread.setDaemon(true)
设置。 -
sleep()
: 线程休眠指定毫秒数,让出 CPU,不释放锁。可能抛出InterruptedException
异常 (必须处理)。 -
yield()
: 建议线程调度器让当前线程让出 CPU 给同优先级线程,仅为建议,不保证生效。
线程中断
interrupt()
方法: 中断线程,设置中断标志。- 如果线程处于
阻塞/限期等待/无限期等待
状态,抛出InterruptedException
异常,提前结束线程。 - 无法中断 I/O 阻塞和
synchronized
锁阻塞。
- 如果线程处于
-
interrupted()
方法: 检查线程是否被中断 (中断标志是否被设置)。常用于循环中判断是否需要结束线程。 -
Executor 的中断:
shutdown()
: 平滑关闭,等待所有任务执行完成。shutdownNow()
: 立即关闭,尝试中断所有正在执行的线程 (interrupt()
)。Future.cancel(true)
: 中断Executor
中单个线程。
线程互斥同步
Java 提供 synchronized
关键字和 ReentrantLock
类实现线程互斥同步。
-
synchronized
关键字: JVM 实现,隐式锁,自动释放锁。-
同步代码块:
synchronized (this | object)
,锁定对象实例或指定对象。public void func1() { synchronized (this) { // 锁当前对象实例 // 同步代码 } }
-
同步方法:
public synchronized void func () {}
,锁定对象实例。 -
同步类:
synchronized (SynchronizedExample.class)
,锁定整个类。Java
public void func2() { synchronized (SynchronizedExample.class) { // 锁类对象 // 同步代码 } }
-
同步静态方法:
public synchronized static void fun() {}
,锁定整个类。
-
-
ReentrantLock
类: JDK 实现,显式锁,需手动lock()
和unlock()
(通常在finally
块中释放,避免死锁)。Java
``` private Lock lock = new ReentrantLock();
public void func() { lock.lock(); try { // 同步代码 } finally { lock.unlock(); } } ```
synchronized
vs. ReentrantLock
:
特性 | synchronized |
ReentrantLock |
---|---|---|
实现 | JVM | JDK |
性能 | JDK 优化后相近 | JDK 优化后相近 |
等待可中断 | 否 | 是 |
公平锁 | 非公平 | 默认非公平,可设为公平 |
锁绑定条件数 | 1 | 多个 Condition 对象 |
锁释放 | 自动 | 手动 (unlock() ) |
选择建议: 优先使用 synchronized
,除非需要 ReentrantLock
的高级特性 (如公平锁、可中断、多条件)。
线程之间的协作
线程协作解决多线程任务间的执行顺序和数据共享问题。
-
join()
方法: 线程 A 中调用线程 B 的join()
,线程 A 进入等待状态,直到线程 B 结束才继续执行。保证线程执行顺序。A a = new A(); B b = new B(a); b.start(); // B 线程启动 a.start(); // A 线程启动 (但 B 中 join(a) 会等待 A 先执行完)
-
wait()
,notify()
,notifyAll()
方法: 对象监视器方法,用于线程间条件等待和唤醒。必须在synchronized
代码块或方法中使用。wait()
释放锁,sleep()
不释放锁。``` public synchronized void before() { notifyAll(); // 唤醒等待线程 }
public synchronized void after() { wait(); // 等待被唤醒 } ```
-
await()
,signal()
,signalAll()
方法:Condition
对象的方法,提供更灵活的条件等待和唤醒机制,需配合ReentrantLock
使用。``` private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition();
public void before() { lock.lock(); condition.signalAll(); // 唤醒等待线程 lock.unlock(); }
public void after() { lock.lock(); condition.await(); // 等待被唤醒 lock.unlock(); } ```
wait()
vs. sleep()
:
特性 | wait() |
sleep() |
---|---|---|
方法归属 | Object |
Thread |
锁释放 | 释放锁 | 不释放锁 |
使用场景 | 线程间协作 | 线程休眠 |
线程的状态及转换
Java线程有6种状态:
-
NEW(新建)
- 创建线程对象后,尚未调用start()方法
- 转换:调用
start()
→RUNNABLE -
RUNNABLE(可运行)
-
线程已启动,正在执行或等待CPU资源
- 转换:
- 获取锁失败→BLOCKED
- 调用
wait()
/join()
/LockSupport.park()
→WAITING - 调用
sleep(time)
/wait(time)
→TIMED_WAITING - 任务执行完毕→TERMINATED
-
BLOCKED(阻塞)
-
线程等待获取监视器锁进入同步区域
- 转换:获取到锁→RUNNABLE
-
WAITING(等待)
-
无限期等待其他线程执行特定操作
- 转换:
- 对于
wait()
:notify()
/notifyAll()
→RUNNABLE/BLOCKED - 对于
join()
:目标线程结束→RUNNABLE - 对于
LockSupport.park()
:unpark()
→RUNNABLE
- 对于
-
TIMED_WAITING(限时等待)
-
有限时间的等待状态
- 转换:
- 等待时间结束→RUNNABLE
- 提前收到通知→RUNNABLE
-
TERMINATED(终止)
-
线程完成执行
线程的使用方式
-
继承Thread类
Java
class MyThread extends Thread { public void run() { /* 代码 */ } } // 使用:new MyThread().start();
-
实现Runnable接口(推荐)
Java
class MyTask implements Runnable { public void run() { /* 代码 */ } } // 使用:new Thread(new MyTask()).start();
-
实现Callable接口(可返回结果)
Java
class MyCallable implements Callable<String> { public String call() throws Exception { return result; } } // 使用: FutureTask<String> task = new FutureTask<>(new MyCallable()); new Thread(task).start(); String result = task.get(); // 获取结果,会阻塞
-
使用线程池
Java
ExecutorService executor = Executors.newFixedThreadPool(10); executor.submit(() -> { /* 任务代码 */ });
-
使用CompletableFuture(Java 8+)
Java
CompletableFuture.supplyAsync(() -> /* 任务代码 */) .thenAccept(result -> /* 处理结果 */);
基础线程机制
-
线程池
- ThreadPoolExecutor:自定义线程池
- Executors工厂类:提供常用线程池实现
-
线程控制
-
sleep()
:线程休眠指定时间,不释放锁 yield()
:提示线程调度器让出CPU执行权join()
:等待目标线程执行完成-
线程优先级
-
setPriority(int)
:设置优先级(1-10),仅为建议性 -
守护线程
-
setDaemon(true)
:设置为守护线程,JVM不会等待守护线程结束
线程的中断方式
-
通过中断标志
thread.interrupt()
:设置中断标志thread.isInterrupted()
:检查中断状态(不清除)Thread.interrupted()
:检查当前线程中断状态(会清除)- 通过共享变量
Java
private volatile boolean running = true; public void run() { while (running) { // 任务代码 } } // 中断:running = false;
-
通过Future取消
Java
Future<?> future = executor.submit(task); future.cancel(true); // 尝试中断正在执行的任务
线程的互斥同步方式
-
synchronized关键字
- 特点:Java内置锁,自动获取/释放
synchronized (object) { /* 同步块 */ } public synchronized void method() { /* 同步方法 */ }
-
ReentrantLock
- 特点:显式锁,更灵活,需手动释放
Lock lock = new ReentrantLock(); lock.lock(); try { // 临界区代码 } finally { lock.unlock(); // 必须在finally中释放锁 }
-
ReadWriteLock
- 特点:读写分离,适合读多写少场景
ReadWriteLock rwLock = new ReentrantReadWriteLock(); // 读操作:多线程可同时获取 rwLock.readLock().lock(); // 写操作:独占锁 rwLock.writeLock().lock();
-
StampedLock(Java 8+)
- 特点:支持乐观读,性能更好
如何选择?
- 简单场景:使用
synchronized
(代码简洁,JVM优化) - 需要高级特性:使用
ReentrantLock
(可中断、限时、公平性) - 读多写少:使用
ReadWriteLock
或StampedLock
线程之间的协作方式
-
wait/notify机制
``` synchronized (obj) { while (!condition) { obj.wait(); // 释放锁并等待 } // 条件满足,继续执行 }
// 另一个线程 synchronized (obj) { // 改变条件 obj.notify(); // 或 obj.notifyAll(); } ```
-
Condition接口
``` Lock lock = new ReentrantLock(); Condition condition = lock.newCondition();
lock.lock(); try { while (!conditionMet) { condition.await(); } } finally { lock.unlock(); }
// 另一个线程 lock.lock(); try { // 改变条件 condition.signal(); // 或 condition.signalAll(); } finally { lock.unlock(); } ```
-
CountDownLatch(一次性同步点)
``` CountDownLatch latch = new CountDownLatch(3); // 等待线程 latch.await(); // 等待计数器归零
// 工作线程 latch.countDown(); // 完成工作,计数器减1 ```
-
CyclicBarrier(可重用同步点)
CyclicBarrier barrier = new CyclicBarrier(3); // 每个线程 barrier.await(); // 等待所有线程到达
-
Phaser(灵活的多阶段同步)
-
Semaphore(控制并发数)
Semaphore semaphore = new Semaphore(5); semaphore.acquire(); // 获取许可 try { // 访问受限资源 } finally { semaphore.release(); // 释放许可 }
-
Exchanger(线程间交换数据)
-
阻塞队列(生产者-消费者模式)
BlockingQueue<T> queue = new LinkedBlockingQueue<>(); // 生产者线程 queue.put(item); // 可能阻塞 // 消费者线程 T item = queue.take(); // 可能阻塞
通过合理选择和组合这些机制,可以有效解决不同场景下的线程协作问题。