刚开始学习多线程,并停留在并发修改的情况下。
这是我的Java课
package ashish.demo.threading.basic;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.List;
/**
* Created by ashishratan on 2/2/17.
*/
public class ItemTask implements Runnable {
private volatile boolean shutdown;
private List<Item> itemList = new ArrayList<Item>();
private volatile Item item;
private volatile boolean addItemEvent;
private volatile boolean removeItemEvent;
@Override
public void run() {
while (!this.shutdown) {
try {
synchronized (this) {
if (this.item != null) {
this.item.setProductName("Created By:: " + Thread.currentThread().getName());
}
if (this.addItemEvent) {
this.itemList.add(this.item);
this.item=null;
this.addItemEvent = false;
this.statusDisplay();
}
if (this.removeItemEvent) {
this.itemList.add(this.item);
this.removeItemEvent = false;
this.statusDisplay();
}
}
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Shutting down...");
}
public void addItem(Item item) {
this.item = item;
this.addItemEvent = true;
}
public synchronized List<Item> getItemList() {
this.statusDisplay();
return itemList;
}
public void setItemList(List<Item> itemList) {
this.itemList = itemList;
}
public synchronized void shutdownHook() {
this.statusDisplay();
this.shutdown = true;
System.out.println(this.getItemList());
}
private synchronized void statusDisplay() {
System.out.println(Thread.currentThread());
System.out.println("Total Items In Stock are " + this.itemList.size());
}
}
跑步班
package ashish.demo.threading;
import ashish.demo.threading.basic.Item;
import ashish.demo.threading.basic.ItemTask;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
ItemTask itemTask = new ItemTask();
Thread thread =null;
for (int i = 0; i < 500; i++) {
thread=new Thread(itemTask);
thread.setName("ItemTask-Thread-"+(i+1));
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
System.out.println("Please Enter Number (0) to exit");
Scanner scanner = new Scanner(System.in);
int i = scanner.nextInt();
while (i>0){
itemTask.addItem(new Item(1,12.0f,"Product "+i,(byte)12));
System.out.println(itemTask.getItemList()); // Line #26, Exception
System.out.println("Please Enter Number (0) to exit");
i = scanner.nextInt();
}
System.out.println("EXIT");
itemTask.shutdownHook();
}
}
package ashish.demo.threading.basic;
import java.io.Serializable;
/**
* Created by ashishratan on 2/2/17.
*/
public class Item implements Serializable {
private Integer orderId;
private Float price;
private String productName;
private byte category;
public Item(Integer orderId, Float price, String productName, byte category) {
this.orderId = orderId;
this.price = price;
this.productName = productName;
this.category = category;
}
public Item() {
}
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public byte getCategory() {
return category;
}
public void setCategory(byte category) {
this.category = category;
}
@Override
public String toString() {
return "Item{" +
"orderId=" + orderId +
", price=" + price +
", productName='" + productName + '\'' +
", category=" + category +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Item)) return false;
Item item = (Item) o;
if (getCategory() != item.getCategory()) return false;
if (getOrderId() != null ? !getOrderId().equals(item.getOrderId()) : item.getOrderId() != null) return false;
if (getPrice() != null ? !getPrice().equals(item.getPrice()) : item.getPrice() != null) return false;
return getProductName() != null ? getProductName().equals(item.getProductName()) : item.getProductName() == null;
}
@Override
public int hashCode() {
int result = getOrderId() != null ? getOrderId().hashCode() : 0;
result = 31 * result + (getPrice() != null ? getPrice().hashCode() : 0);
result = 31 * result + (getProductName() != null ? getProductName().hashCode() : 0);
result = 31 * result + (int) getCategory();
return result;
}
}
异常跟踪
Please Enter Number (0) to exit
3
Thread[main,5,main]
Total Items In Stock are 0
[]
Please Enter Number (0) to exit
Thread[ItemTask-Thread-455,10,main]
Total Items In Stock are 1
6
Thread[main,5,main]
Total Items In Stock are 1
Thread[ItemTask-Thread-464,10,main]
Total Items In Stock are 2
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at java.util.AbstractCollection.toString(AbstractCollection.java:461)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at ashish.demo.threading.Main.main(Main.java:26)
12
最佳答案
Java Concurrency in Practice中的建议是:“当心隐式迭代”。您在行上有隐式迭代:
System.out.println(itemTask.getItemList());
因为此列表需要迭代才能将其转换为字符串。
itemTask.getItemList()
已同步的事实无关紧要-仅在对itemTask.getItemList()
的调用期间才保留监视器:返回该结果后,该监视器将不再保留,这意味着该监视器不再保留当您将该结果传递给System.out.println
时。为了确保您在打印项目列表时对项目列表具有独占访问权,请在
itemTask
上显式同步:synchronized (itemTask) {
System.out.println(itemTask.getItemList());
}
这将在
System.out.println
调用期间正确保持监视器。 itemTask
中的修改不能同时进行,因为这些修改是在synchronized (this)
块中进行的,其中“ this
”是您要在外部进行同步的itemTask
。替代方法是从
getItemList
方法返回列表的防御性副本:public synchronized List<Item> getItemList() {
this.statusDisplay();
return new ArrayList<>(itemList);
}
这将消除对
getItemList
的调用进行外部同步的需要,这是一个更安全的设计,因为您不依赖于类的客户端来“做正确的事”。