1

My program needs to generate unique labels that consist of a tag, date and time. Something like this:

"myTag__2019_09_05__07_51"

If one tries to generate two labels with the same tag in the same minute, one will receive equal labels, what I cannot allow. I think of adding as an additional suffix result of System.nanoTime() to make sure that each label will be unique (I cannot access all labels previously generated to look for duplicates):

"myTag__2019_09_05__07_51__{System.nanoTime() result}"

Can I trust that each invocation of System.nanoTime() this will produce a different value? I tested it like this on my laptop:

assertNotEquals(System.nanoTime(), System.nanoTime())

and this works. I wonder if I have a guarantee that it will always work.

Michał Powłoka
  • 1,424
  • 1
  • 13
  • 31
  • 3
    You can't guarantee that `System.nanoTime()` always produces unique values. You can consider the UUID (https://docs.oracle.com/javase/7/docs/api/java/util/UUID.html) which generates unique id's. If your tag length permits you can combine both UUID & timestamp – Ramesh Subramanian Sep 05 '19 at 06:01
  • 2
    No one can guarantee you to always produce different values. After all, there is only a finite number of values to be produced. A duplicate is going to appear _at some point_. All we can do is to make that happen as far in the future as possible. – Sweeper Sep 05 '19 at 06:15
  • No, it is not even guaranteed that subsequent calls to `System.nanoTime()` will return increasing values. See https://stackoverflow.com/questions/8853698/is-system-nanotime-system-nanotime-guaranteed-to-be-0 – Jesper Sep 05 '19 at 12:42

1 Answers1

5

TLDR; If you only use a single thread on a popular VM on a modern operating system, it may work in practice. But many serious applications use multiple threads and multiple instances of the application, and there won't be any guarantee in that case.

The only guarantee given in the Javadoc for System.nanoTime() is that the resolution of the clock is at least as good as System.currentTimeMillis() - so if you are writing cross-platform code, there is clearly no expectation that the results of nanoTime are unique, as you can call nanoTime() many times per millisecond.

On my OS (Java 11, MacOS) I always get at least one nanosecond difference between successive calls on the same thread (and that is after Integer.MAX_VALUE looks at successive return values); it's possible that there is something in the implementation that guarantees it.

However it is simple to generate duplicate results if you use multiple Threads and have more than 1 physical CPU. Here's code that will show you:

public class UniqueNano {
    private static volatile long a = -1, b = -2;

    public static void main(String[] args) {
        long max = 1_000_000;
        new Thread(() -> {
            for (int i = 0; i < max; i++) { a = System.nanoTime(); }
        }).start();
        new Thread(() -> {
            for (int i = 0; i < max; i++) { b = System.nanoTime(); }
        }).start();
        for (int i = 0; i < max; i++) {
            if (a == b) {
                System.out.println("nanoTime not unique");
            }
        }
    }
}

Also, when you scale your application to multiple machines, you will potentially have the same problem.

Relying on System.nanoTime() to get unique values is not a good idea.

Erwin Bolwidt
  • 30,799
  • 15
  • 56
  • 79
  • Thank you, that is a very nice answer. I already replaced it with UUID and as far as I understand it should remain unique in a multithread environment. Although, if I want to scale it on multiple JVMs I need to provide a common pool of used values because there is no way to control it then, right? – Michał Powłoka Sep 05 '19 at 19:08