Java易失和/或同步

use*_*403 4 java concurrency volatile synchronized

我有一个静态方法,它应该根据当前时间戳生成一个唯一的ID,如下面的代码所示.为了确保新生成的ID与先前生成的ID不同(由于非常快的计算机使得毫秒不会改变),我放入一个循环来比较新生成的ID与先前生成的ID.如果它们相同,它将生成另一个ID.

public class Util {

    protected static String uniqueID;

    public static String generateUniqueID() {
        SimpleDateFormat timstampFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        do {
            String timestamp = timstampFormat.format(new Date());
            if (!timestamp.equals(uniqueID)) {
                uniqueID = timestamp;
                return uniqueID;
            }
        } while (true);
    }

}
Run Code Online (Sandbox Code Playgroud)

我希望上面的代码在多个线程调用方法时起作用.

如果我只将volatile关键字放入uniqueID变量,那还不够好吗?我还需要同步块吗?

如果有一个synchronized块但没有volatile关键字呢?

提前致谢.

添加:

如果我更改为以下代码,仍然需要volatile关键字吗?

public class Util {

    private static volatile String uniqueID;

    public static synchronized String generateUniqueID() {
        uniqueID = UUID.randomUUID().toString();
        return uniqueID;
    }

}
Run Code Online (Sandbox Code Playgroud)

Pau*_*aul 8

此代码存在多个问题.

永远不要使用时间戳作为UID,除非你是绝对肯定的,否则在一个小于你正在使用的时间戳的最低分辨率的时间内将不会生成多个UID.我建议改用完全不同的方法.如果您绝对想要使用时间戳格式或仅使用计数器,请将计数器附加到时间戳.除了正常的系统时间之外,另一种选择是使用System.nanoTime(),尽管这种方法可能会提供相当多的陷阱.

如果您尝试在相同的毫秒内生成两个UID,则while循环将循环最多一整毫秒.没有快速的计算机可以完全浪费CPU时间.循环至少会运行几千次而没有适当的结果.

标记变量volatile不会.您必须标记在方法中运行的整个块,synchronized以防止多个线程同时运行它.但考虑一个案例,您希望在一个ms内生成1000个UID.现在应该做什么,现在突然间需要一整秒钟.你正在制造一个巨大的瓶颈.

我的建议:
立即删除该方法.没有太多可以修复这个代码到在性能和正确性方面实际上可接受的程度.阅读本教程关于并发性.获取生成UID的新方法并从头开始.

或者:
为什么甚至为已经存在的东西编写代码?使用Oracle提供的UID类.另一个好方法是使用UUID,它是实用程序包的一部分,很可能比一般UID.取决于您对生成的UID的要求.