为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

HUWWW 2023-02-16 15:46:00
我有一个同时处理大量请求的 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);        } 谢谢!
查看完整描述

3 回答

?
UYOU

TA贡献1878条经验 获得超4个赞

您可以使用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();

    }

    ...

}


查看完整回答
反对 回复 2023-02-16
?
慕容3067478

TA贡献1773条经验 获得超3个赞

我认为您可以使用 ReentrantLock 及其tryLock方法。从文档ReentrantLock::tryLock

仅当调用时锁未被另一个线程持有时才获取锁。

如果当前线程已经持有此锁,则持有计数递增 1,并且该方法返回 true。

如果锁被另一个线程持有,则此方法将立即返回值 false。

所以你可以在你的服务中创建这样的锁作为一个字段,这样调用你的线程methodA将共享它然后:

public class MyService {

    private ReentrantLock reentrantLock = new ReentrantLock();


    public void methodA() {

        if(reentrantLock.tryLock()) {

            doSomething();

            reentrantLock.unlock();

        }

    }

}

编辑:这里的锁将通过调用 Thread 来持有,这个线程将等待提交的任务完成然后解锁锁:


public class MyService {

    private ReentrantLock reentrantLock = new ReentrantLock();

    

    private ExecutorService pool = Executors.newCachedThreadPool();

    

    public void methodA() {

        if(reentrantLock.tryLock()) {

            Future<?> submit = pool.submit(() -> doSomething()); // you can submit your invalidateCacheRunnableTask runnable here.

            try {

                submit.get();

            } catch (InterruptedException | ExecutionException e) {

                e.printStackTrace();

            } finally {

                reentrantLock.unlock();

            }

        }

    }

}

还要记住,我在这个例子中使用了 threadPool,所以这个池需要适当地关闭


查看完整回答
反对 回复 2023-02-16
?
不负相思意

TA贡献1777条经验 获得超10个赞

我建议您使用阻塞队列,而不是使用某种显式锁。我看到的优点是,如果/当需要时,您不需要重复生成线程。您只需要生成一个线程一次,该线程将只处理所有doSomething。


设想:


调用methodA时,它会将专用线程的必要信息放入 BlockingQueue 并继续运行。专用线程将从BlockingQueue 中轮询信息(在空队列上阻塞)。当队列中收到一些信息时,它会运行您的doSomething方法。


BlockingQueue<Info> queue;


methodA() {

    //...

    queue.add(info);

    // non-blocking, keeps going    

}



void dedicatedThread(){

    for(;;) {

        //Blocks until some work is put in the queue

        Info info = queue.poll(); 

        doSomething(info);

    }


}

注意:我假设类型Info包含方法doSomething的必要信息。但是,如果您不需要共享任何信息,我建议您改用信号量。在这种情况下,方法 A 会将票放入信号量中,专用线程将尝试绘制票,阻塞直到收到一些票。


查看完整回答
反对 回复 2023-02-16
  • 3 回答
  • 0 关注
  • 183 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号