我是多線程編程的新手,並且有一個問題。我如何讓每個線程遍歷由不同線程添加的列表中的所有元素?迭代通過正在被另一個線程修改的列表
這裏有一個簡單的程序來演示。我有一個整數列表和10個線程,編號爲1到10,正在處理它。每個線程都將列表中的所有值寫入StringBuilder。線程寫入列表中的所有值後,會將其編號添加到列表中,然後終止。
我想讓每個線程繼續檢查列表中的元素,直到列表不再被任何其他線程修改,但是在鎖定時遇到問題。如果成功的話,那麼該程序將輸出可能看起來像:
3: 1,
8: 1,3,2,4,5,7,
6: 1,3,2,4,5,7,8,
9: 1,3,2,4,5,7,8,6,
7: 1,3,2,4,5,
10: 1,3,2,4,5,7,8,6,9,
5: 1,3,2,4,
4: 1,3,2,
2: 1,3,
1:
有時碰巧,但往往兩個或更多的線程鎖設置之前完成,所以迭代過早結束:
1:
2: 1,5,4,8,7,3,10,
10: 1,5,4,8,7,3,
9: 1,5,4,8,7,3,10,2,
3: 1,5,4,8,7,
7: 1,5,4,8,
5: 1, <<one of these threads didn't wait to stop iterating.
4: 1, <<
8: 1,5,4,
6: 1,5,4,8,7,3,10,2,
有沒有人有任何想法?
===========
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
public class ListChecker implements Runnable{
static List<Integer> list = new ArrayList<Integer>();
static ReentrantLock lock = new ReentrantLock();
int id;
StringBuilder result = new StringBuilder();
public ListChecker(int id){
this.id = id;
}
@Override
public void run() {
int i=0;
do{
while (i < list.size()){
result.append(list.get(i++)).append(',');
}
if (!lock.isLocked()){
break;
}
}while (true);
addElement(id);
System.out.println(id + ": " + result.toString());
}
public void addElement(int element){
try{
lock.lock();
list.add(element);
}finally{
lock.unlock();
}
}
public static void main(String[] args){
for(int i=1; i<=10; i++){
ListChecker checker = new ListChecker(i);
new Thread(checker).start();
}
}
}
編輯:感謝您的幫助迄今。我應該澄清一下,我希望每個線程同時在列表中迭代。在我的情況中,每個線程都需要在列表的每個元素上執行很多處理(而不是追加到StringBuffer,我正在對候選項目與入圍名單進行大量比較)。因此,讓多線程同時處理同一列表中的每個線程都可以提高我的性能。所以,我不認爲鎖定整個迭代,或者說整個迭代是一個同步(列表)塊,將會起作用。
編輯2:我想我明白了。訣竅不僅僅是在添加元素的同時添加元素,還要確定是否還有其他元素。這可以防止線程2在線程1完成添加到列表之前停止其迭代。它看起來有點笨拙,但是這使我需要在同步塊外的多個線程中運行的代碼,所以我的真實情況應該會得到我需要的性能提升。
感謝大家幫助!
import java.util.ArrayList;
import java.util.List;
public class ListChecker2 implements Runnable{
static List<Integer> list = new ArrayList<Integer>();
int id;
StringBuilder result = new StringBuilder();
public ListChecker2(int id){
this.id = id;
}
@Override
public void run() {
int i = 0;
do{
synchronized (list) {
if (i >= list.size()){
list.add(id);
System.out.println(id + ": " + result.toString());
return;
}
}
result.append(list.get(i++)).append(',');
System.out.println("running " + id);
}while(true);
}
public static void main(String[] args){
for(int i=1; i<=30; i++){
ListChecker2 checker = new ListChecker2(i);
new Thread(checker).start();
}
}
}
危險之處在於一個線程將到達列表(1)的末尾並添加一個新條目(2),而另一個線程在(1)和(2)之間添加其條目。然後,您將有一個未查看所有條目的線程。你會考慮使用一個特製的列表,當它迭代到最後一個條目時,它會自動將自己鎖定到你的線程中? – OldCurmudgeon 2012-03-29 13:29:34