5

代码片段 - 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看到。

4

3 回答 3

6

您不断地创建新的 Integer 对象,然后在其上进行同步,这至少使考虑它变得非常混乱。所以你可以得到以下场景:

线程 A 获取nRequests(假设为 0)的当前值

线程 B 排队等待相同的值 (0)

线程 A 增加nRequests(到值 1)

线程 C 获取新值并对其进行同步,增加它并释放该值。

线程 A 在 0 上放开监视器

线程 B 在 0 上同步并增加到 1,覆盖 C 的变化

使用第二种方法,您有一个每个人都必须同步的对象。这正是你想要的。

于 2013-08-03T07:42:58.760 回答
5

的实例Integer是不可变的,nRequests++因此创建一个新Integer对象来保存结果,并将其存储在nRequests. 该synchronized语句在对象上同步。因此,线程将在不同的对象上同步。是的,同一对象的同步块中可能只有一个线程,但不同的线程可能同时在不同对象的同步块中……

同步访问静态状态的最简单方法是将其放入静态同步方法中:

static synchronized void increment() {
    nRequests++;
}

这等效于以下同步块:

synchronized (RequestObject.class) {
    nRequests++;
}

其中 RequestObject 是包含静态字段的类。

于 2013-08-03T07:40:10.243 回答
2

问题是Integer类在java中是不可变的。所以每个线程在不同的对象上同步,因为nRequests++每次调用都会创建一个新对象。

在第二种情况下lock,每个实例的对象都是相同的,并成功地序列化线程对nRequests变量的访问。

于 2013-08-03T07:42:15.817 回答