英文:
Prevent thread blocking queued threads
问题
我有一个任务,会使用不同的值多次运行。我想防止它同时执行两个相同任务(基于字符串值)。下面是字符串的示例。这些值将会变化,但为了简单起见,我在示例中包含了这些值。我通过ExecutorService
提交这些任务。任务会运行,但第二个"hi"会阻止其他任务运行。所以有4/5个任务同时运行。一旦第一个"hi"释放锁定,第五个任务继续运行,其他任务也正常继续。有没有办法防止这种类型的任务阻塞,以便其他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();
}
我已更新帖子,以便更清楚地理解...
英文:
I have a task that will run many times with different values. I'd like to prevent it from executing 2 of the same tasks (Based on the string value) at the same time. Below is an example of the strings. These values will change, but for simplicity I have included these values below in the example. I submit these tasks via an ExecutorService
The tasks run, but the 2nd hi blocks the other tasks from running. So 4/5 tasks run concurrently. Once the lock is released from the first hi the 5th tasks continues and the other tasks continue fine. Is there a way to prevent this type of blocking of the task so that the other 3 tasks can run before it so there is no queuing until there is actually 5 tasks running concurrently.
Submission of the tasks:
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"));
The Task is simple. It just prints out the string:
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();
}
I've updated the post to allow for things to be more clearly understood...
答案1
得分: 3
为了避免阻塞线程,您必须确保在另一个线程之前甚至不运行该操作。例如,您可以使用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
允许我们处理以下情况作为原子更新:
-
如果关键字没有先前的未来存在,只需安排操作,创建未来
-
如果已经存在先前的未来,链式安排操作,以在前一个完成后调度;依赖操作将成为新的未来
-
如果作业已完成,则两个参数的
remove(key, job)
将仅在它仍然是当前作业时将其移除
main
方法中的示例演示了如何使用两个线程的线程池来运行两个独立的操作,永远不会阻塞线程。
英文:
To avoid blocking a thread, you have to ensure that the action doesn’t even run before the other. For example, you can use a CompletableFuture
to chain an action, to be scheduled when the previous has been completed:
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));
}
The ConcurrentHashMap
allows us to handle the cases as atomic updates
-
If no previous future exists for a key, just schedule the action, creating the future
-
If a previous future exists, chain the action, to be scheduled when the previous completed; the dependent action becomes the new future
-
If a job completed, the two-arg
remove(key, job)
will remove it if and only if it is still the current job
The example in the main
method demonstrates how two independent actions can run with a thread pool of two threads, never blocking at thread.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论