线程安全问题本质
当多个线程并发操作共享资源(变量/对象)时,可能因非原子性操作或内存可见性问题导致数据不一致。
解决方案一:synchronized 关键字
实现方式:
-
实例方法同步锁
在实现Runnable
接口的自定义线程类中,对操作共享资源的代码块使用同步锁:public void run() { synchronized(this) { // 锁住当前实例对象 // 操作共享资源的代码 } }
-
静态资源同步锁
若操作静态变量,需使用类级别的锁:synchronized(MyThread.class) { // 锁住类的Class对象 // 操作静态变量的代码 }
注意事项:
- 同步范围应尽量缩小(锁粒度最小化)以提高并发效率
- 避免在构造方法中使用同步(可能导致对象未完全初始化)
解决方案二:Lock 显式锁
实现步骤:
-
创建
ReentrantLock
实例(推荐成员变量)private final Lock lock = new ReentrantLock();
-
显式加锁与释放(必须保证释放)
public void run() { lock.lock(); // 手动加锁 try { // 操作共享资源的代码 } finally { lock.unlock(); // 必须放在finally块确保释放 } }
优势:
- 支持可中断锁(
lockInterruptibly()
) - 支持超时获取锁(
tryLock(long, TimeUnit)
) - 支持公平锁策略(构造函数传
true
)
最佳实践建议
-
优先使用synchronized
简单场景下更安全(自动释放锁),JVM会优化锁升级(偏向锁->轻量级锁->重量级锁) -
需要精细控制时选择Lock
当需要以下特性时:- 尝试获取锁(tryLock)
- 等待锁可中断
- 需要公平锁机制
- 需要绑定多个Condition
-
性能考量
JDK1.6+版本中两者性能差异不大,应优先考虑代码可维护性 -
避免嵌套锁
防止死锁,加锁顺序需全局统一