Два потока читают один и тот же список, но с разных концов
У меня есть список, мне нужно пройти по списку двумя потоками. Одно чтение сверху донизу, а другое снизу вверх. И когда они пересекаются, чтение должно прекратиться.
Для перебора списка я могу использовать ListIterator, но я не могу думать, как эти потоки будут читать из того же списка?
3 ответа
Поскольку потоки только читают, нет необходимости использовать потокобезопасную версию списка.
Чтобы убедиться, что потоки перестают читать, когда они пересекаются, вам нужно синхронизировать их, поэтому перед чтением другого элемента они должны проверить, доступен ли этот элемент. Наивный способ реализовать это - сохранить текущий индекс в каждом потоке и предоставить другому потоку доступ к этому индексу (обязательно синхронизируйте этот индекс). Это приведет к большим накладным расходам.
Лучшая идея состоит в том, чтобы работать в партиях. Разделите список на разделы, например, по 16 пунктов. Затем потоки могут прочитать всю партию, прежде чем проверять пересечение.
Вы можете разделить список, в котором один поток будет читать от 0 до половины, а второй - от половины +1 до последнего.
class PrintList implements Runnable{
private List list = new ArrayList();
public PrintList(List list){
this.list = list;
}
@Override
public void run() {
if(Thread.currentThread().getName() != null && Thread.currentThread().getName().equalsIgnoreCase("thread1")){
int mid =list.size()/2;
for(int i = 0; i< mid;i++){
// System.out.println("Thread 1 "+list.get(i));
}
}else if(Thread.currentThread().getName() != null && Thread.currentThread().getName().equalsIgnoreCase("thread2")){
for(int i = mid; i<list.size(); i++){
//System.out.println("Thread 2 "+list.get(i));
}
}
}
}
Существует простой способ решить эту проблему, если принять во внимание, что общее количество элементов, обработанных потоком A, и общее количество элементов, обработанных потоком B, в конце будут равны количеству элементов в списке.
Поэтому, если у вас есть счетчик, который уменьшается каждый раз, когда поток хочет обработать следующий элемент, вы знаете, что все сделано, когда счетчик достигнет нуля.
public class MyThreadSyncer {
protected final AtomicInteger elementCount;
public MyThreadSyncer(final Collection c) {
elementCount = new AtomicInteger(c.size());
}
public boolean canProcessNext() {
return (this.elementCount.decrementAndGet() >= 0);
}
}
Используя этот класс, каждый поток устанавливает свой собственный частный счетчик (поток A равен 0, поток B имеет размер 1), а затем проверяет следующим образом:
if (this.threadSyncer.canProcessNext()) {
this.theTasks[this.myPosition].process();
this.myPosition += 1; // this line in thread A
this.myPosition -= 1; // this line in thread B
}