前言
Java 多线程编程是提高程序性能和响应能力的关键技术,但也涉及到复杂的并发问题。以下是一个结构化的知识总结。
线程与进程
- 进程:独立运行的程序实例,拥有独立的内存空间。
- 线程:进程内的执行单元,共享进程的资源,切换开销更小。
- 区别:进程间隔离,线程共享内存;线程更轻量
创建线程的三种方式
继承 Thread
类
1 | class MyThread extends Thread { |
实现 Runnable
接口(推荐,避免单继承限制)
1 | class MyRunnable implements Runnable { |
实现 Callable
接口(可返回结果,配合 FutureTask
使用)
1 | class MyCallable implements Callable<String> { |
线程生命周期(状态)
- 新建(New):线程对象创建后未启动。
- 就绪(Runnable):调用
start()
后,等待CPU调度。 - 运行(Running):执行
run()
方法。 - 阻塞(Blocked):等待锁(如进入同步代码块)。
- 等待(Waiting):无期限等待,需其他线程唤醒(如
Object.wait()
)。 - 超时等待(Timed Waiting):有限时间等待(如
Thread.sleep(ms)
)。 - 终止(Terminated):
run()
执行完毕或异常退出。
线程同步与通信
同步机制
synchronized
关键字:修饰方法或代码块,确保原子性。Lock
接口(如ReentrantLock
):更灵活,支持尝试锁、公平锁。volatile
变量:保证可见性,禁止指令重排序,但不保证原子性。
线程通信
wait()/notify()/notifyAll()
:需在同步块内调用,避免虚假唤醒。BlockingQueue
:线程安全队列,实现生产者-消费者模式。
线程池(Executor框架)
核心类
ExecutorService
:管理线程池生命周期。ThreadPoolExecutor
:可配置核心参数(核心线程数、最大线程数、队列、拒绝策略)。
常见线程池
FixedThreadPool
:固定线程数,适用于负载稳定场景。CachedThreadPool
:线程数弹性伸缩,适合短时异步任务。SingleThreadExecutor
:单线程顺序执行任务。
拒绝策略
如 AbortPolicy
(抛异常)、CallerRunsPolicy
(调用者执行任务)。
并发工具类
ConcurrentHashMap
:分段锁实现高并发访问。CopyOnWriteArrayList
:写时复制,读操作无锁。- 原子类(如
AtomicInteger
):CAS操作实现无锁线程安全。 CountDownLatch
:等待多个任务完成。CyclicBarrier
:多线程到达屏障后继续执行。
高级主题
- Fork/Join框架:分治策略,适用于可拆分的计算密集型任务。
CompletableFuture
(Java 8+):链式异步编程,支持组合多个异步操作。- ThreadLocal:线程局部变量,需注意内存泄漏(使用后及时调用
remove()
)。
避免并发问题
- 死锁预防:避免嵌套锁、按顺序获取锁、设置超时(如
tryLock
)。 - 内存可见性:使用
volatile
或同步机制确保修改可见。 - 资源竞争:优先使用线程安全的数据结构(如
ConcurrentHashMap
)。
调试与监控
- 线程转储(Thread Dump):通过
jstack
或可视化工具(如 VisualVM)分析死锁。 - 日志记录:跟踪线程执行流程,定位竞态条件。
后记
在多线程的应用中,尽量做到:
- 减少锁粒度:尽量缩小同步代码块。
- 使用线程池:避免频繁创建/销毁线程。
- 避免共享状态:优先使用不可变对象或线程局部变量。
- 优先使用高层抽象:如
Executor
、Concurrent Collections
,而非直接操作线程。