1 常见问题
synchronized方法是一种常用的线程同步方法,在我们代码中多处使用。但是使用不当的情况下容易引入安全问题,因此《Java编码安全Checklist_V2.0》中专门设置了一个检查项:
与非信任代码交互的类的同步操作要使用私有的、无法被非信任代码访问与修改的锁
举例如下:
1)SomeObject类中定义了一个同步方法changeValue()。
public class SomeObject
{
// Locks on the object's monitor
public synchronized void changeValue()
{
// . . .
}
}
2)非信任代码通过某种方法获取到了一个SomeObject对象,对其加锁,并且长期持有该锁。
// Untrusted code
SomeObject theObject = getTheObject();
synchronized (someObject)
{
while (true)
{
// Indefinitely delay someObject
Thread.sleep(Integer.MAX_VALUE);
}
}
3)其他代码在调用该对象的changeValue方法时无法获取锁,将长期被阻塞。
// Trusted code
SomeObject theObject = getTheObject();
theObject.changeValue(); //调用对象的同步方法时,需要先获取对象锁。
2 修改方法
2.1 最小化暴露
一般来说,同一个package中的代码可以认为是可信任的。
如果一个类只是供同一个package中的代码访问,那么该类声明为default就够了(即不加任何访问控制符),不需要声明为public,这样就天然解决了这个问题。
说句题外话,我们在编码时经常习惯性的把所有的类都声明为public,实际上大多数类都不需要这么大的访问权限,我们应该根据实际情况来选择合适的访问控制符,做到最小化暴露。
附:访问控制级别表:
| private | default | protected | public |
同一个类中 | Yes | Yes | Yes | Yes |
同一个包中 |
| Yes | Yes | Yes |
子类中 |
|
| Yes | Yes |
全局范围内 |
|
|
| Yes |
2.2 使用私有锁对象
如果使用同步方法的类确实需要声明为public,可以使用私有锁对象习语(private lock object idiom)来进行同步。该习语使用类中声明的私有不变(private final)java.lang.Object实例所关联的隐式锁来代替类对象本身的隐式锁。这个习语要求在类方法中使用同步块而不是使用同步方法。因为敌对类不能访问这个私有不变锁对象,因此它没法与该类中的方法进行锁竞争。当一个类可能与非信任代码交互时,使用私有不变锁对象对这个类中的代码做同步。
public class SomeObject
{
private final Object lock = new Object(); // private final lock object
public void changeValue()
{
synchronized (lock)
{
// Locks on the private Object
// ...
}
}
}