【问题标题】:Spring controller how to serve only 1 request each time, and discard other requests received for the same method until first request has finishedSpring控制器如何每次只服务一个请求,并丢弃同一方法收到的其他请求,直到第一个请求完成
【发布时间】:2021-08-16 17:59:05
【问题描述】:

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

@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 个请求执行。同步块内的代码执行可持续约 1 小时。同时,我希望取消到达该方法的每个其他请求。有什么方法可以实现吗?

澄清一下:

现在第一个请求将被处理,当它完成时,等待锁的下一个请求将被处理,然后是下一个正在等待的请求。

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

【问题讨论】:

  • 您可以尝试用可重入锁替换同步块,并在该类中使用 tryLock() 方法。如果任何其他线程持有锁,则此方法将返回 false,您可以完成请求。
  • *我想要的是不允许在第一个请求完成后处理其他请求。 * 您的意思是该方法预计只在应用程序的生命周期内执行一次?
  • @Adarsh 我想要的是不允许在第一个请求完成后已经等待服务的其他请求
  • 从某种意义上说,在执行其他请求期间发生的任何事情都必须取消。

标签: java spring multithreading synchronization


【解决方案1】:

方法一:

使用单一许可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..");
        }
    }
}

方法2:

使用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..");
        }
    }
}

司机:

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..

【讨论】:

    【解决方案2】:

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

    看到这个 SO 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 处于打开状态。

    【讨论】:

    • 这不是线程安全的,您没有正确使用 AtomicBoolean。您链接到的问题在其答案中确实正确使用了 AtomicBoolean。
    • 谢谢@ErwinBolwidt
    猜你喜欢
    • 2020-11-24
    • 1970-01-01
    • 2021-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-31
    相关资源
    最近更新 更多