0

我们有一个 Spring Boot 服务,我们每天都会收到文件,由于某些问题(在生产者上),我们收到了多个附加了相同名称和日期的文件。新文件覆盖旧文件,为了处理它,我们希望在每个文件名处附加一个序列(从 1 开始)。但是序列应该在每天午夜自动重置为“1”。

任何人都可以建议一个 API 或一种重置序列的方法。

为了生成自动序列,我们使用 AtomicSequenceGenerator ,但我们无法实现简单的自动重置逻辑。

public class AtomicSequenceGenerator implements SequenceGenerator {

    private AtomicLong value = new AtomicLong(1);

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

3 回答 3

2

没有收到两次 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,然后给它。
  • 如果不是,则可能较早柜台的人拿走了 1,然后我们需要再拿下一个。
于 2019-10-01T15:16:36.440 回答
1

您可以创建一个生成器的单例实例,它会在新日期过去后立即重置。

像这样的东西:

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());
    }
}
于 2019-10-01T12:52:50.133 回答
1

根据@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++;
        }
    }
}

主要区别是:

  • 这仅使用私人监视器来保护LocalDatevalue.
  • value现在是一个普通的长,因为它有一把锁,它不再需要AtomicLong了。
  • 我注入一个Clock对象(为了更容易测试)
  • 没有双重检查锁定。可以说双重检查锁定可以更快,但我不知道它是否真的需要,直到您进行一些性能测试。
于 2019-10-02T09:41:40.487 回答