如何在多线程环境中设置值时删除双重检查

How to remove double checking when setting a value in a multithreaded environment

我有一个 class Checker 存储字段 archiveDate。有几个线程可以设置这个字段。他们仅在该字段尚不可用时才记录该字段。如果其中一个线程记录了日期,则其他线程不必覆盖它。还有另一个线程(Checker 本身)可以删除日期。它仅在设置日期后删除日期。现在我的代码看起来像这样。

public class Checker extends Thread {
    private volatile LocalDateTime archiveDate;
    private ReentrantLock lock;

    public Checker() {
        lock = new ReentrantLock();
    }

    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            if (archiveDate != null) {
                lock.lock();
                try {
                    if (archiveDate != null && archiveDate.isBefore(LocalDateTime.now())) {
                        //do something
                        archiveDate = null;
                    }
                } finally {
                    lock.unlock();
                }
            }
            try {
                TimeUnit.SECONDS.sleep(30);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }

    public void changeDate(LocalDateTime date) {
        if (archiveDate == null) {
            lock.lock();
            if (archiveDate == null) {
                try {
                    archiveDate = date;
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

我可以避免双重检查或以某种方式简化代码吗?

在我看来,您使用的工具不正确。要在特定日期时间执行任务,请使用 ScheduledExecutorService:

的适配器
static final ScheduledExecutorService ES = Executors.newSingleThreadScheduledExecutor();

public static ScheduledFuture<?> schedule(LocalDateTime targetTime, Runnable r) {
    return ES.schedule(r,
        LocalDateTime.now().until(targetTime, ChronoUnit.NANOS), TimeUnit.NANOSECONDS);
}

对我来说,不管指定的日期如何,在有待处理的请求时忽略新请求看起来有点奇怪,但是可以使用上面的 schedule 方法实现与您的问题代码类似的逻辑:

class Checker implements Runnable {
    final AtomicReference<LocalDateTime> archiveDate = new AtomicReference<>();

    @Override
    public void run() {
        //do something
    
        archiveDate.set(null);
    }

    public void changeDate(LocalDateTime date) {
        if(archiveDate.compareAndSet(null, date)) {
            schedule(date, this);
        }
    }
}