我们有一套行动或“工作”,我们希望一次发生一次(不同时发生)。即:当B发生时,作业A不会发生,并且您不能同时运行两个C作业。
如果线程尝试同时运行作业,则应该收到错误。我们不应该只是排队请求。
某些作业以异步方式发生,用户发出请求,我们返回状态消息和id,然后在服务器上异步处理作业。
我们正在寻找有关如何处理此方案的建议。
一个选项是lock on a shared object:
public class Global {
public static final Object lock = new Object();
}
public class JobA {
public void go() {
synchronized(Global.lock) {
//Do A stuff
}
}
}
public class JobB {
public void go() {
synchronized(Global.lock) {
//Do B stuff
}
}
}
问题:
JobA
想要将消息写入队列以进行异步处理,那么我们如何确保当JobA
中的另一种方法从队列中读取消息时该方法将能够在Global.lock
的另一个实例开始之前获取JobA
锁定?有关更好方法的任何建议吗?
答案 0 :(得分:8)
您是否考虑过使用SingleThreadExecutor?顾名思义,它使用单个线程来执行Callables(类似于Runnables - 请参阅doc),从而满足您对同步处理的要求。使用java.util.concurrent
类可以极大地简化线程代码。
要处理在另一个作业运行时提交作业并生成错误的情况,请设置有界队列和拒绝处理程序。有关this page的详细信息,请参阅ThreadPoolExecutor.setRejectedExecutionHandler()和第8.3.2 / 8.3.3节。
答案 1 :(得分:7)
听起来你需要的是一个独占锁,你可以在尝试获取锁时避免阻塞。
幸运的是,Lock接口有一个tryLock方法,因此您可以执行以下操作。
final Lock lock = new ReentrantLock();
....
void attemptJob( Runnable runnable )
{
if (!lock.tryLock())
throw new ConcurrentJobException();
try
{
runnable.run();
}
finally
{
lock.unlock();
}
}
如果你想运行异步,那么你应该将Runnable(或者你喜欢的Callable)委托给ExecutorService,当它完成时,释放锁。
编辑:例如(加上一些更多的最终声明)
final Lock lock = new ReentrantLock();
final ExecutorService service = Executors.newSingleThreadExecutor();
....
void attemptJob( final Runnable runnable )
{
if (!lock.tryLock())
throw new ConcurrentJobException();
service.execute( new Runnable()
{
public void run()
{
try
{
runnable.run();
}
finally
{
lock.unlock();
}
}
});
}
完成后,不要忘记关闭()ExecutorService。
答案 2 :(得分:0)
看看Spring Batch。我认为它可以帮助你。
答案 3 :(得分:0)
另一种可能性是使用配置的ThreadPoolExecutor。我相信这会起作用,但强烈建议读者自己测试一下。
Executor executor = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
以上是newSingleThreadExecutor在Executors类中的作用。
为了满足“并发任务错误”的要求,我认为您可以将队列实现更改为SynchronousQueue。
Executor executor = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new SynchronousQueue<Runnable>());
这应该允许一个任务运行,如果第一个任务在第一个任务完成之前提交,则会调用ThreadPoolExecutor RejectedExecutionHandler,默认情况下会抛出RejectedExecutionException。