java - 为什么竞争条件只能用 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]
解决方案
当我更改同步并使用 ReentrantLock 锁定 pick()、increment() 方法时,不确定为什么其他线程不会更改索引。结果看起来不错。有人可以解释原因吗?非常感谢。
关于synchronized
方法
private synchronized void pickItem(int index, List<String> list) {
res.add(list.get(index));
increment();
}
正在使用类返回的对象实例的隐式锁进行锁定,并且由于为每个类的新对象实例创建:this
Printer
Thread
Printer
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
单独(没有synchronized
on 方法pickItem
)解决了前面提到的竞争条件。
推荐阅读
- spring-boot - ActiveMQ Artemis - Spring Boot 节流
- xml - XML/XSL if else 子字符串
- javascript - 单独更改点大小散点图——ChartJS
- docker - 如何从主机(Windows 机器)连接到 docker 容器
- log4j - 如何为嵌入式 Jetty 服务器配置 log4j.properties?
- ruby-on-rails - 通过 Rmagick 错误将 PDF/DOC/DOX 转换为 PNG 时:没有这样的文件或目录 @error/blob.c/OpenBlob/2712 - Ruby on Rails
- sql - 如何修改我的模式或查询,使其有效运行?
- swiftui - SwiftUI iOS 14 beta TextField 100% CPU
- mysql - AWS lambda 执行中的数据库错误“无法连接到 MySQL 服务器”
- typescript - 传递任何或记录时,TypeScript 中重载匹配顺序的函数签名