Spring 控制器如何每次只服务 1 个请求,并在第一个请求完成之前丢弃使用相同方法收到的其他请求

Spring controller how to serve only 1 request each time, and discard other requests received for the same method until first request has finished

正如标题所描述的,我想实现以下目标

@Controller
public class ImportController {


    @RequestMapping(value = "/{File}", method = RequestMethod.GET)
    @LogAware
    public String import(@PathVariable(value = "File") String excel, Model model) {

        try {
            synchronized (this) {

            //code...

          }
       }

   }
}

我希望代码一次只针对 1 个请求执行。 synchronized 块内代码的执行可以持续大约 1 小时。与此同时,我希望取消到达该方法的彼此请求。有什么办法可以实现吗?

澄清一下:

因为现在第一个请求将被服务,当它完成时,下一个正在等待锁定的请求将被服务,然后是下一个正在等待的请求。

我想要的是在第一个请求完成后不允许其他已经在等待服务的请求。如果请求是在第一个请求执行期间发出的,我想 return 向用户发送错误请求或其他内容并取消他们的请求。

这是一种你可以考虑的方法。这在 AtomicBoolean 中使用全局状态,希望在您的用例中使用它是安全的(?)!

看到这个 When do I need to use AtomicBoolean in Java?

static AtomicBoolean atomicBoolean  = new AtomicBoolean(false);

    //controller definition
        if(atomicBoolean.compareAndSet(false, true)) {
            // your logic
            atomicBoolean.compareAndSet(true, false);
        }

    // rest of the controller logic

但是,请考虑将请求排队并将其作为后台任务处理的选项。在大多数情况下,不建议长时间保持套接字和 HTTP 打开。

方法一:

使用单证Semaphore

这是一个示例代码:

import java.util.concurrent.Semaphore;

public class Test {
    Semaphore s = new Semaphore(1); // Single permit.

    public void nonBlockingMethod() throws InterruptedException {
        // A thread tries to acquire a permit, returns immediately if cannot
        if (s.tryAcquire()) {
            // No. of permits = 0
            try {
                System.out.println(Thread.currentThread().getName() + " begins execution..");

                // long running task
                Thread.sleep(4000);

                System.out.println(Thread.currentThread().getName() + " exiting..");
            } finally {
                s.release(); // Release permit. No. of permits = 1
            }
        } else {
            System.out.println(Thread.currentThread().getName() + " cannot run as another thread is already running..");
        }
    }
}

方法二:

使用 ReentrantLock

示例代码:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test {
    Lock s = new ReentrantLock();

    public void nonBlockingMethod() throws InterruptedException {
        if (s.tryLock()) {
            try {
                System.out.println(Thread.currentThread().getName() + " begins execution..");

                // long running task
                Thread.sleep(4000);

                System.out.println(Thread.currentThread().getName() + " exiting..");
            } finally {
                s.unlock();
            }
        } else {
            System.out.println(Thread.currentThread().getName() + " cannot run as another thread is already running..");
        }
    }
}

Driver:

public static void main(String[] args) throws InterruptedException {
    Test t = new Test();

    Runnable r = () -> {
        try {
            t.nonBlockingMethod();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    };

    for (int i = 0; i < 3; i++) {
        new Thread(r, "Loop-1-Thread-" + i).start();
    }

    Thread.sleep(3999);

    // one of the threads in this iteration may get to run the task
    for (int i = 3; i < 8; i++) {
        new Thread(r, "Loop-2-Thread-" + i).start();
    }
}

(其中之一) 输出 (s):

Loop-1-Thread-2 cannot run as another thread is already running..
Loop-1-Thread-1 cannot run as another thread is already running..
Loop-1-Thread-0 begins execution..
Loop-2-Thread-3 cannot run as another thread is already running..
Loop-2-Thread-4 cannot run as another thread is already running..
Loop-2-Thread-5 cannot run as another thread is already running..
Loop-1-Thread-0 exiting..
Loop-2-Thread-6 begins execution..
Loop-2-Thread-7 cannot run as another thread is already running..
Loop-2-Thread-6 exiting..