此代码中是否存在竞争条件?

时间:2017-11-21 12:59:02

标签: java multithreading thread-safety synchronized

我需要在5个线程中增加一个计数器,直到它达到500.像这样的5个线程正在工作。但我需要知道它是否有竞争条件。

此代码的另一个问题是它以错误的顺序给我打印。

class HiloContador extends Thread {

    static int count = 0;

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            synchronized (new Integer(count)) {
                ++count;
                System.out.println(count);
            }
        }
    }
}

输出

  

4       6       8       9       10       11       12       五       14       15       3       17       18

一些想法?

2 个答案:

答案 0 :(得分:4)

是的,存在竞争条件,因为您实际上在不同对象上同步代码。 synchronized块代表一个关键部分。要进入关键部分,您必须获得全局锁定。您可以将对象用于锁定或整数。 这就是我要实现的方式:

class HiloContador extends Thread {

    static int count = 0;
    static Object lock = new Object();

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            synchronized (lock) {
                ++count;
                System.out.println(count);
            }
        }
    }
}

现在println将处于预期的顺序。

答案 1 :(得分:2)

分析

  

此代码中是否存在竞争条件?

是的,存在竞争条件:此行表示不正确的同步:

synchronized (new Integer(count)) {

这就是为什么。
我们来参考文档:

  

每个对象都有一个与之关联的内在锁。按照惯例,需要对对象字段进行独占和一致访问的线程必须在访问对象之前获取对象的内部锁,然后在完成它们时释放内部锁。据说一个线程在获得锁定和释放锁定之间拥有内在锁定。只要一个线程拥有一个内部锁,没有其他线程可以获得相同的锁。另一个线程在尝试获取锁时会阻塞。

     

- Intrinsic Locks and Synchronization (The Java™ Tutorials > Essential Classes > Concurrency)

在目前的情况下,第一句话很关键。当前实现使用新对象的内部锁来在每次循环迭代时进行同步。这是不正确的。

解决方案概述

实现增量操作原子性的替代方法:

  1. 使用内在锁(synchronized关键字)。请参阅Intrinsic Locks and Synchronization (The Java™ Tutorials > Essential Classes > Concurrency)文章。
  2. 使用原子变量(java.util.concurrent.atomic包:AtomicBooleanAtomicIntegerAtomicLong等类。请参阅Atomic Variables (The Java™ Tutorials > Essential Classes > Concurrency)文章。
  3. 使用锁定对象(java.util.concurrent.locks包的类)。请参阅Lock Objects (The Java™ Tutorials > Essential Classes > Concurrency)文章。
  4. 最小解决方案

    让我们使用原子变量(解决方案#3),即AtomicInteger类:它具有内置的所需功能。

    另外,我们不要扩展Thread类:让我们提取相应的Runnable接口实现。

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class Program {
        public static void main(String[] args) throws InterruptedException {
            final CountingRunnable countingRunnable = new CountingRunnable();
            final List<Thread> threads = new ArrayList<>();
            for (int i = 0; i < 5; ++i) {
                final Thread thread = new Thread(countingRunnable);
                threads.add(thread);
                thread.start();
            }
    
            for (final Thread thread : threads) {
                thread.join();
            }
        }
    
        private static final class CountingRunnable implements Runnable {
            private final AtomicInteger count = new AtomicInteger(0);
    
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(count.incrementAndGet());
                }
            }
        }
    }
    

    其他参考资料

    1. 整个Lesson: Concurrency (The Java™ Tutorials > Essential Classes)
    2. Memory Consistency Errors (The Java™ Tutorials > Essential Classes > Concurrency)。要了解发生在之前的关系。