Java 线程安全计数器

Posted

技术标签:

【中文标题】Java 线程安全计数器【英文标题】:Java Thread Safe Counter 【发布时间】:2013-04-06 14:30:28 【问题描述】:

我正在学习编写线程安全的程序以及如何评估非线程安全的代码。

一个类被认为是线程安全的,如果它在被多个线程执行时正确运行。

我的 Counter.java 不是线程安全的,但是所有 3 个线程的输出都按预期从 0 到 9 打印。

谁能解释为什么?以及线程安全是如何工作的?

public class Counter 

    private int count = 0;

    public void increment() 
        count++;
    

    public void decrement() 
        count--;
    

    public void print() 
        System.out.println(count);
    



public class CountThread extends Thread 
    private Counter counter = new Counter();

    public CountThread(String name) 
        super(name);
    

    public void run() 
        for (int i=0; i<10; i++) 
            System.out.print("Thread " + getName() + " ");
            counter.print();
            counter.increment();
        
    



public class CounterMain 

    public static void main(String[] args) 
        CountThread threadOne = new CountThread("1");
        CountThread threadTwo = new CountThread("2");
        CountThread threadThree = new CountThread("3");

        threadOne.start();
        threadTwo.start();
        threadThree.start();
    


【问题讨论】:

您在每个线程中使用不同的 Counter 对象,因此在这种情况下您看不到任何线程安全问题。 【参考方案1】:

你的Counter不是通过3个线程共享的,而是每个线程都有一个唯一的Counter

【讨论】:

或许您应该解释一下为什么会出现这种情况,以获得更彻底的答案。 (显而易见,因此省略)的解释是 OP 在每个线程中创建了一个唯一的 Counter 实例。 我当然遵循推理。我只是不知道作者是否理解他对 Counter 实例的放置如何使它成为线程特定的。 (+1,我想。)【参考方案2】:

您的代码在您的特定测试中运行良好。这并不意味着它总是能正常工作。尝试多次迭代,您可能会开始看到异常。

您的测试有点像,如果您测试一座桥可以支持 20 辆卡车,只需一辆汽车骑在桥上。它没有表现出任何东西。并且几乎不可能使用测试证明代码是线程安全的。只有仔细阅读和理解所有潜在问题才能保证这一点。一个非线程安全的程序可能会运行多年,然后突然出现错误。

为了让您反击安全,请使用 AtomicInteger。

编辑:

此外,正如@SpringRush 所指出的,您不会在线程之间共享单个计数器。每个线程都有自己的计数器。所以你的代码实际上是线程安全的,但我认为它并没有达到你想要的效果。

【讨论】:

是的,我在发布我的答案的第一个版本后才看到。我修好了。 也就是说,每个Thread中声明的所有变量都不会受到影响? 线程安全问题仅在多个线程之间共享某些状态时发生。如果某些状态仅由一个线程可见和触及,则此状态不会有任何线程安全问题。 好的。这是我写的无效示例。【参考方案3】:

所以这实际上是线程安全的。对于每个 CountThread,都有一个计数器。要使其不是线程安全的,请将计数器变量更改为:

private static Counter counter = new Counter(); 

那么它就不是线程安全的了,因为不同的线程可以同时修改计数器状态。

【讨论】:

【参考方案4】:

试试这个:

public class ThreadSafeCounter 

    AtomicInteger value = new AtomicInteger(0);

public ThreadSafeCounter(AtomicInteger value) 
    this.value = value;


public void increment() 
    value.incrementAndGet();


public void decrement() 
    value.decrementAndGet();


public AtomicInteger getValue() 
    return value;

【讨论】:

以上是关于Java 线程安全计数器的主要内容,如果未能解决你的问题,请参考以下文章

Android中的线程线程安全 & 线程同步

Android中的线程线程安全 & 线程同步

jvm内存模型中-栈,方法区,程序计数器是线程安全的

Java专题十三:线程安全与同步

java

如何使用多线程优化多查询复杂业务接口,并保证线程安全