我最近重构了一段用于生成唯一负数的代码。
编辑:多个线程获取这些 id 并作为键添加到数据库;数字需要为负数才能轻松识别 - 在测试会话结束时,它们会从数据库中删除。
我的 Java 算法如下所示:
private final Set<Integer> seen = Collections.synchronizedSet(new HashSet<Integer>());
public Integer generateUniqueNegativeIds() {
int result = 0;
do {
result = random.nextInt();
if (result > 0) {
result *= -1;
}
} while (!seen.add(result));
return result;
}
上面的代码结构,加上它对集合和“重试”循环的推测性添加,让我觉得有一个等效的非阻塞算法可以用任何原子变量替换同步集合。
我做了几次尝试使用原子变量重写,但都没有通过多线程攻击测试。
有没有优雅的非阻塞等价物?
编辑:出于好奇,这是使用原子整数作为保护的有缺陷的尝试
private final AtomicInteger atomi = new AtomicInteger(0);
public Integer generateUniqueNegativeIdsWithAtomicAlgo() {
boolean added = false;
int result = 0;
do {
result = random.nextInt();
if (result > 0) {
result *= -1;
}
if (atomi.compareAndSet(0, result)) {
added = cache.add(result);
}
} while (!added);
return atomi.getAndSet(0);
}
编辑:下面的测试工具:
public static void main(String[] args) {
final int NUMBER_OF_THREADS = 10000;
final Set<Integer> uniques = Collections.synchronizedSet(new HashSet<Integer>());
final List<Integer> positives = Collections.synchronizedList(new ArrayList<Integer>());
final NegativeUniqueIdGenerator nuig = new NegativeUniqueIdGenerator();
Thread[] workers = new Thread[NUMBER_OF_THREADS];
long start = System.nanoTime();
for (int i = 0; i < workers.length; i++) {
Runnable runnable = new Runnable() {
public void run() {
int number = nuig.generateUniqueNegativeIds();
if (number > 0) {
positives.add(number);
}
uniques.add(number);
}
};
workers[i] = new Thread(runnable);
workers[i].start();
}
for (int i = 0; i < workers.length; i++) {
try {
workers[i].join();
} catch (InterruptedException ie) {}
}
long end = System.nanoTime();
System.out.println(String.format("duration = %dns", (end - start)));
System.out.println(String.format("#threads = %d", NUMBER_OF_THREADS));
System.out.println(String.format("#uniques = %d", uniques.size()));
System.out.println(String.format("#positives = %d", positives.size()));
System.out.println(String.format("#duplicates = %d", NUMBER_OF_THREADS - uniques.size()));
System.out.println(String.format("ratio = %f",
((double) NUMBER_OF_THREADS - uniques.size())
/ NUMBER_OF_THREADS));
assert uniques.size() == NUMBER_OF_THREADS;
}