java - 生成唯一负数的非阻塞算法

标签 java algorithm atomic nonblocking

我最近重构了一段用于生成唯一负数的代码。
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/

相关文章:

algorithm - 如何有效地将整数转换为斐波那契编码?

algorithm - 谜题:N个人坐在圆 table 上。没有交叉任何其他握手的握手方式

Java AtomicInteger 在一百万次迭代后不等于一百万(包括最小示例)

java - 需要将 AssetInputStream 转换为 FileInputStream

java - 如何使用搜索功能使过滤后的项目显示在 ListView 中(android)

java - 如何在不使用任何循环结构的情况下遍历集合?

java - 本地开发服务器的身份验证不起作用

java - 计算直线上特定点的坐标

c - Solaris 中的 pthread 互斥与原子操作

Java:线程间共享和调用变量