0

因为它总是打印出'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);
}
4

6 回答 6

4

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

它不是线程安全的,你只是很幸运。如果你运行这个 1000 次,或者在不同的架构上,你会看到不同的输出——不是 3。

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

public static AtomicInteger count = new AtomicInteger();
...
public void run() {
    count.incrementAndGet();
}
...
于 2013-04-13T18:38:25.353 回答
2

在我看来,count++在您为其他班级调用“运行”之前,它的速度足以完成。所以基本上它是按顺序运行的。
但是,如果这是一个真实的例子,并且两个不同的线程CountThread并行使用,那么是的,你会遇到同步问题。

为了验证这一点,您可以尝试在 count++ 之前和之后打印一些测试输出,然后您将在完成之前查看是否b.start()正在调用。对.count++a.start()c.start()

考虑改用 AtomicInteger,这比尽可能同步要好得多 -

incrementAndGet
public final int incrementAndGet()
以原子方式将当前值加一。

于 2013-04-13T18:37:14.440 回答
1

此代码不是线程安全的:

public static int count = 0;

class CountThread extends Thread {

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

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

考虑一个系统,其中 in 的值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();
    }
}
于 2013-04-13T18:44:04.160 回答
0

仅仅因为输出是正确的,它就不是线程安全的。创建线程会在操作系统方面造成大量开销,之后可以预期单行代码将在单个时间片内完成。无论如何它都不是线程安全的,只是没有足够的潜在冲突来实际触发一个。

于 2013-04-13T18:37:38.173 回答
0

它不是线程安全的。

只是碰巧有机会展示这个问题。考虑计数到更高的数字(1000000?),run以增加多个线程上的 2 个操作重叠的机会。

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

于 2013-04-13T18:37:43.647 回答
0

要使类线程安全,要么计数volatile以强制线程之间的内存围栏,要么使用AtomicInteger,或者像这样重写(我的偏好):

class CountThread extends Thread {

    private static final Object lock = new Object();

    public void run()
    {
        synchronized(lock) {
            count++;
        }
    }
}  
于 2013-05-15T13:33:12.143 回答