我最近重构了一段用于生成唯一负数的代码。
edit: 多个线程获取这些 id 并将其作为键添加到数据库中;数字必须为负数才能轻松识别 - 在测试 session 结束时,它们将从数据库中删除。
我的 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;
}
最佳答案
如果你不关心随机性,你可以像这样递减一个计数器:
private final AtomicInteger ai=new AtomicInteger(0);
public int nextID() {
return ai.addAndGet(-1);
}
编辑:
对于随机数,您可以只使用您的解决方案并使用例如。 ConcurrentHashMap 或 ConcurrentSkipListSet 而不是 synchronizedSet。您必须确保不同的线程使用随机生成器的不同实例,并且这些生成器不相关。
关于java - 生成唯一负数的非阻塞算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/579925/