【问题标题】:Thread safe unique number generator线程安全的唯一编号生成器
【发布时间】:2019-03-24 19:20:44
【问题描述】:

如何实现java线程安全的唯一数字生成器? 这是我的版本

public class Generator {
    List<Integer> list = new ArrayList<>();

    public Generator() {
        for (int i = 0; i < 1000; i++) {
            list.add(i);
        }
    }

    public Integer generate() {
        Collections.shuffle(list);
        return list.get(0);
    }
}

但是理论上,洗牌的时候可以得到一个唯一的号码。如何实现独特性?洗牌时是否需要在我的收藏中同步,即

synchronized (list) {
    Collections.shuffle(list);
}

谢谢。

【问题讨论】:

  • 嗯,这不是唯一的。 get(0) 可能返回相同的数字。

标签: java multithreading thread-safety


【解决方案1】:

这里有两个问题,安全发布,然后是变异 (shuffle()) 的后续线程安全,然后是读取 (get(0))。

我认为您的代码根本不能解决安全发布问题。为此使用volatilefinal,并为volatile 在ctor 中最后进行分配。

public class Generator {
    private final List<Integer> list = new ArrayList<>();

    public Generator() {
        for (int i = 0; i < 1000; i++) {
            list.add(i);
        }
    }

或者

public class Generator {
    private volatile List<Integer> list; 

    public Generator() {
        List<Integer> temp = = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            temp.add(i);
        }
        list = temp;
    }

现在我认为您可以在list(或任何其他对象)上进行同步以获得线程安全。您必须确保 get 在同步块内。请注意,您的代码存在错误,实际上并不会产生唯一的数字。

public Integer generate() {
  synchronized( list ) {
    Collections.shuffle(list);
    return list.get(0);
  }
}

或者

public synchronized Integer generate() {
    Collections.shuffle(list);
    return list.get(0);
}

请参阅 Brian Goetz Java 并发实践了解更多信息,尤其是。安全出版。 SO 也有一个简短的discussion of the Safe Publication pattern

获取唯一编号只是逻辑,与线程安全无关。首先在ctor中只洗牌一次,然后只增加一个计数器以按顺序返回数字。请记住,您的列表只有 1000 个号码,因此您最多可以返回 1000 个唯一号码。

private int index;
public synchronized Integer generate() {
    if( index >= 1000 ) throw IllegalArgumentError();
    return list.get( index++ );
}

【讨论】:

  • 这是一个漂亮的答案,赞。
  • 感谢您的回答。我知道,这个生成器不能返回唯一的数字,我怎样才能实现唯一性?
【解决方案2】:

我在这里猜测(对不起,如果偏离基础),您正在寻找 0

ThreadLocalRandom.current().nextInt(1000);

【讨论】:

  • 需要在不使用 Random 和 ThreadLocalRandom 的情况下创建生成器。
  • ...Collections.shuffle() 使用随机数
  • @NotaJD ,如果我在同一秒产生不同的线程来获取值,它会是唯一的吗?