为什么这个线程安全?

时间:2013-04-13 18:34:00

标签: java multithreading

因为它总是打印出'3'。不需要同步?我正在测试这个简单的事情,因为我在一个真正的多线程问题上遇到了麻烦,这不能很好地说明问题,因为它很大。这是一个展示情况的简化版本。

class Test {

public static int count = 0;

class CountThread extends Thread {

    public void run()
    {
        count++;
    }
}  

public void add(){
    CountThread a = new CountThread();
    CountThread b = new CountThread();
    CountThread c = new CountThread();

    a.start();
    b.start();
    c.start();

    try {
        a.join();
        b.join();
        c.join();
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }

}
public static void main(String[] args) {

    Test test = new Test();
    System.out.println("START = " + Test.count);

    test.add();

    System.out.println("END: Account balance = " + Test.count);
}

6 个答案:

答案 0 :(得分:4)

  

因为它总是打印出'3'。不需要同步?

这不是线程安全的,你只是运气好。如果运行1000次,或者在不同的体系结构上运行,您将看到不同的输出 - 即不是3。

我建议使用AtomicInteger而不是静态字段++,而不是synchronized

public static AtomicInteger count = new AtomicInteger();
...
public void run() {
    count.incrementAndGet();
}
...

答案 1 :(得分:2)

对我而言,像count++一样快到足以完成,直到你为另一个类调用'run'。所以基本上它是顺序运行的 但是,如果这是一个真实的例子,并且两个不同的线程并行使用CountThread,那么是的,你会遇到同步问题。

要验证这一点,您可以尝试在计数++之前和之后打印一些测试输出,然后在b.start()完成之前,您将看到count++是否正在调用a.start()c.start()也是如此。

请考虑使用AtomicInteger,这比尽可能同步更好 -

  

<强> incrementAndGet
  public final int incrementAndGet()
  原子上将当前值增加1。

答案 2 :(得分:1)

此代码不是线程安全的:

public static int count = 0;

class CountThread extends Thread {

    public void run()
    {
        count++;
    }
}

您可以在一个系统上运行此代码一百万次,并且每次都可以通过。这并不意味着它是线程安全的。

考虑将count中的值复制到多个处理器缓存的系统。在将某个缓存强制复制回主RAM之前,它们都可以独立更新。考虑++不是原子操作。 count的读写顺序可能会导致数据丢失。

实现此代码的正确方法(使用Java 5及更高版本):

public static java.util.concurrent.atomic.AtomicInteger count =
                                 new java.util.concurrent.atomic.AtomicInteger();

class CountThread extends Thread {

    public void run()
    {
        count.incrementAndGet();
    }
}

答案 3 :(得分:0)

仅仅因为输出是正确的,它不是线程安全的。创建一个线程会导致操作系统方面的大量开销,之后只需要在单个时间段内完成该单行代码。它绝不是线程安全的,只是没有足够的潜在冲突来实际触发它。

答案 4 :(得分:0)

它不是线程安全的。

恰好可以通过缩短来获得显示问题的可衡量机会。考虑在run中计数更高的数字(1000000?),以增加多个线程上2次操作重叠的可能性。

还要确保您的机器不是单核CPU ...

答案 5 :(得分:0)

要使类线程安全,请使count volatile强制线程之间的内存屏障,或者使用 AtomicInteger ,或者像这样重写(我的偏好):

class CountThread extends Thread {

    private static final Object lock = new Object();

    public void run()
    {
        synchronized(lock) {
            count++;
        }
    }
}