我有一个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);
}
谢谢!
答案 0 :(得分:0)
您可以使用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();
}
...
}
答案 1 :(得分:0)
我认为您可以使用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,因此需要适当地关闭该池。
答案 2 :(得分:0)
我建议您使用blocking queue,而不要使用某种显式锁。我看到的好处是,如果需要,您将不需要重复生成线程。您只需要产生一个仅处理所有 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 的必要信息。但是,如果您不需要共享任何信息,建议您使用信号量。在这种情况下,methodA会将票证放入信号灯中,而专用线程将尝试提取票证,直到收到一些票证为止一直阻塞。