Java 如果取消时正在运行runnable,如何取消ShceduledFuture并等待runnable停止?

Java 如果取消时正在运行runnable,如何取消ShceduledFuture并等待runnable停止?,java,java.util.concurrent,Java,Java.util.concurrent,当任何命令在任何ScheduledExecutorService上以固定速率调度时,它返回ScheduledFuture,也可以取消该命令。 但是“取消”不能保证命令在取消返回之后仍然没有执行,例如,因为命令在“Cunely'”被调用时已经在执行的中间。p> 对于大多数用例来说,这就足够了。但当需要在取消后阻塞当前线程时,若命令已经在执行中,我需要处理用例,并等待命令完成。换句话说,若命令仍在执行,那个么调用cancel的线程不应该前进。使用MayInterruptFrunning=true取消

当任何命令在任何ScheduledExecutorService上以固定速率调度时,它返回ScheduledFuture,也可以取消该命令。 但是“取消”不能保证命令在取消返回之后仍然没有执行,例如,因为命令在“Cunely'”被调用时已经在执行的中间。p> 对于大多数用例来说,这就足够了。但当需要在取消后阻塞当前线程时,若命令已经在执行中,我需要处理用例,并等待命令完成。换句话说,若命令仍在执行,那个么调用cancel的线程不应该前进。使用MayInterruptFrunning=true取消也不合适,因为我不想中断当前执行,我只需要等待正常完成

我没有找到如何通过标准JDK类来实现这一需求问题1:我错了吗?这种功能是否存在

所以我决定自己实施: 导入java.util.concurrent.*

public class GracefullyStoppingScheduledFutureDecorator implements ScheduledFuture {

/**
 * @return the scheduled future with method special implementation of "cancel" method, 
 * which in additional to standard implementation, 
 * provides strongly guarantee that command is not in the middle of progress when "cancel" returns  
 */
public static ScheduledFuture schedule(Runnable command, long initialDelay, long period, TimeUnit unit, ScheduledExecutorService scheduler) {
    CancellableCommand cancellableCommand = new CancellableCommand(command);
    ScheduledFuture future = scheduler.scheduleAtFixedRate(cancellableCommand, initialDelay, period, unit);
    return new GracefullyStoppingScheduledFutureDecorator(future, cancellableCommand);
}

private GracefullyStoppingScheduledFutureDecorator(ScheduledFuture targetFuture, CancellableCommand command) {
    this.targetFuture = targetFuture;
    this.runnable = command;
}

private final ScheduledFuture targetFuture;
private final CancellableCommand runnable;

@Override
public boolean cancel(boolean mayInterruptIfRunning) {
    runnable.cancel();
    return targetFuture.cancel(mayInterruptIfRunning);
}

@Override
public long getDelay(TimeUnit unit) {
    return targetFuture.getDelay(unit);
}

@Override
public int compareTo(Delayed o) {
    return targetFuture.compareTo(o);
}

@Override
public boolean isCancelled() {
    return targetFuture.isCancelled();
}

@Override
public boolean isDone() {
    return targetFuture.isDone();
}

@Override
public Object get() throws InterruptedException, ExecutionException {
    return targetFuture.get();
}

@Override
public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
    return targetFuture.get(timeout, unit);
}

private static class CancellableCommand implements Runnable {

    private final Object monitor = new Object();
    private final Runnable target;
    private boolean cancelled = false;

    private CancellableCommand(Runnable target) {
        this.target = target;
    }

        public void cancel() {
            synchronized (monitor) {
                cancelled = true;
            }
        }

        @Override
        public void run() {
            synchronized (monitor) {
                if (!cancelled) {
                    target.run();
                }
            }
        }

    }

}
问题2:有人能在上面的代码中找到错误吗

问题2:有人能在上面的代码中找到错误吗

假设存在死锁,可通过以下场景再现:

  • 具有线程T1,该线程保存监视器M1
  • 计划任务正在线程T2上执行(保持其监视器M2),并且想要进入M1,所以T2需要等待T1退出监视器M1
  • T1决定取消任务,但因为它的监视器M2被任务本身锁定,所以出现了死锁
  • 最有可能的情况是abovr不真实,但为了避免所有可能的情况,我决定以无锁方式重写代码:

    public class GracefullyStoppingScheduledFuture {
    
    /**
     * @return the scheduled future with method special implementation of "cancel" method,
     * which in additional to standard implementation,
     * provides strongly guarantee that command is not in the middle of progress when "cancel" returns
     */
    public static GracefullyStoppingScheduledFuture cheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit, ScheduledExecutorService scheduler) {
        CancellableCommand cancellableCommand = new CancellableCommand(command);
        ScheduledFuture future = scheduler.scheduleAtFixedRate(cancellableCommand, initialDelay, period, unit);
        return new GracefullyStoppingScheduledFuture(future, cancellableCommand);
    }
    
    private GracefullyStoppingScheduledFuture(ScheduledFuture targetFuture, CancellableCommand command) {
        this.targetFuture = targetFuture;
        this.runnable = command;
    }
    
    private final ScheduledFuture targetFuture;
    private final CancellableCommand runnable;
    
    public void cancelAndBeSureOfTermination(boolean mayInterruptIfRunning) throws InterruptedException, ExecutionException {
        try {
            targetFuture.cancel(mayInterruptIfRunning);
        } finally {
            runnable.cancel();
        }
    }
    
    private static class CancellableCommand implements Runnable {
    
        private static final int NOT_EXECUTING = 0;
        private static final int IN_PROGRESS = 1;
        private static final int CANCELLED_WITHOUT_OBSTRUCTION = 2;
        private static final int CANCELLED_IN_MIDDLE_OF_PROGRESS = 3;
    
        private final AtomicInteger state = new AtomicInteger(NOT_EXECUTING);
        private final AtomicReference<Thread> executionThread = new AtomicReference<>();
        private final CompletableFuture<Void> cancellationFuture = new CompletableFuture<>();
        private final Runnable target;
    
        private CancellableCommand(Runnable target) {
            this.target = target;
        }
    
        public void cancel() throws ExecutionException, InterruptedException {
            if (executionThread.get() == Thread.currentThread()) {
                // cancel method was called from target by itself
                state.set(CANCELLED_IN_MIDDLE_OF_PROGRESS);
                return;
            }
            while (true) {
                if (state.get() == CANCELLED_WITHOUT_OBSTRUCTION) {
                    return;
                }
                if (state.get() == CANCELLED_IN_MIDDLE_OF_PROGRESS) {
                    cancellationFuture.get();
                    return;
                }
                if (state.compareAndSet(NOT_EXECUTING, CANCELLED_WITHOUT_OBSTRUCTION)) {
                    return;
                }
                if (state.compareAndSet(IN_PROGRESS, CANCELLED_IN_MIDDLE_OF_PROGRESS)) {
                    cancellationFuture.get();
                    return;
                }
            }
        }
    
        @Override
        public void run() {
            if (!state.compareAndSet(NOT_EXECUTING, IN_PROGRESS)) {
                notifyWaiters();
                return;
            }
    
            try {
                executionThread.set(Thread.currentThread());
                target.run();
            } finally {
                executionThread.set(null);
                if (!state.compareAndSet(IN_PROGRESS, NOT_EXECUTING)) {
                    notifyWaiters();
                }
            }
        }
    
        private void notifyWaiters() {
            if (state.get() == CANCELLED_WITHOUT_OBSTRUCTION) {
                // no need to notify anything
                return;
            }
            // someone waits for cancelling
            cancellationFuture.complete(null);
            return;
        }
    
    }
    
    公共类GracefullyStoppingsScheduledFuture{
    /**
    *@使用“取消”方法的特殊实现返回计划的未来方法,
    *除了标准实施之外,
    *提供强有力的保证,命令在“取消”返回时不在进行中。
    */
    公共静态GracefullyStoppingsScheduledFuture ScheduleAtfixedRate(可运行命令、长初始延迟、长周期、时间单位、ScheduledExecutorService调度器){
    CancelableCommand CancelableCommand=新的CancelableCommand(命令);
    ScheduledFuture future=scheduler.scheduleAtFixedRate(CancelableCommand,initialDelay,period,unit);
    返回新的GracefullyStoppingsScheduledFuture(future,CancelableCommand);
    }
    private GracefullyStoppingsScheduledFuture(ScheduledFuture targetFuture,CancelableCommand){
    this.targetFuture=targetFuture;
    this.runnable=命令;
    }
    私人最终计划未来目标未来;
    private final cancelablecommand runnable;
    public void CancelandBesureof Termination(布尔值MayInterruptFrunning)引发InterruptedException、ExecutionException{
    试一试{
    targetFuture.cancel(可能中断frunning);
    }最后{
    runnable.cancel();
    }
    }
    私有静态类CancelableCommand实现Runnable{
    私有静态final int NOT_EXECUTING=0;
    私有静态final int IN_PROGRESS=1;
    私人静态最终int取消,无障碍=2;
    进程中间的私有静态最终int取消=3;
    私有最终AtomicInteger状态=新的AtomicInteger(未执行);
    private final AtomicReference executionThread=新的AtomicReference();
    private final CompletableFuture cancellationFuture=新的CompletableFuture();
    私人最终可运行目标;
    私有可取消命令(可运行目标){
    this.target=目标;
    }
    public void cancel()引发ExecutionException、InterruptedException{
    if(executionThread.get()==Thread.currentThread()){
    //cancel方法本身已从目标调用
    state.set(在进程的中间被取消);
    返回;
    }
    while(true){
    if(state.get()==取消,无障碍){
    返回;
    }
    if(state.get()==在进度的中间被取消){
    cancellationFuture.get();
    返回;
    }
    if(state.compareAndSet(未执行,取消,无障碍)){
    返回;
    }
    if(state.compareAndSet(进行中,取消进行中){
    cancellationFuture.get();
    返回;
    }
    }
    }
    @凌驾
    公开募捐{
    if(!state.compareAndSet(未执行,正在进行)){
    通知服务员();
    返回;
    }
    试一试{
    executionThread.set(Thread.currentThread());
    target.run();
    }最后{
    executionThread.set(null);
    如果(!state.compareAndSet(正在进行,而不是正在执行)){
    通知服务员();
    }
    }
    }
    私人服务员(){
    if(state.get()==取消,无障碍){
    //不需要通知任何事情
    返回;
    }
    //有人在等取消
    cancellationFuture.complete(空);
    返回;
    }
    }