Concurrency and System.arraycopy

0
20 июл 2016 11:14
Данный код чередует элементы с двух листов. к примеру list1 = {0,2},list2 = {1,3}
в результате получаем {0,1,2,3}
Не будет ли проблем при работе без синхронизации при вызове копирования.
Гарантируется ли, что в результате всегда данные будет видны в основном потоке( System.out.println(new ArrayList<Object>(Arrays.asList(list)));), и если да, то почему?
Ситуация следующая, допустим один поток записал данные в кэш(изменения массива было), и когда будем выводить данные, данных не будет, так как они висят в кэше, а вместо этого будет null ссылки.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class AA {
	public static final int size = 100;
	public static AtomicInteger middle = new AtomicInteger();

	public static void main(String[] args) throws InterruptedException {
		int cores = Runtime.getRuntime().availableProcessors();
		int threads = cores*4;
		ExecutorService executorService = Executors.newFixedThreadPool(threads);
		CountDownLatch countDownLatch = new CountDownLatch(threads);
		ArrayList<Object> a = new ArrayList<>(size/2);
		ArrayList<Object> b = new ArrayList<>(size/2);
		Object[] list = new Object[size];
		
		
		for(int i =0; i < size; i++){
			if(i%2==0){
				a.add(i);
			}else{
				b.add(i);
			}
		}
	
		int partData =  a.size()/threads;
		int currentIndex = 0;
		for(int i = 0; i<threads-1; i++){
			executorService.submit(new SorterCallable(i*partData, i*partData+partData, currentIndex, list, a, b,countDownLatch));
			currentIndex+=partData*2;
		}
		
		executorService.submit(
				new SorterCallable((threads-1)*partData, a.size(), 
														currentIndex, list, a, b, countDownLatch));
		countDownLatch.await();
		executorService.shutdownNow();
		System.out.println(new ArrayList<Object>(Arrays.asList(list)));
		
	}
}
class SorterCallable implements Callable<Object>{
	private int start;
	private int end;
	private int currentIndex;
	private Object[] list;
	private List<Object> a;
	private List<Object> b;
	private CountDownLatch countDownLatch;
//	private static final ReentrantLock lock = new ReentrantLock();
	@Override
	public Object call() throws Exception {
		try{
		ArrayList<Object> arrayList = new ArrayList<>();
		for(int i=start; i<end; i++){
			arrayList.add(a.get(i));
			arrayList.add(b.get(i));
		}
//		lock.lock();
		
//			list.addAll(currentIndex, arrayList);
			System.arraycopy(arrayList.toArray(), 0, list, currentIndex  , arrayList.size());
		
//		lock.unlock();
		countDownLatch.countDown();
		}catch(Throwable throwable){
			throwable.printStackTrace();
		}
		return null;
	}


	public SorterCallable(int start, int end, int currentIndex, Object[] list, List<Object> a, List<Object> b, CountDownLatch countDownLatch) {
		super();
		
		this.start = start;
		this.end = end;
		this.currentIndex = currentIndex;
		this.list = list;
		this.a = a;
		this.b = b;
		this.countDownLatch = countDownLatch;
	}


	@Override
	public String toString() {
		return "CallableSorter [start=" + start + ", end=" + end + ", currentIndex=" + currentIndex + "]";
	}
	
};

Ответов: 1

0
20 июл 2016 17:41
Проблемы конечно будут, т.к. без синхронизации видимость изменения элементов массива в разных потоках не гарантируется. Из документации на ExecutorService:
Quote:

Memory consistency effects: Actions in a thread prior to the submission of a Runnable or Callable task to an ExecutorService happen-before any actions taken by that task, which in turn happen-before the result is retrieved via Future.get().


Т.е. надо записывать результаты вызова методов submit в массив/коллекцию переменных типа Future, а затем, перед выводом результата в главном потоке, вызвать в цикле для этих переменных метод get.
Модераторы: Нет
Сейчас эту тему просматривают: Нет