多线程操作导致list报NoSuchElementException

为什么我用两个线程操作一个list,一个不断的删除,一个不断的添加。
会导致
Exception in thread "Thread-0" java.util.NoSuchElementException
at java.util.LinkedList.remove(LinkedList.java:788)
at java.util.LinkedList.removeFirst(LinkedList.java:134)
at SynlistTest.remove(SynlistTest.java:10)
at SynlistTest$1.run(SynlistTest.java:27)
at java.lang.Thread.run(Thread.java:619)
[code="java"]
public class SynlistTest {

LinkedList<String> items = new LinkedList<String>();

public String remove() {

    if (!items.isEmpty()) {
         return items.removeFirst();
     }
    return null;
}

public void add(String item) {

    items.add(item);
}


public void test() {

    Runnable thread1 = new Runnable() {

        public void run() {
            while (true) {
                remove();
            }
        }
    };

    Runnable thread2 = new Runnable() {

        public void run() {
            while (true) {
                add("sanyun");
            }
        }
    };

    new Thread(thread1).start();
    new Thread(thread2).start();
}

public static void main(String[] args) {

    new SynlistTest().test();
}

}
[/code]

我给你的类稍微做了修改
public class SynlistTest {

List items = Collections.synchronizedList(new LinkedList());
// LinkedList items = new LinkedList();

 public String remove() {  

     if (!items.isEmpty()) {  
         //使用索引代替removefirst();
          return   items.remove(0); 
      }  
     return null;  
 } 

主要的问题是LinkedList是非线程安全的,如果多个线程同时访问列表,而其中至少一个线程从结构上修改了该列表,则它必须 保持外部同步。我帮你修改了是基于LIST的,如果也可以使用下面的方式基于linklist的线程安全的访问。

public class SynlistTest {

 LinkedList<String> items = new LinkedList<String>(); 

 String flag="110";
 public  String remove() {  
     synchronized(flag){
     if (!items.isEmpty()) {  
         System.out.println("remove.......");
          return items.removeFirst();  
      }  
     return null;  
 }  }

 public void add(String item) {  
     synchronized(flag){
         System.out.println("add.......");
     items.add(item);  
     }
 } 

两个线程共享这个判断标志flag,当第一个线程执行add操作的时候将flag的标志位修改此时如果第二个线程来访问标志位,发现不对正在被使用 就不会执行操作。所以就不会出现同时add和move的情况,确保了同步。

唔,没看出来为什么……明明在修改了链表内容之后它的size才改变的,没看出来是在什么位置上的切换会出问题 T T
不过这个应用场景是很典型的生产者-消费者问题,在Java 5或以上可以用BlockingQueue接口嘛,要用链式容器的话可以用LinkedBlockingQueue。


[code="java"]
LinkedList items = new LinkedList();
[/code]
改成
[code="java"]
List items = java.util.Collections.synchronizedList(new LinkedList()) ;
[/code]


[code="java"]
items.removeFirst();
[/code]
改成
[code="java"]
return items.remove(0);
[/code]