我在写 《并发场景下数据写入功能的实现》 时提过,在并发场景下,如果存在数据竞争,则需要用锁来保证线程安全。锁会增加编码的复杂度,也会降低代码的执行效率,还潜在死锁、活锁等隐患。活锁,通常加个随机的等待时间,做几次重试就可以避免,故本对“锁的使用”和“死锁的避免”做近一步说明。
1. 锁的使用
锁的使用套路是:
- 在访问共享资源之前,先获取锁
- 如果获取锁成功,则访问共享资源
- 访问结束,释放锁,以便其他线程继续访问共享资源
对代码上锁,可以使用java 关键字 synchronized
或java并发包中的 Lock
,如:
private Object lock = new Object(); public void visitShareResWithLock() { synchronized (lock) { // 在这里安全的访问共享资源 } }
private Lock lock = new ReentrantLock(); public void visitShareResWithLock() { lock.lock(); try { // 在这里安全的访问共享资源 } finally { lock.unlock(); } }
再次强调,锁的使用是有代价的:
- 加锁和解锁过程,需要CPU时间,有性能损耗;锁的使用可能会带来线程等待,降低程序的执行性能
- 锁的使用不当,会造成死锁,且调试不便。
故锁的使用,一定要注意 锁的释放,且只有在并发环境中,共享资源不支持并发访问,或者说并发访问共享资源会导致系统错误的情况下,才需要使用锁 。
具体来说,锁的使用,要注意以下几点:
&#