Java 多线程进阶——并发安全、死锁与生产者消费者模型
- 为什么需要线程同步?
当多个线程同时修改同一个变量(例如银行账户余额)时,可能会发生“数据覆盖”或“读取脏数据”的情况。这就好比两个人同时去改同一份文档,如果不加锁,最后的结果肯定是一团糟。
为了解决这个问题,Java 提供了线程同步机制。
- 给代码加把锁:synchronized
synchronized是Java中最常用的同步关键字,它能保证同一时刻只有一个线程能执行被锁住的代码块。
同步方法:直接在方法声明上加 synchronized ,锁的是当前对象 ( this )。
同步代码块: synchronized(obj) { ... } ,锁的是括号里的对象 obj 。
原理:这就像上厕所,门上的锁就是 synchronized 。一个人进去了(获取锁),其他人只能在外面等(阻塞),直到里面的人出来(释放锁)。
- 警惕!多线程的噩梦——死锁
死锁是指两个或多个线程互相持有对方需要的资源,并且都在等待对方释放,导致所有线程都无法继续执行。
死锁产生的四个必要条件:
互斥条件:资源只能被一个线程占用。
请求与保持条件:占着碗里的,看着锅里的。
不剥夺条件:资源只能由持有者主动释放。
循环等待条件:A 等 B,B 等 A,形成闭环。
避免死锁最简单的方法是破坏“循环等待条件”,比如规定所有线程必须按照相同的顺序获取锁。
- 经典实战:生产者消费者模型
场景:有一个仓库(缓冲区),生产者负责生产产品放入仓库,消费者负责从仓库取走产品。
规则:
仓库满了,生产者必须停止生产(等待)。
仓库空了,消费者必须停止消费(等待)。
一旦有变动,需要通知对方。
核心方法配合:
wait() :线程进入等待状态,释放锁。
notify() / notifyAll() :唤醒正在等待的线程。
在这个模型中,我们通常使用 Object 类的 wait/notify 机制,或者使用更高级的 BlockingQueue (阻塞队列)来实现解耦。
浙公网安备 33010602011771号