无论使用哪种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;
this.locktable = new AtomicIntegerArray(array.length);
for (int i = 0;i<array.length;i++) unlock(i);
}
void swap (int idx1, int idx2)
{
if (idx1==idx2) return;
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)
{
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);
希望这有些意义。
更新:
一些测试表明,除非你有很多线程同时访问数组(在普通硬件上超过100个),简单的同步(array){}比复杂的同步更快。
java.util.concurrent.atomic
不公开这些操作。 - Tom Hawtin - tackline