第一,使用 synchronized 加锁虽然简单,但我们首先要弄清楚共享资源是类还是实例级别的、会被哪些线程操作,synchronized 关联的锁对象或方法又是什么范围的。
第二,加锁尽可能要考虑粒度和场景,锁保护的代码意味着无法进行多线程操作。对于Web 类型的天然多线程项目,对方法进行大范围加锁会显著降级并发能力,要考虑尽可能地只为必要的代码块加锁,降低锁的粒度;而对于要求超高性能的业务,还要细化考虑锁的读写场景,以及悲观优先还是乐观优先,尽可能针对明确场景精细化加锁方案,可以在适当的场景下考虑使用 ReentrantReadWriteLock、StampedLock 等高级的锁工具类。
第三,业务逻辑中有多把锁时要考虑死锁问题,通常的规避方案是,避免无限等待和循环等待。此外,如果业务逻辑中锁的实现比较复杂的话,要仔细看看加锁和释放是否配对,是否有遗漏释放或重复释放的可能性;并且要考虑锁自动超时释放了,而业务逻辑却还在进行的情况下,如果别的线线程或进程拿到了相同的锁,可能会导致重复执行。
常用锁问题:
一是加锁和释放没有配对的问题
加群解锁没有配对可以用一些代码质量工具协助排插,如Sonar,集成到ide和代码仓库,在编码阶段发现,加上超时自动释放,避免长期占有锁
二是锁自动释放导致的重复逻辑执行的问题。
1. 避免超时,单独开一个线程给锁延长有效期。比如设置锁有效期30s,有个线程每隔10s重新设置下锁的有效期。
2. 避免重复,业务上增加一个标记是否被处理的字段。或者开一张新表,保存已经处理过的流水号。
3.可以用锁续期,如redisson的watchdog;或者保证业务的幂等性,重复执行也没问题。