Как написать генератор последовательности Java для UniqueId, который будет автоматически сбрасываться в «1» (базовый порядковый номер) в полночь каждый день - PullRequest
0 голосов
/ 01 октября 2019

У нас есть служба загрузки Spring, где мы получаем файлы каждый день, из-за какой-то проблемы (от производителя) мы получаем несколько файлов с одинаковыми именами и датами. Новый файл перезаписывает старый, для его обработки мы хотим добавить последовательность (начиная с 1) к каждому имени файла. Но последовательность должна автоматически сбрасываться на «1» в полночь каждый день.

Кто-нибудь может предложить API или способ сброса последовательности.

Для генерации автопоследовательности мы используем AtomicSequenceGenerator,но мы не можем реализовать простую логику автоматического сброса.

public class AtomicSequenceGenerator implements SequenceGenerator {

    private AtomicLong value = new AtomicLong(1);

    @Override
    public long getNext() {
        return value.getAndIncrement();
    }
}

Ответы [ 3 ]

1 голос
/ 01 октября 2019

Чтобы не получать дважды 1:

public class AtomicSequenceGenerator implements SequenceGenerator {

    private AtomicLong value = new AtomicLong(1);
    private volatile LocalDate lastDate = LocalDate.now();

    @Override
    public long getNext() {
        LocalDate today = LocalDate.now();
        if (!today.equals(lastDate)) {
            synchronized(this) {
                if (!today.equals(lastDate)) {
                    lastDate = today;
                    value.set(1);
                }
            }
        }
        return value.getAndIncrement();
    }
}

Это немного уродливо, поэтому попробуйте один счетчик:

public class AtomicSequenceGenerator implements SequenceGenerator {

    private static long countWithDate(long count, LocalDate date) {
        return (((long)date.getDayOfYear()) << (63L-9)) | count;
    }

    private static long countPart(long datedCount) {
        return datedCount & ((1L << (63L-9)) - 1);
    }

    private static boolean dateChanged(long datedCount, LocalDate date) {
         return (int)(datedCount >>> (63L-9)) != date.getDayOfYear();
    }

    private AtomicLong value = new AtomicLong(countWithDate(1, LocalDate.now()));

    @Override
    public long getNext() {
        long datedCount = value.getAndIncrement();
        LocalDate today = LocalDate.now();
        if (dateChanged(dateCount, today)) {
            long next = countWithDate(1L, today);
            if (value.compareAndSet(datedCount+1, next)) {
                datedCount = next;
            } else {
                datedCount = getNext();
            }
        }
        return datedCount;
    }
}

При этом используется AtomicLong с указанием днягод упакован в счетчик.

  • Один тянет за следующий счетчик.
  • Если дата изменилась, то:
  • , когда можно установить 1 на следующий день, а затем датьit.
  • когда нет, кто-то раньше, возможно, с более ранним счетчиком взял 1, а затем нам нужно снова взять следующий.
1 голос
/ 02 октября 2019

По просьбе @JoopEggen моя версия его первого решения:

public class AtomicSequenceGenerator implements SequenceGenerator {

    private final Clock clock;
    private final Object lock = new Object();

    @GuardedBy("lock")
    private long value;
    @GuardedBy("lock")
    private LocalDate today;

    public AtomicSequenceGenerator(Clock clock) {
        this.clock = clock;
        synchronized (lock) {
            value = 1;
            today = LocalDate.now(clock);
        }
    }

    @Override
    public long getNext() {
        synchronized (lock) {
            LocalDate date = LocalDate.now(clock);
            if (!date.equals(today)) {
                today = date;
                value = 1;
            }
            return value++;
        }
    }
}

Основные различия:

  • При этом используется только частный монитор для защиты LocalDate и value.
  • value теперь имеет простую длину, поскольку он защищен замком, ему больше не нужно быть AtomicLong.
  • Я ввожуClock объект (для упрощения тестирования)
  • Нет двойной проверки блокировки. Возможно, двойная проверка блокировки может быть быстрее, но я не знаю, действительно ли она нужна, пока вы не проведете некоторое тестирование производительности.
0 голосов
/ 01 октября 2019

Вы можете создать одноэлементный экземпляр вашего генератора, который сбрасывает себя, как только будет установлена ​​новая дата.

Примерно так:

public class AtomicSequenceGenerator implements SequenceGenerator {

    // Private constructor in order to avoid the creation of an instance from outside the class
    private AtomicSequenceGenerator(){}

    private AtomicLong value = new AtomicLong(1);

    @Override
    public long getNext() {
        return value.getAndIncrement();
    }

// This is where the fun starts
// The T indicates some type that represents the file date
    private static T prev_file_date = null;
    private static AtomicSequenceGenerator instance = new AtomicSequenceGenerator();

    public static synchronized long getNext(T file_date)
    {
      if ((prev_file_date == null) || (!prev_file_date.equals(file_date)))
      {
        instance.value.set(1);
        prev_file_date = file_date;
      }
      return (instance.getNext());
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...