synchronized关键字在java中无法正常工作

时间:2016-07-08 07:03:38

标签: java concurrency synchronization synchronized

修改: 我已经在堆栈上找到了答案: https://stackoverflow.com/a/16280842/3319557

我遇到同步问题。我有以下两种方法:

public synchronized void incrementCounter1() {
    counter++;
}

public void incrementCounter2() {
    synchronized (counter) {
        counter++;
    }
}

我在许多线程中测试每个(单独)。第一种方法表现如预期,但第二种方法(incrementCounter2)错误。有人可以解释为什么会这样吗?

我认为这个方法设计得很好,因为我在Java Concurrency in Practice中发现了类似的东西。从这本书中摘过:

@ThreadSafe
public class ListHelper<E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());
    ...
    public boolean putIfAbsent(E x) {
        synchronized (list) {
            boolean absent = !list.contains(x);
            if (absent)
                list.add(x);
            return absent;
        }
    }
}

我使用我正在修改的对象的监视器,就像在书中一样。

完整代码:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SynchronizationTest {
    public static final int N_THREADS = 500;
    public static final int N_Loops = 5000;
    private Integer counter = 0;
    Lock l = new ReentrantLock();

    public void incrementCounter0() {
        counter++;
    }

    public synchronized void incrementCounter1() {
        counter++;
    }

    public void incrementCounter2() {
        synchronized (counter) {
            counter++;
        }
    }

    public void incrementCounter3() {
        try {
            l.lock();
            counter++;
        } finally {
            l.unlock();
        }
    }

    private interface IncrementStrategy {
        void use(SynchronizationTest t);
    }

    private static class IncrementingRunnable implements Runnable {
        SynchronizationTest synchronizationTest;
        IncrementStrategy methodToUse;

        public IncrementingRunnable(SynchronizationTest synchronizationTest, IncrementStrategy methodToUse) {
            this.synchronizationTest = synchronizationTest;
            this.methodToUse = methodToUse;
        }

        @Override
        public void run() {
            for (int i = 0; i < N_Loops; i++) {
                methodToUse.use(synchronizationTest);
            }
        }

    }

    public void test(IncrementStrategy methodToUse, String methodName) {
        counter = 0;
        Thread[] threads = new Thread[N_THREADS];
        for (int i = 0; i < N_THREADS; i++) {
            threads[i] = new Thread(new IncrementingRunnable(this, methodToUse));
            threads[i].start();
        }
        for (int i = 0; i < N_THREADS; i++) {
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(methodName + " diff than expected " + (counter - N_THREADS * N_Loops));
    }

    public void test() {
        test(t -> t.incrementCounter0(), "incrementCounter0 (expected to be wrong)");
        test(t -> t.incrementCounter1(), "incrementCounter1");
        test(t -> t.incrementCounter2(), "incrementCounter2");
        test(t -> t.incrementCounter3(), "incrementCounter3");
    }

    public static void main(String[] args) {
        new SynchronizationTest().test();
    }

}

我知道,应该使用ExecutorService,使用AtomicLong可以解决整个问题,但这不是这个问题的重点。

代码输出为:

incrementCounter0 (expected to be wrong) diff than expected -1831489
incrementCounter1 diff than expected 0
incrementCounter2 diff than expected -599314
incrementCounter3 diff than expected 0

PS。 如果我将字段添加到SynchronizationTest

Object counterLock = new Object();

并改变 incrementCounter2 to:

public void incrementCounter2() {
    synchronized (counterLock) {
        counter++;
    }
}

然后incremetCounter2按预期工作。

3 个答案:

答案 0 :(得分:4)

您正在同步不同的对象

incrementCounter1this上同步,而incrementCounter2在计数器Integer对象本身上同步。

答案 1 :(得分:2)

您正在尝试使用两个锁定监视器(假设counterObject,可能是Integer?)

public class Foo {
    // Uses instance of Foo ("this")
    public synchronized void incrementCounter1() {
        counter++;
    }

    public void incrementCounter2() {
        // uses counter object as lock monitor
        synchronized (counter) {
            counter++; 
        }
    }

}

我不确定您使用counter++尝试实现的目标counter类型为Integer

解决问题的几个选项:

  • 使用相同的锁定监视器
  • 您可能需要查看AtomicInteger
  • 使用锁定API(例如ReentrantReadWriteLock

答案 2 :(得分:0)

丑恶。

synchronized void method(...

this对象上进行同步。

  synchronized(object) {
      ...

object上进行同步。

现在:

synchronized (counter) {
    ++counter;

还必须在Object上同步,但counter是基本类型,int。 会发生什么,该计数器是以整数形式装箱的。

当counter为0 .. 127时,检索到的Integer对象每次都不同,但是共享。对于1234,创建一个新的唯一Integer对象,并且synchronized不会产生任何影响。 (整数是不可变的。)

我认为这几乎是一个语言错误,这是FindBugs找到的东西。