猿问

使用多个线程仅调用一次方法

我有一个同时处理大量请求的 Web 应用程序。在应用程序的一个 API 方法中,我有一个方法 - methodA()。在这个方法中,我调用了另一个方法 - doSomething()。我有一个场景,我希望对 methodA() 的第一次调用将在单独的线程中运行 doSomething() 方法,但此时,如果已调用对 methodA() 的另一个调用,则不要运行 doSomething( ) 方法(因为它仍在由另一个线程运行)并继续 methodA() 的其余部分。


methodA() {

 .

 .

 doSomething() // In a new thread

 .

 .

}

我考虑过使用原子布尔值作为标志,但我不确定这是否是最好的主意。


    private final AtomicBoolean isOn = new AtomicBoolean(false);

    methodA() {

        .

        .

        if (isOn.compareAndSet(false, true)) {

                     Runnable doSomethingRunnableTask = () -> { 

                         doSomething(); };

                     Thread t1 = new Thread(doSomethingRunnableTask);

                     t1.start();

                     isOn.set(false);

        } 

谢谢!


HUWWW
浏览 184回答 3
3回答

UYOU

您可以使用ReentrantLock。锁一次只允许一个线程,它的tryLock()方法将根据是否获得锁立即返回 true 或 false。ReentrantLock lock = new ReentrantLock();methodA() {    ...    if (lock.tryLock()) {        try {            doSomething();        } finally {            lock.unlock();        }    }    ...}如果您想doSomething()在另一个线程中执行,并且不想阻塞任何调用线程,您可以使用与您最初想到的类似的东西。AtomicBoolean flag = new AtomicBoolean();methodA() {    ...    if (flag.compareAndSet(false, true)) {        // execute in another thread / executor        new Thread(() -> {            try {                doSomething();            } finally {                // unlock within the executing thread                // calling thread can continue immediately                flag.set(false);            }        }).start();    }    ...}

慕容3067478

我认为您可以使用 ReentrantLock 及其tryLock方法。从文档ReentrantLock::tryLock仅当调用时锁未被另一个线程持有时才获取锁。如果当前线程已经持有此锁,则持有计数递增 1,并且该方法返回 true。如果锁被另一个线程持有,则此方法将立即返回值 false。所以你可以在你的服务中创建这样的锁作为一个字段,这样调用你的线程methodA将共享它然后:public class MyService {&nbsp; &nbsp; private ReentrantLock reentrantLock = new ReentrantLock();&nbsp; &nbsp; public void methodA() {&nbsp; &nbsp; &nbsp; &nbsp; if(reentrantLock.tryLock()) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; doSomething();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reentrantLock.unlock();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}编辑:这里的锁将通过调用 Thread 来持有,这个线程将等待提交的任务完成然后解锁锁:public class MyService {&nbsp; &nbsp; private ReentrantLock reentrantLock = new ReentrantLock();&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; private ExecutorService pool = Executors.newCachedThreadPool();&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; public void methodA() {&nbsp; &nbsp; &nbsp; &nbsp; if(reentrantLock.tryLock()) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Future<?> submit = pool.submit(() -> doSomething()); // you can submit your invalidateCacheRunnableTask runnable here.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; submit.get();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } catch (InterruptedException | ExecutionException e) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; e.printStackTrace();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } finally {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reentrantLock.unlock();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}还要记住,我在这个例子中使用了 threadPool,所以这个池需要适当地关闭

不负相思意

我建议您使用阻塞队列,而不是使用某种显式锁。我看到的优点是,如果/当需要时,您不需要重复生成线程。您只需要生成一个线程一次,该线程将只处理所有doSomething。设想:调用methodA时,它会将专用线程的必要信息放入 BlockingQueue 并继续运行。专用线程将从BlockingQueue 中轮询信息(在空队列上阻塞)。当队列中收到一些信息时,它会运行您的doSomething方法。BlockingQueue<Info> queue;methodA() {&nbsp; &nbsp; //...&nbsp; &nbsp; queue.add(info);&nbsp; &nbsp; // non-blocking, keeps going&nbsp; &nbsp;&nbsp;}void dedicatedThread(){&nbsp; &nbsp; for(;;) {&nbsp; &nbsp; &nbsp; &nbsp; //Blocks until some work is put in the queue&nbsp; &nbsp; &nbsp; &nbsp; Info info = queue.poll();&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; doSomething(info);&nbsp; &nbsp; }}注意:我假设类型Info包含方法doSomething的必要信息。但是,如果您不需要共享任何信息,我建议您改用信号量。在这种情况下,方法 A 会将票放入信号量中,专用线程将尝试绘制票,阻塞直到收到一些票。
随时随地看视频慕课网APP

相关分类

Java
我要回答