Java 遍历另一个线程正在修改的列表
我是多线程编程新手,有一个问题。如何让每个线程遍历由不同线程添加到的列表中的所有元素 这里有一个简单的程序来演示。我有一个整数列表,10个线程,编号从1到10,正在处理它。每个线程都要将列表中的所有值写入StringBuilder。线程写入列表中的所有值后,将其编号添加到列表中,然后终止 我试图让每个线程不断检查列表中的元素,直到列表不再被任何其他线程修改,但在锁定列表时遇到问题。如果成功,程序的输出可能如下所示:Java 遍历另一个线程正在修改的列表,java,multithreading,Java,Multithreading,我是多线程编程新手,有一个问题。如何让每个线程遍历由不同线程添加到的列表中的所有元素 这里有一个简单的程序来演示。我有一个整数列表,10个线程,编号从1到10,正在处理它。每个线程都要将列表中的所有值写入StringBuilder。线程写入列表中的所有值后,将其编号添加到列表中,然后终止 我试图让每个线程不断检查列表中的元素,直到列表不再被任何其他线程修改,但在锁定列表时遇到问题。如果成功,程序的输出可能如下所示: 3: 1, 8: 1,3,2,4,5,7, 6: 1,3,2,4,5,7,8,
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,我正在对候选项与最终入围者列表进行大量比较。因此,让每个线程能够同时在同一个列表上工作是多线程提高性能所必需的。所以,我不认为锁定整个迭代,或者将整个迭代放在synchronizedlist块上会起作用
编辑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();
}
}
}
你选择了一个棘手的案子,让你的脚湿了。同时读取和写入对象是复杂的,从来没有被推荐过,而且经常被特别阻止,ie无法使用迭代器和列表 但是 如果必须这样做,则应在添加元素之前在列表上同步,在添加元素之后调用list.notifyAll,这将唤醒等待更多结果的线程。同时,您的其他线程应该读到最后,然后在列表上同步您需要在对象上同步以调用wait、notify或notifyAll并调用wait
顺便说一句,实现这一点的一个更简单的方法是使用listener/observer/observable模式,尽管listener模式通常是单线程的。你可能想在谷歌上搜索这方面的教程。你选择了一个很难解决的问题。同时读取和写入对象是复杂的,从来没有被推荐过,而且经常被特别阻止,ie无法使用迭代器和列表 但是 如果必须这样做,则应在添加元素之前在列表上同步,在添加元素之后调用list.notifyAll,这将唤醒等待更多结果的线程。同时,您的其他线程应该读到最后,然后在列表上同步您需要在对象上同步以调用wait、notify或notifyAll并调用wait
顺便说一句,实现这一点的一个更简单的方法是使用listener/observer/observable模式,尽管listener模式通常是单线程的。您可能需要在谷歌上搜索有关此的教程。您可以使用类似Collections.synchronizedListList的方法 这是您使用它的方式:
List list = Collections.synchronizedList(new ArrayList());
...
synchronized (list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
您可以使用类似Collections.synchronizedListList的方法 这是您使用它的方式:
List list = Collections.synchronizedList(new ArrayList());
...
synchronized (list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
通过在列表上同步,您可以更简单地做到这一点
public class ListChecker implements Runnable {
// Number of threads.
static final int THREADS = 10;
// The list.
static final List<Integer> list = new ArrayList<Integer>(THREADS);
// My ID
int id;
public ListChecker(int id) {
// Remember my ID.
this.id = id;
}
@Override
public void run() {
// My string.
StringBuilder result = new StringBuilder ();
// Wait for exclusive access to the list.
synchronized (list) {
// Build my string.
for ( Integer i : list ) {
result.append(i).append(",");
}
// Add me to the list.
list.add(id);
}
System.out.println(id + ": " + result.toString());
}
public static void main(String[] args) {
for (int i = 1; i <= THREADS; i++) {
ListChecker checker = new ListChecker(i);
new Thread(checker).start();
}
}
}
增加
如果您需要避免像编辑建议的那样锁定整个列表,您可以尝试一个特殊的列表,该列表在每次传递最后一个条目时都会自动锁定。当然,你需要特别解锁它
遗憾的是,这种技术不能很好地处理空列表的情况。也许你能想出一个合适的解决办法
public class ListChecker implements Runnable {
// Number of threads.
static final int THREADS = 20;
// The list.
static final EndLockedList<Integer> list = new EndLockedList<Integer>();
// My ID
int id;
public ListChecker(int id) {
// Remember my ID.
this.id = id;
}
@Override
public void run() {
// My string.
StringBuilder result = new StringBuilder();
// Build my string.
for (int i = 0; i < list.size(); i++) {
result.append(i).append(",");
}
// Add me to the list.
list.add(id);
// Unlock the end lock.
list.unlock();
System.out.println(id + ": " + result.toString());
}
public static void main(String[] args) {
for (int i = 0; i < THREADS; i++) {
ListChecker checker = new ListChecker(i + 1);
new Thread(checker).start();
}
}
private static class EndLockedList<T> extends ArrayList<T> {
// My lock.
private final Lock lock = new ReentrantLock();
// Were we locked?
private volatile boolean locked = false;
@Override
public boolean add(T it) {
lock.lock();
try {
return super.add(it);
} finally {
lock.unlock();
}
}
// Special get that locks the list when the last entry is got.
@Override
public T get(int i) {
// Get it.
T it = super.get(i);
// If we are at the end.
if (i == size() - 1) {
// Speculative lock.
lock.lock();
// Still at the end?
if (i < size() - 1) {
// Release the lock!
lock.unlock();
} else {
// Remember we were locked.
locked = true;
// It is now locked for putting until specifically unlocked.
}
}
// That's the one.
return it;
}
// Unlock.
void unlock() {
if (locked) {
locked = false;
lock.unlock();
}
}
}
}
通过在列表上同步,您可以更简单地做到这一点
public class ListChecker implements Runnable {
// Number of threads.
static final int THREADS = 10;
// The list.
static final List<Integer> list = new ArrayList<Integer>(THREADS);
// My ID
int id;
public ListChecker(int id) {
// Remember my ID.
this.id = id;
}
@Override
public void run() {
// My string.
StringBuilder result = new StringBuilder ();
// Wait for exclusive access to the list.
synchronized (list) {
// Build my string.
for ( Integer i : list ) {
result.append(i).append(",");
}
// Add me to the list.
list.add(id);
}
System.out.println(id + ": " + result.toString());
}
public static void main(String[] args) {
for (int i = 1; i <= THREADS; i++) {
ListChecker checker = new ListChecker(i);
new Thread(checker).start();
}
}
}
增加
如果您需要避免像编辑建议的那样锁定整个列表,您可以尝试一个特殊的列表,该列表在每次传递最后一个条目时都会自动锁定。当然,你需要特别解锁它
遗憾的是,这种技术不能很好地处理空列表的情况。也许你能想出一个合适的解决办法
public class ListChecker implements Runnable {
// Number of threads.
static final int THREADS = 20;
// The list.
static final EndLockedList<Integer> list = new EndLockedList<Integer>();
// My ID
int id;
public ListChecker(int id) {
// Remember my ID.
this.id = id;
}
@Override
public void run() {
// My string.
StringBuilder result = new StringBuilder();
// Build my string.
for (int i = 0; i < list.size(); i++) {
result.append(i).append(",");
}
// Add me to the list.
list.add(id);
// Unlock the end lock.
list.unlock();
System.out.println(id + ": " + result.toString());
}
public static void main(String[] args) {
for (int i = 0; i < THREADS; i++) {
ListChecker checker = new ListChecker(i + 1);
new Thread(checker).start();
}
}
private static class EndLockedList<T> extends ArrayList<T> {
// My lock.
private final Lock lock = new ReentrantLock();
// Were we locked?
private volatile boolean locked = false;
@Override
public boolean add(T it) {
lock.lock();
try {
return super.add(it);
} finally {
lock.unlock();
}
}
// Special get that locks the list when the last entry is got.
@Override
public T get(int i) {
// Get it.
T it = super.get(i);
// If we are at the end.
if (i == size() - 1) {
// Speculative lock.
lock.lock();
// Still at the end?
if (i < size() - 1) {
// Release the lock!
lock.unlock();
} else {
// Remember we were locked.
locked = true;
// It is now locked for putting until specifically unlocked.
}
}
// That's the one.
return it;
}
// Unlock.
void unlock() {
if (locked) {
locked = false;
lock.unlock();
}
}
}
}
请更新您的跑步方法。让我知道它是否按照你的期望工作
public void run() {
int i=0;
lock.lock();
while (i < list.size()){
result.append(list.get(i++)).append(',');
}
addElement(id);
lock.unlock();``
System.out.println(id + ": " + result.toString());
}
请更新您的跑步方法。让我知道它是否按照你的期望工作
public void run() {
int i=0;
lock.lock();
while (i < list.size()){
result.append(list.get(i++)).append(',');
}
addElement(id);
lock.unlock();``
System.out.println(id + ": " + result.toString());
}
这确实给出了c
正确的结果,但如果我理解正确的话,它是通过一次将迭代限制为单个线程来实现的。如果在while循环中打印每个线程的id,尤其是如果将线程数从10个增加到30个左右,就可以看到这一点。我认为Tom将迭代器放在synchronizedlist块中的解决方案做了同样的事情。理想情况下,多个线程可以同时迭代同一个列表,但在更新列表时暂停。但是谢谢你的帮助!是的,因为列表是共享的,所以我们应该同步访问,否则结果将不正确。这确实会给出正确的结果,但如果我理解正确,它会通过将迭代限制为一次一个线程来实现。如果在while循环中打印每个线程的id,尤其是如果将线程数从10个增加到30个左右,就可以看到这一点。我认为Tom将迭代器放在synchronizedlist块中的解决方案做了同样的事情。理想情况下,多个线程可以同时迭代同一个列表,但在更新列表时暂停。但是谢谢你的帮助!是的,由于列表是共享的,我们应该同步访问,否则结果将不正确谢谢,O.C.请查看我的编辑;我认为这将迭代限制为一个线程,我想阻止它。指向你,因为你的评论最终让这个东西变得有意义,EndLockedList的想法非常聪明。谢谢,O.C.请查看我的编辑;我认为这将迭代限制为一个线程,我想阻止它。指向你,因为你的评论最终使这个东西变得有意义,EndLockedList的想法非常聪明。谢谢,Tom!请看我的编辑;如果可能的话,我希望在多个线程中同时进行迭代,但我认为这一更改限制了每次只进行一个线程。谢谢,Tom!请看我的编辑;如果可能的话,我希望在多个线程中同时进行迭代,但我认为这一更改限制了它一次只能执行一个线程。危险在于,一个线程将到达列表1的末尾,并添加一个新条目2,而另一个线程将其条目添加到1和2之间。然后,您将有一个线程没有查看所有条目。你会考虑使用一个专门制作的列表,一旦它迭代到最后一个条目,自动将自己锁定到线程中吗?危险是一个线程将到达列表1的末尾,并添加新的条目2,而另一个线程在1和2之间添加其条目。然后,您将有一个线程没有查看所有条目。你会考虑使用一个专门制作的列表,一旦它迭代到最后一个条目,自动将自己锁定到你的线程中吗?