同步对Immutable Integer对象的访问

时间:2013-08-03 07:32:54

标签: java multithreading synchronization

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

3 个答案:

答案 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变量。