2

我想测量 2 个线程计数到 1000 需要多长时间。如何对以下代码进行基准测试?

public class Main extends Thread {
    public static int number = 0;

    public static void main(String[] args) {

        Thread t1 = new Main();
        Thread t2 = new Main();

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        for (int i = 0; i <= 1000; i++) {
            increment();
            System.out.println(this.getName() + " " + getNumber());
        }
    }

    public synchronized void increment() {
        number++;
    }

    public synchronized int getNumber() {
        return number;
    }
}

为什么即使我使用synchronized关键字我仍然得到以下结果(提取)?

Thread-0 9
Thread-0 11
Thread-0 12
Thread-0 13
Thread-1 10
4

4 回答 4

1

输出不同步。场景是:

  1. Thread-0 单独运行 9 次迭代。
  2. Thread-1 调用 increment 和 getNumber,返回10.
  3. Thread-0 再运行三个迭代。
  4. Thread-1 调用 println 与10.
于 2013-01-13T20:45:46.323 回答
1

你没有同步这个:

    for (int i = 0; i <= 1000; i++) {
        increment();
        System.out.println(this.getName() + " " + getNumber());
    }

所以,一个线程可以执行increment(),等待下一个线程,然后继续getValue()(从而得到你的结果)。鉴于添加值的速度有多快,更改线程会为多次迭代提供其他时间。

做 public static final String LOCK = "lock";

synchronized(LOCK) {
   for (int i = 0; i <= 1000; i++) {
        increment();
        System.out.println(this.getName() + " " + getNumber());
    }
}

您不需要synchronize方法(正如我在评论中解释的那样)。

于 2013-01-13T20:47:00.613 回答
1

为什么即使我使用了 synchronized 关键字,我仍然得到以下结果(提取)?

您同步对number变量的访问,但是增量和获取是分开同步的,这也不会使您的println()原子化。这个顺序是完全可能的:

0 -> inc
1 -> inc
0 -> getnumber
1 -> getnumber
1 -> print
0 -> print

首先,如果要解决“增量和获取”问题,可以使用AtomicInteger

private static final AtomicInteger count = new AtomicInteger(0);

// ...

@Override
public void run()
{
    final String me = getName();

    for (int i = 0; i < 1000; i++)
        System.out.println(me + ": " + count.incrementAndGet());
}

但是,即使这样也不能保证打印顺序。使用上面的代码,这种情况仍然是可能的:

0 -> inc
0 -> getnumber
1 -> inc
1 -> getnumber
1 -> print
0 -> print

要解决此问题,您需要使用例如 a ReentrantLock

private static final Lock lock = new ReentrantLock();
private static int count;

// ...

@Override
public void run()
{
    final String me = getName;

    for (int i = 0; i < 1000; i++) {
        // ALWAYS lock() in front of a try block and unlock() in finally
        lock.lock();
        try {
            count++;
            System.out.println(me + ": " + count);
        finally {
            lock.unlock();
        }
    }
}
于 2013-01-13T20:48:05.653 回答
1

你没有同步。synchronized关键字等效于synchonize (this) {}但您正在增加一个static不包含在您的对象中的数字。您实际上有 2 个对象/线程,并且它们都与自己同步,而不是彼此同步。

要么让你成为财产volatile并且根本不同步,要么使用这样的锁定对象:

public static int number = 0;
public static final Object lock = new Object();

public void increment() {
    synchronized (lock) {
        number++;
    }
}

public int getNumber() {
    synchronized (lock) {
        return number;
    }
}
于 2013-01-13T20:49:02.930 回答