我有一个任务,它将以不同的值运行多次。我想防止它同时执行2个相同的任务(基于字符串值)。下面是字符串的一个示例。这些值会改变,但为了简单起见,我在下面的示例中包含了这些值。我通过ExecutorService
提交这些任务,这些任务运行,但第二个hi阻止其他任务运行。所以4/5个任务并发运行。一旦第一个任务的锁被释放,第5个任务继续执行,其他任务继续执行。是否有一种方法可以防止这种类型的阻塞任务,以便其他3个任务可以在它之前运行,这样就不会排队,直到有5个任务并发运行。
任务提交:
executor.submit(new Task("hi"));
executor.submit(new Task("h"));
executor.submit(new Task("u"));
executor.submit(new Task("y"));
executor.submit(new Task("hi"));
executor.submit(new Task("p"));
executor.submit(new Task("o"));
executor.submit(new Task("bb"));
任务很简单。它只是打印出字符串:
Lock l = getLock(x);
try {
l.lock();
System.out.println(x);
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
Logger.getLogger(Task.class.getName()).log(Level.SEVERE, null, ex);
}
} finally {
l.unlock();
}
我已经更新了这篇文章,以便更清楚地理解……
为了避免阻塞线程,您必须确保操作不会在另一个操作之前运行。例如,您可以使用CompletableFuture
来链接操作,以便在前一个操作完成时进行调度:
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(2);
for(int i = 0; i < 5; i++) submit("one", task("one"), es);
for(int i = 0; i < 5; i++) submit("two", task("two"), es);
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(26));
es.shutdown();
}
static Runnable task(String x) {
return () -> {
System.out.println(x);
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
};
}
static final ConcurrentHashMap<String, CompletableFuture<Void>> MAP
= new ConcurrentHashMap<>();
static final void submit(String key, Runnable task, Executor e) {
CompletableFuture<Void> job = MAP.compute(key,
(k, previous) -> previous != null?
previous.thenRunAsync(task, e): CompletableFuture.runAsync(task, e));
job.whenComplete((v,t) -> MAP.remove(key, job));
}
ConcurrentHashMap
允许我们处理原子更新
如果一个键没有先前的future,只需调度动作,创建future
如果存在先前的未来操作,则链接该操作,在先前操作完成时进行调度;从属动作成为新的将来
如果作业完成,当且仅当它仍然是当前作业时,双参数
remove(key, job)
将删除它
main
方法中的示例演示了两个独立的操作如何在两个线程的线程池中运行,而不会在线程上阻塞。