2013-04-13 81 views
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); 
} 
+1

围绕'main'代码循环,你会发现情况并非如此。 – Chan

回答

2

我看来,像count++足够快结束,直到你调用“跑”到其它类。所以基本上它是连续运行的
但是,如果这是一个真实的例子,并且两个不同的线程并行使用CountThread,那么是的,你会遇到同步问题。

要验证您可以尝试在count ++之前和之后打印一些测试输出,那么在a.start()完成之前,您会看到b.start()是否调用count++。相同的c.start()

考虑使用的AtomicInteger代替,这是方式比同步更好可能时 -

incrementAndGet
public final int incrementAndGet()
以原子递增一个的当前值。

+0

由于IO或PrintStream同步,打印将更改线程运行顺序。证明你在说什么很难。尽管它是最正确的。 – Gray

+0

如果我使用synchronized void run(),还需要使用AtomaticInteger()吗? – user697911

+0

不,但您最好使用AtomicInteger,因为它会产生更快的结果。 – danieln

0

仅仅因为输出是正确的,它不是线程安全的。创建一个线程会在操作系统上造成很大的开销,之后只需要在单个时间片内完成这一行代码。这不是线程安全的,只是没有足够的潜在冲突来触发一个。

0

它不是线程安全的。

它恰好是短路的方式,以显示该问题的可测量的机会。考虑在run中计数到更高的数字(1000000?)以增加在多个线程上重叠进行2次操作的机会。

另外,还要确保你的机器不是单核心CPU ...

4

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

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

我建议使用AtomicInteger而不是一个静态字段++而不是​​。

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

即使我使用AtomicInteger,我仍然必须使用静态的,对吗? – user697911

+1

您可以将计数器作为“CountThread”的参数传递给线程。对于所有线程来说,'static'只是一个简单的方法来引用同一个counter @ user697911。 – Gray

+1

-1会关心解释答案的错误吗? – Gray

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(); 
    } 
} 
0

使课堂线程要么使数volatile强行线程之间的内存栅栏,或使用的AtomicInteger,或改写像这样(我的偏好):

class CountThread extends Thread { 

    private static final Object lock = new Object(); 

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