代码段 - 1
class RequestObject implements Runnable
{
private static Integer nRequests = 0;
@Override
public void run()
{
synchronized (nRequests)
{
nRequests++;
}
}
}
代码段 - 2
class RequestObject implements Runnable
{
private static Integer nRequests = 0;
private static Object lock = new Object();
@Override
public void run()
{
synchronized (lock)
{
nRequests++;
}
}
}
虽然第二个代码片段工作正常而没有引起任何竞争条件,但第一个代码片段不能成功同步对同一个类的不同实例(RequestObject)之间的静态数据成员的访问。有人可以对此有所了解吗?我想了解为什么第一种方法不起作用。
我的原始实现是第一个。后来我在https://stackoverflow.com/a/2120409/134387看到了。
答案 0 :(得分:6)
您不断创建新的Integer对象,然后对其进行同步,这至少会让人难以理解。所以你可以得到以下场景:
线程A获取当前值nRequests
(假设为0)
线程B队列为相同的值(0)
线程A增加nRequests
(值为1)
线程C获取新值并在其上进行同步,增加它并释放值。
线程A放开监视器0
线程B在0上同步并将其增加到1,覆盖C的变化
使用第二种方法,您拥有一个每个人都必须同步的对象。这正是你想要的。
答案 1 :(得分:4)
Integer
的实例是不可变的,nRequests++
因此会创建一个新的Integer
对象来保存结果,并将其存储在nRequests
中。 synchronized
语句在对象上同步。因此,线程将在不同的对象上同步。是的,同一个对象上同步块中可能只有一个线程,但不同的线程可能同时在不同对象的同步块中...
同步访问静态的最简单方法是将其置于静态同步方法中:
static synchronized void increment() {
nRequests++;
}
这相当于以下同步块:
synchronized (RequestObject.class) {
nRequests++;
}
其中RequestObject是包含静态字段的类。
答案 2 :(得分:2)
问题是Integer
类在java中是不可变的。因此,每个线程在不同的对象上同步,因为nRequests++
在每次调用时都会创建一个新对象。
在第二种情况下,lock
对象对于每个实例都是相同的,并且成功地将线程的访问序列化为nRequests
变量。