为什么竞争条件只能用 ReentrantLock 解决而不是同步

Why race condition is only solved with ReentrantLock and not synchronized

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

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

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

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

Not sure why does the index not change by other threads, when I change synchronized and use ReentrantLock to lock pick(), increment() methods. the result looks good. Can someone explain the reason? Many thanks.

方法上的synchronized

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

正在使用 class Printerthis 返回的对象实例的 隐式 锁进行锁定,因为对于每个 Thread 创建了 Printer class 的新对象实例:

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

每个线程在 class Printer 的不同实例上调用 synchronized。因此,这些线程 未使用相同的 lock 进行锁定,因此存在 竞争条件 .

然而,lock变量

ReentrantLock lock = new ReentrantLock();

是对象PrintMessage的字段变量,所有线程共享。因此,当线程调用 lock.lock();lock.unlock(); 时,它们使用相同的(共享)锁,因此不再存在上述 race-condition.

单独使用 ReentrantLock(方法 pickItem 上没有 synchronized)解决上述 race-condition.