首页 > 解决方案 > 为什么竞争条件只能用 ReentrantLock 解决而不是同步

问题描述

我尝试使用多线程将所有元素添加到我的结果列表中。我不确定为什么结果看起来很奇怪。

public class PrintMessage {

    public static void main(String[] args) {
        PrintMessage p = new PrintMessage();
        List<String> list = List.of("a", "b", "c", "d", "e", "f", "g", "h");

        ExecutorService executor = Executors.newFixedThreadPool(5);

        p.printValue(list, executor);
        try {
            executor.awaitTermination(1000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executor.shutdown();

        System.out.println(res);
    }

    //ReentrantLock lock = new ReentrantLock();
    private volatile int index = 0;
    static List<String> res = new ArrayList<>();

    class Printer implements Runnable {
        List<String> list;

        public Printer(List<String> list) {
            this.list = list;
        }

        @Override
        public void run() {
            //lock.lock();
           
            pickItem(index, list);
            //increment();
            //lock.unlock();
        }
    }

    private void increment() {
        index++;
    }
    private synchronized void pickItem(int index, List<String> list) {
        res.add(list.get(index));
        increment();
    }
    public void printValue(List<String> list, ExecutorService executor) {

        for (int i = 0; i < list.size(); i++) {
            executor.submit(new Printer(list));
        }
    }
}

当我更改同步并使用ReentrantLock锁定 pick()、increment() 方法时,不确定为什么其他线程不会更改索引。结果看起来不错。有人可以解释原因吗?非常感谢。

[a, b, c, e, e, f, a, a]

标签: javamultithreadingperformanceparallel-processingsynchronized

解决方案


当我更改同步并使用 ReentrantLock 锁定 pick()、increment() 方法时,不确定为什么其他线程不会更改索引。结果看起来不错。有人可以解释原因吗?非常感谢。

关于synchronized方法

private synchronized void pickItem(int index, List<String> list) {
    res.add(list.get(index));
    increment();
}

正在使用类返回的对象实例的隐式锁进行锁定,并且由于为每个类的新对象实例创建:thisPrinterThreadPrinter

for (int i = 0; i < list.size(); i++) {
    executor.submit(new Printer(list));
}

每个线程都在调用synchronized类的不同实例Printer。因此,这些线程没有使用相同的锁定lock,因此存在竞争条件

然而,lock变量

ReentrantLock lock = new ReentrantLock();

是 Object 的一个字段变量PrintMessage它在所有线程之间共享。因此,当线程调用lock.lock();并且lock.unlock();它们使用相同的(共享)锁时,不再存在上述的竞争条件

ReentrantLock单独(没有synchronizedon 方法pickItem)解决了前面提到的竞争条件


推荐阅读