我最近重构了一段用于生成唯一负数的代码。
编辑:多个线程获取这些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;
}
的代码结构,其投机除了一套和“重试”循环,让我觉得有它取代了同步设置具有等效非阻塞算法任何atomic variables。
我做了几次尝试使用原子变量重新编写代码,但都未通过多线程攻击测试。
有没有优雅的无阻塞等效物?
编辑:为了好奇这里是用原子整数作为后卫
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;
}
AtomicInteger是如何失败的?这些ID必须是不可预测的,或者是一个可接受的序列? – erickson 2009-02-24 00:06:38
如果我需要不可预知性,使用原子变量的解决方案会失败,是的 – jorgetown 2009-02-24 09:44:48
好吧,它需要变得多么不可预测?您是否试图抵御想要预测ID的攻击者,或者只是确保您具有统一的分布以获得良好的性能 - 例如,避免散列表中的“热”桶。 – erickson 2009-02-25 03:51:00