java多线程
⚙️ 一、线程基础概念
-
线程与进程
- 进程:操作系统资源分配的基本单位(如独立内存空间),例如一个运行的Java程序。
- 线程:CPU调度的最小单位,共享进程资源(堆内存),多线程可并发执行任务。
- 核心价值:提升吞吐量(并行处理)、增强响应性(避免阻塞)、高效利用资源(如I/O等待时执行其他任务)。
-
线程生命周期
- 新建(NEW):通过
new Thread()
创建但未启动。 - 可运行(RUNNABLE):调用
start()
后进入就绪状态,等待CPU调度。 - 阻塞/等待:包括
BLOCKED
(锁竞争)、WAITING
(无限期等待)、TIMED_WAITING
(超时等待)。 - 终止(TERMINATED):
run()
执行完毕或异常退出。
- 新建(NEW):通过
🛠️ 二、线程创建方式
方式 | 示例代码 | 适用场景 | 优缺点 |
---|---|---|---|
继承Thread类 | class MyThread extends Thread { run() {...} } new MyThread().start(); |
简单逻辑 | ❌ 单继承限制,资源共享需静态变量 |
实现Runnable接口 | class MyTask implements Runnable { run() {...} } new Thread(task).start(); |
需资源共享、避免单继承限制 | ✅ 推荐,逻辑与线程控制分离 |
实现Callable接口 | Callable<String> task = () -> "结果"; Future<String> future = executor.submit(task); |
需返回值、异步任务 | ✅ 支持Future获取结果,可配合线程池 |
💡 关键区别:
Runnable
无返回值,Callable
有返回值(通过Future.get()
获取)。start()
启动新线程,run()
直接调用则变为普通方法(仍在当前线程)。
🔒 三、线程安全与同步
-
问题根源
- 竞态条件:多线程同时修改共享数据(如
count++
非原子操作)。 - 可见性问题:线程缓存导致数据修改未及时同步到主存。
- 有序性问题:指令重排打乱执行顺序。
- 竞态条件:多线程同时修改共享数据(如
-
同步解决方案
-
synchronized
关键字- 修饰实例方法:锁对象为
this
。 - 修饰静态方法:锁对象为类Class。
- 同步代码块:
synchronized(lockObj) { ... }
。
- 修饰实例方法:锁对象为
-
ReentrantLock
- 更灵活:支持公平锁、可中断锁、尝试获取锁(
tryLock()
)。
private final ReentrantLock lock = new ReentrantLock(); lock.lock(); try { /* 临界区 */ } finally { lock.unlock(); }
- 更灵活:支持公平锁、可中断锁、尝试获取锁(
- 原子类:
AtomicInteger
、AtomicReference
等,基于CAS实现无锁安全操作。 -
volatile
:仅保证可见性和禁止指令重排,不保证原子性(适用一写多读场景)。
-
⚡️ 四、线程池(核心实践)
-
为何使用线程池?
- 避免频繁创建/销毁线程的开销(类比“招聘/解雇员工成本高”)。
- 控制并发线程数,防止资源耗尽。
-
常用线程池(通过
Executors
创建)类型 特点 newFixedThreadPool(n)
固定大小,任务队列无界 newCachedThreadPool()
线程数弹性伸缩,空闲线程60秒回收 newSingleThreadExecutor()
单线程串行执行 newScheduledThreadPool(n)
支持定时/周期性任务 -
基础用法
ExecutorService pool = Executors.newFixedThreadPool(4); pool.execute(() -> System.out.println("任务执行")); pool.shutdown(); // 优雅关闭(等待已提交任务完成)
🚀 五、高级特性与工具类
-
线程通信
-
wait()/notify()
:需在synchronized
块内调用,wait()
释放锁并等待,notify()
唤醒一个等待线程(notifyAll()
唤醒所有)。 - 典型场景:生产者-消费者模型(需配合条件判断避免虚假唤醒)。
-
-
并发工具类
-
CountDownLatch
:倒计时门闩(等待多个任务完成)。 -
CyclicBarrier
:循环栅栏(线程到达屏障后统一执行后续逻辑)。 -
Semaphore
:信号量(控制并发访问资源数)。
-
-
Fork/Join框架
- 分治思想:将大任务拆分为子任务并行执行,再合并结果(如归并排序)。
⚠️ 六、常见问题与规避
-
死锁
- 条件:互斥、持有并等待、不可抢占、循环等待。
- 规避:按固定顺序获取锁、设置锁超时(
tryLock(timeout)
)、避免嵌套锁。
-
性能优化
- 缩小同步范围(减少锁竞争)。
- 优先使用线程安全集合(如
ConcurrentHashMap
)。 - 避免过度创建线程(通过线程池控制)。
💎 总结建议
- 优先选择:
Runnable
+ 线程池,资源利用高效且易管理。 - 同步机制:简单场景用
synchronized
,复杂需求(如超时控制)选ReentrantLock
。 - 避坑指南:
- 禁止直接调用
run()
代替start()
; - 静态方法同步锁是类对象,非静态方法锁是实例对象;
- 高并发场景通过
ThreadLocal
为线程提供独立变量副本(如数据库连接)。
- 禁止直接调用