Независимо от используемого API, вы не сможете добиться как поточнобезопасного, так и свободного от блокировки обмена элементов массива в Java.
Замена элемента требует нескольких операций чтения и обновления, которые должны выполняться атомарно. Для симуляции атомности вам нужен замок.
РЕДАКТИРОВАТЬ:
Альтернативой алгоритму без блокировки может быть микроблокировка: вместо блокировки всего массива можно блокировать только те элементы, которые меняются местами.
Ценность этого подхода полностью сомнительна. То есть, если алгоритм, который требует замены элементов, может гарантировать, что разные потоки будут работать в разных частях массива, синхронизация не требуется.
В противном случае, когда разные потоки могут попытаться поменять местами перекрывающиеся элементы, тогда порядок выполнения потоков будет иметь значение. Например, если один поток пытается поменять местами элементы 0 и 1 массива, а другой одновременно пытается поменять местами 1 и 2, то результат будет полностью зависеть от порядка выполнения, для начальных {'a', 'b', 'c '} вы можете получить либо {' b ',' c ',' a '} или {' c ',' a ',' b '}. Следовательно, вам потребуется более сложная синхронизация.
Вот быстрый и грязный класс для массивов символов, который реализует микроблокировку:
import java.util.concurrent.atomic.AtomicIntegerArray;
class SyncCharArray {
final private char array [];
final private AtomicIntegerArray locktable;
SyncCharArray (char array[])
{
this.array = array;
// create a lock table the size of the array
// to track currently locked elements
this.locktable = new AtomicIntegerArray(array.length);
for (int i = 0;i<array.length;i++) unlock(i);
}
void swap (int idx1, int idx2)
{
// return if the same element
if (idx1==idx2) return;
// lock element with the smaller index first to avoid possible deadlock
lock(Math.min(idx1,idx2));
lock(Math.max(idx1,idx2));
char tmp = array[idx1];
array [idx1] = array[idx2];
unlock(idx1);
array[idx2] = tmp;
unlock(idx2);
}
private void lock (int idx)
{
// if required element is locked when wait ...
while (!locktable.compareAndSet(idx,0,1)) Thread.yield();
}
private void unlock (int idx)
{
locktable.set(idx,0);
}
}
Вам нужно создать SyncCharArray, а затем передать его всем потокам, которые требуют замены:
char array [] = {'a','b','c','d','e','f'};
SyncCharArray sca = new SyncCharArray(array);
// then pass sca to any threads that require swapping
// then within a thread
sca.swap(15,3);
Надеюсь, в этом есть какой-то смысл.
UPDATE:
Некоторое тестирование показало, что, если у вас нет большого количества потоков, одновременно обращающихся к массиву (более 100 на обычном оборудовании), простая синхронизация (массив) {} работает намного быстрее, чем сложная синхронизация.