我们有一组行动或“工作”,我们希望一次发生一个(而不是同时发生)。即:作业 A 不能在 B 发生时发生,并且不能同时运行两个 C 作业。
如果线程尝试同时运行作业,它们应该会收到错误。我们不应该只是对请求进行排队。
有些作业在用户发出请求时异步发生,我们返回状态消息和 ID,然后在服务器上异步处理作业。
我们正在寻求有关如何处理这种情况的建议。
一种选择是锁定共享对象:
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
中的另一个方法从队列中读取消息时,该方法将能够在 JobA< 的另一个实例之前获取 Global.lock
锁/代码> 开始?
关于更好的方法有什么建议吗?
We have a set of actions or "Jobs" which we'd like to happen one at a time (not concurrently). Ie: Job A can't happen while B is happening, and you can't have two C jobs running at the same time.
In the event that a thread attempts to run a job concurrently they should get an error. We shouldn't just queue up the request.
Some jobs happen asynchronously where the user makes request, we return a status message and id, and then process job asynchronously on server.
We're looking for advice on how to handle this scenario.
One option is to 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
}
}
}
Problems with this:
- We would queue up concurrent requests and not return an error message (which we want to do)
- If
JobA
wants to write message to a queue to process asynchronously, how can we be assured that when the message is read from the queue by another method in JobA
that the method will be able to acquire the Global.lock
lock before another instance of JobA
starts?
Any suggestions on a better approach?
发布评论
评论(4)
您是否考虑过使用 单线程执行器 ?顾名思义,它使用单个线程来执行 Callables(类似于 Runnables - 请参阅文档),从而满足您对同步处理的要求。使用可用的 java.util.concurrent 类将极大地简化您的线程代码。
要处理在另一个作业正在运行时提交作业并生成错误的情况,请设置有界队列和拒绝处理程序。请参阅此页面和第 8.3.2/8.3.3 节特别是有关 ThreadPoolExecutor.setRejectedExecutionHandler()。
Have you considered using a SingleThreadExecutor ? As its name suggests, it uses a single thread to execute Callables (similar to Runnables - see the doc) and thus satisfies your requirement for synchronous processing. Using the
java.util.concurrent
classes available will simplify your threading code enormously.To handle the scenario when submitting a job when another job is running, and generating an error, set a bounded queue and a rejection handler. See this page and section 8.3.2/8.3.3 specifically for details on ThreadPoolExecutor.setRejectedExecutionHandler().
听起来您需要的是一个独占锁,您可以在尝试获取锁时避免阻塞。
幸运的是,Lock 接口有一个 tryLock 方法,因此您可以执行以下操作。
如果你想运行异步,那么你应该将 Runnable(或者 Callable,如果你愿意)委托给 ExecutorService,并在它完成时释放锁。
编辑:例如(加上一些最终声明)
完成后不要忘记 shutdown() ExecutorService。
Sounds like what you need is an exclusive lock where you can avoid blocking when trying to acquire the lock.
Fortunately the Lock interface has a tryLock method so you can do the following.
If you want to run async then you should delegate the Runnable (or Callable if you prefer) to an ExecutorService and, when it completes, release the lock.
EDIT: e.g. (plus some more final declarations)
Don't forget to shutdown() the ExecutorService when you are done with it.
看看 Spring Batch。我认为它可以帮助你。
Have a look at Spring Batch. I think it can help you.
另一种可能性是使用已配置的 ThreadPoolExecutor。我相信这会起作用,但强烈鼓励读者亲自测试。
上面的内容大约是 newSingleThreadExecutor 在 Executors 类中所做的事情。
为了满足“并发任务错误”的要求,我认为您可以将队列实现更改为 SynchronousQueue。
这应该允许一个任务运行,如果在第一个任务完成之前提交第二个任务,则将调用 ThreadPoolExecutor RejectedExecutionHandler,默认情况下会抛出 RejectedExecutionException。
Another possibility is to use a configured ThreadPoolExecutor. I believe this will work but strongly encourage readers to test it for themselves.
The above is approx what newSingleThreadExecutor does in the Executors class.
To satisfy the "error on concurrent task" requirement I think you can just change the queue implementation to a SynchronousQueue.
This should allow one task to run and if a second if submitted before the first finishes the ThreadPoolExecutor RejectedExecutionHandler will get called, which by default throws a RejectedExecutionException.