对装箱类型或字符串进行同步¶
ID: java/sync-on-boxed-types
Kind: problem
Security severity:
Severity: error
Precision: very-high
Tags:
- reliability
- correctness
- concurrency
- language-features
- external/cwe/cwe-662
Query suites:
- java-security-and-quality.qls
代码不应该对装箱类型(例如 Integer
、Boolean
)或类型 String
的变量或字段进行同步,因为它很可能包含在整个程序中使用的对象。例如,Boolean.TRUE
持有一个将在整个程序中的许多地方使用的单个实例:每当 true
被自动装箱或 Boolean.valueOf
使用 true
作为参数被调用时,都会返回 Boolean
的相同实例。因此,两个类对类型为 Boolean
的字段进行同步很可能最终对同一个对象进行同步。这可能导致死锁或线程被不必要地阻塞。
建议¶
对特定锁对象进行同步,而不是使用具有装箱类型的对象。
示例¶
在以下示例中,目的是让 ThreadA
和 ThreadB
同时运行。不幸的是,ThreadA.lock
和 ThreadB.lock
都引用了同一个对象(即 String
"lock"
的内部值),因此它们运行方法中的同步块无法并发执行。
class BadSynchronize{
class ThreadA extends Thread{
private String value = "lock"
public void run(){
synchronized(value){
//...
}
}
}
class ThreadB extends Thread{
private String value = "lock"
public void run(){
synchronized(value){
//...
}
}
}
public void run(){
new ThreadA().start();
new ThreadB().start();
}
}
在以下示例中,展示了上面推荐的方法。为每个线程创建了一个单独的锁对象,允许它们并发执行。
class GoodSynchronize{
class ThreadA extends Thread{
private Object lock = new Object();
public void run(){
synchronized(lock){
//...
}
}
}
class ThreadB extends Thread{
private Object lock = new Object();
public void run(){
synchronized(lock){
//...
}
}
}
public void run(){
new ThreadA().start();
new ThreadB().start();
}
}
参考资料¶
SEI CERT Oracle Java 编码标准:LCK01-J. 不要在可能被重用的对象上进行同步,
常见弱点枚举:CWE-662.