如何在Java中同步类中的系统时间访问

如何在Java中同步类中的系统时间访问,java,time,system,synchronized,Java,Time,System,Synchronized,我正在编写一个类,当调用该类时,该类将调用一个方法以使用系统时间生成一个唯一的8个字符的字母数字作为参考ID。但我担心在某个时刻,可能会在同一毫秒内进行多次调用,导致相同的引用ID。如何保护对系统时间的调用不受可能同时调用此方法的多个线程的影响?对于唯一ID,系统时间是不可靠的来源。就这样。不要用它。 您需要某种形式的永久源(UUID使用安全的随机源,该源由操作系统提供) 系统时间可能会向后移动/跳跃,甚至几毫秒,从而完全破坏您的逻辑。如果你只能容忍64位,你可以使用这是一个很好的折衷方案,也可

我正在编写一个类,当调用该类时,该类将调用一个方法以使用系统时间生成一个唯一的8个字符的字母数字作为参考ID。但我担心在某个时刻,可能会在同一毫秒内进行多次调用,导致相同的引用ID。如何保护对系统时间的调用不受可能同时调用此方法的多个线程的影响?

对于唯一ID,系统时间是不可靠的来源。就这样。不要用它。 您需要某种形式的永久源(UUID使用安全的随机源,该源由操作系统提供)


系统时间可能会向后移动/跳跃,甚至几毫秒,从而完全破坏您的逻辑。如果你只能容忍64位,你可以使用这是一个很好的折衷方案,也可以自己做食谱:比如2012年初以来的18位(你还有700多年的时间)然后来自SecureRandom的46位随机性-这不是最好的情况,技术上可能会失败,但不需要外部持久性。

系统时间是唯一ID的不可靠来源。就这样。不要用它。 您需要某种形式的永久源(UUID使用安全的随机源,该源由操作系统提供)

系统时间可能会向后移动/跳跃,甚至几毫秒,从而完全破坏您的逻辑。如果你只能容忍64位,你可以使用这是一个很好的折衷方案,也可以自己做食谱:比如2012年初以来的18位(你还有700多年的时间)然后来自SecureRandom的46位随机性-这不是最好的情况,技术上可能会失败,但不需要外部持久性。

我建议将添加到引用ID。这将使引用更独特。但是,即使在线程内,对时间源的连续调用也可能传递相同的值。甚至对最高分辨率源()的调用也可能在某些硬件上产生相同的值。这个问题的一个可能的解决方案是根据其前一个时间值测试收集的时间值,并在“时间戳”中添加一个增量项。当这应该是人类可读的时,您可能需要超过8个字符。 最有效的时间戳源是GetSystemTimeAsFileTime API。我在回答中写了一些细节。

我建议将添加到引用ID中。这将使引用更加独特。但是,即使在线程内,对时间源的连续调用也可能传递相同的值。甚至对最高分辨率源()的调用也可能在某些硬件上产生相同的值。这个问题的一个可能的解决方案是根据其前一个时间值测试收集的时间值,并在“时间戳”中添加一个增量项。当这应该是人类可读的时,您可能需要超过8个字符。
最有效的时间戳源是GetSystemTimeAsFileTime API。我在回答中写了一些细节。

您可以使用
UUID
类为您的ID生成位,然后使用一些位运算符和
Long.toString
将其转换为base-36(字母数字)

对于100000个ID,代码的执行非常一致,速度非常快。当我将另一个数量级增加到1000000时,我开始获得重复的ID。我修改了修剪,将编码字符串的末尾改为开头,这大大提高了重复ID率。现在拥有1000000个ID并不能为我产生任何重复

您最好还是使用同步计数器,如
AtomicInteger
AtomicLong
,并使用上述代码对base-36中的数字进行编码,特别是如果您计划拥有大量ID


编辑:计数器方法,如果需要:

private final AtomicLong counter;

public IdGenerator(int start) {
    // start could also be initialized from a file or other
    // external source that stores the most recently used ID
    counter = new AtomicLong(start);
}

public String getId() {
    long result = counter.getAndIncrement();
    String encoded = Long.toString(result, 36);
    // Remove sign if negative
    if (result < 0)
        encoded = encoded.substring(1, encoded.length());
    // Trim extra digits or pad with zeroes
    if (encoded.length() > 8) {
        encoded = encoded.substring(0, 8);
    }
    while (encoded.length() < 8) {
        encoded = "0" + encoded;
    }
}
专用最终原子计数器;
公共IdGenerator(int启动){
//也可以从文件或其他文件初始化start
//存储最近使用的ID的外部源
计数器=新原子长(开始);
}
公共字符串getId(){
长结果=counter.getAndIncrement();
字符串编码=Long.toString(结果,36);
//如果为负数,请删除符号
如果(结果<0)
encoded=encoded.substring(1,encoded.length());
//修剪多余的数字或用零填充
if(encoded.length()>8){
编码=编码的子串(0,8);
}
while(encoded.length()<8){
encoded=“0”+已编码;
}
}

此代码是线程安全的,可以同时访问。

您可以使用
UUID
类为ID生成位,然后使用一些位运算符和
Long.toString
将其转换为base-36(字母数字)

对于100000个ID,代码的执行非常一致,速度非常快。当我将另一个数量级增加到1000000时,我开始获得重复的ID。我修改了修剪,将编码字符串的末尾改为开头,这大大提高了重复ID率。现在拥有1000000个ID并不能为我产生任何重复

您最好还是使用同步计数器,如
AtomicInteger
AtomicLong
,并使用上述代码对base-36中的数字进行编码,特别是如果您计划拥有大量ID


编辑:计数器方法,如果需要:

private final AtomicLong counter;

public IdGenerator(int start) {
    // start could also be initialized from a file or other
    // external source that stores the most recently used ID
    counter = new AtomicLong(start);
}

public String getId() {
    long result = counter.getAndIncrement();
    String encoded = Long.toString(result, 36);
    // Remove sign if negative
    if (result < 0)
        encoded = encoded.substring(1, encoded.length());
    // Trim extra digits or pad with zeroes
    if (encoded.length() > 8) {
        encoded = encoded.substring(0, 8);
    }
    while (encoded.length() < 8) {
        encoded = "0" + encoded;
    }
}
专用最终原子计数器;
公共IdGenerator(int启动){
//也可以从文件或其他文件初始化start
//存储最近使用的ID的外部源
计数器=新原子长(开始);
}
公共字符串getId(){
长结果=counter.getAndIncrement();
字符串编码=Long.toString(结果,36);
//如果为负数,请删除符号
如果(结果<0)
encoded=encoded.substring(1,encoded.length());
//修剪多余的数字或用零填充
if(encoded.length()>8){
编码=编码的子串(0,8);
}
while(encoded.length()<8){
编码
private final AtomicLong counter;

public IdGenerator(int start) {
    // start could also be initialized from a file or other
    // external source that stores the most recently used ID
    counter = new AtomicLong(start);
}

public String getId() {
    long result = counter.getAndIncrement();
    String encoded = Long.toString(result, 36);
    // Remove sign if negative
    if (result < 0)
        encoded = encoded.substring(1, encoded.length());
    // Trim extra digits or pad with zeroes
    if (encoded.length() > 8) {
        encoded = encoded.substring(0, 8);
    }
    while (encoded.length() < 8) {
        encoded = "0" + encoded;
    }
}