findBug工具-Synchronization 相关问题的解决
1. findBug工具-Synchronization on Boolean(在Boolean类型的对象上使用了Synchronize关键字)
Bug原因:
在出现并发时,通过同步锁使得只能由一个线程延迟加载一个或一组对象供后续使用(例如一个巨大的List或需要缓存的对象等等)。 然而,此前竟然从未意识到可能存在的严重问题。
其中,规则制定者一书的作者认为错误地在基本类型上使用synchronize关键字是导致并发问题的常见原因之一,可能导致死锁或其他不可预测的结果,并将其抽象为不应该在任何可能被重用的对象[包括可能在JVM内部重用的对象]上使用.
即Boolean类型的对象不可用于synchronization同步锁。其问题的实质在于Boolean.TRUE和Boolean.FALSE这两个常量在JVM中实际是java.lang.Boolean类的两个静态成员变量,因而可能在程序中被多处引用。例如,当上例中的inited指向Boolean.FALSE时,如果有其他的同步代码块在无意中使用了相同的Boolean常量,那么就有可能导致死锁。
2. findBug工具-Synchronizes on a boxed primitive object(在装箱基本类型的对象上使用了Synchronize关键字)
上述代码将int类型的count自动装箱为Interger包装类型的对象Lock,然后使用synchronize关键字对包装类型的Lock变量加锁。可以预见,出于存储和性能等等的考虑,在自动装箱时JVM内部必然会重用具有相同值类型的包装类,因而Lock指向的对象极有可能被重用,进而在后续引发与Boolean类型变量存在的相同问题
3. findBug工具-Synchronizes on a interned String lock object(在内部字符串对象上使用了Synchronize关键字)
根据Java API文档,intern()方法实际是返回对象池中的对象,因而调用intern()方法后获得到对象相当于JVM中的一个全局变量。
所以,即便是像上述错误代码中使用private和final关键字修饰lock变量,lock变量指向的仍然是同一个可能被重用的字符串常量。这种情况所带来的问题与之前提到的两种问题类似。
4. findBug工具-Synchronizes on a String Literal(在字符串常量上使用了Synchronize关键字)
基于之前的解释很容易理解第四种情况,即JVM中的字符串常量是全局重用的。
终极解决方案
- 使用字符串实例
与字符串常量不同,字符串实例的引用是唯一的,因而不存在字符串常量可能导致的问题。
- 使用private final类型的java.lang.Object
CERT规范中提到,这种解决方法是少数可以直接利用到java.lang.Object的情况。在此处之所以强调使用Raw Object,同时还在CERT规范的原文中多次提到《Use private final lock objects to synchronize classes that may interact with untrusted code》,是由于不使用Raw Object可能导致被exploit的问题,在此不再赘述。