在某个电商网站上,我们有一个商品管理系统,需要管理大量的商品信息。为了方便对商品进行增加、删除和查询操作,我们使用了 ArrayList 来存储商品对象。然而,由于多个管理员可以同时修改商品列表,可能会导致 ArrayList 在非线程安全的情况下出现数据不一致的问题,并且可能引发其他潜在错误。
让我们先来复现一个非线程安全的场景。考虑以下代码:
import java.util.ArrayList;
import java.util.List;
public class ProductManagementSystem {
private List<Product> productList;
public ProductManagementSystem() {
// 初始化商品列表
productList = new ArrayList<>();
}
// 添加商品到列表中
public void addProduct(Product product) {
productList.add(product);
}
// 从列表中删除商品
public void removeProduct(Product product) {
productList.remove(product);
}
// 在列表中查找商品
public boolean findProduct(Product product) {
return productList.contains(product);
}
}
以上是一个简单的商品管理系统,其中 Product
类表示商品对象。
我们模拟两个管理员同时对商品列表进行操作,一个管理员添加商品,另一个管理员在此同时尝试删除商品:
public static void main(String[] args) throws InterruptedException {
// 创建商品管理系统实例
ProductManagementSystem system = new ProductManagementSystem();
// 创建两个商品
Product product1 = new Product("001", "Apple");
Product product2 = new Product("002", "Banana");
// 创建线程1用于不断地向商品列表中添加商品1
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
system.addProduct(product1);
}
});
// 创建线程2用于等待商品1添加到列表,然后尝试删除商品1
Thread thread2 = new Thread(() -> {
while (!system.findProduct(product1)) {
// 等待商品1被添加进列表
}
system.removeProduct(product1);
});
// 启动线程1和线程2
thread1.start();
thread2.start();
// 等待线程1和线程2完成
thread1.join();
thread2.join();
// 打印剩余商品数量
System.out.println("Remaining products count: " + system.getProductListSize());
}
在上述示例中,第一个管理员线程 thread1
负责不断添加商品1到系统中(执行1000次),而第二个管理员线程 thread2
在等待商品1被添加进列表后尝试删除商品1。由于 ArrayList 非线程安全,可能会导致数据不一致的问题。
为了解决 ArrayList 的非线程安全问题,我们可以使用 Collections.synchronizedList()
方法来创建一个线程安全的包装列表。使用同步列表可确保只有一个线程可以访问列表,并保护对列表的并发操作。
以下是修改后的代码,使用线程安全的 ArrayList 替代原始的 ArrayList:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ProductManagementSystem {
private List<Product> productList;
public ProductManagementSystem() {
// 创建一个线程安全的包装列表
productList = Collections.synchronizedList(new ArrayList<>());
}
// 添加商品方法
public void addProduct(Product product) {
productList.add(product);
}
// 删除商品方法
public void removeProduct(Product product) {
productList.remove(product);
}
// 查找商品方法
public boolean findProduct(Product product) {
return productList.contains(product);
}
}
在上述代码中,通过调用 Collections.synchronizedList()
方法并传入原始的 ArrayList 对象来创建一个线程安全的包装列表。
重新运行之前的测试代码,我们可以观察到在使用线程安全的 ArrayList 后,不再出现数据不一致的情况。
然而,需要注意的是虽然同步列表保证了线程安全性,但由于只允许一个线程访问列表,可能会影响并发性能。对于高并发场景,可能需要考虑更高效的并发集合,如 java.util.concurrent.CopyOnWriteArrayList
。
综上所述,通过使用线程安全的 ArrayList 或其他并发集合类,我们可以解决 ArrayList 在非线程安全情况下出现的数据不一致问题,确保多个管理员同时操作商品列表时的数据一致性和可靠性。然而,在选择并发集合时,需要权衡性能和并发要求,以便选择最适合自己业务场景的集合类型。