Java 如何通过多个线程执行一系列操作?
我正在探索一个问题,这可能是一个问题类的特例,但我不知道问题类,也不知道合适的术语,所以我不得不求助于使用特别词汇来描述问题。一旦我知道了正确的术语,我会重新措辞 我有一群单身汉Java 如何通过多个线程执行一系列操作?,java,multithreading,swing,Java,Multithreading,Swing,我正在探索一个问题,这可能是一个问题类的特例,但我不知道问题类,也不知道合适的术语,所以我不得不求助于使用特别词汇来描述问题。一旦我知道了正确的术语,我会重新措辞 我有一群单身汉a,B,C。单身人士包括: 无关的。没有像“必须先访问B,然后才能使用C执行X”或类似的约束 不是线程安全的 系统尽可能接受并行处理的任务。 每个任务由一系列操作组成,每个操作都将使用其中一个单例执行。不同的任务可能以不同的顺序访问不同的单例,并且任务可能包含操作循环 伪代码: void myTask(in1,in2
a
,B
,C
。单身人士包括:
- 无关的。没有像“必须先访问B,然后才能使用C执行X”或类似的约束
- 不是线程安全的
每个任务由一系列操作组成,每个操作都将使用其中一个单例执行。不同的任务可能以不同的顺序访问不同的单例,并且任务可能包含操作循环 伪代码:
void myTask(in1,in2,…){
多维莎(()->{
//使用in1,in2。。。
//检查和/或更新文件
//设置要用作下一个操作输入的输出:
outA1=。。。
outA2=。。。
...
});
doWithB(()->{
//使用outA1、outA2。。。
//检查和/或更新B
//设置要用作下一个操作输入的输出:
outB1=。。。
outB2=。。。
...
});
//任务可以以任何顺序重复接触单例
多维莎(()->{
//outB1,outB2,…,检查/修改A,设置输出
outAx1=。。。
outAx2=。。。
...
});
//任务可能有循环:
while(conditionInC(()->…){
doWithC(()->…);
doWithD(()->…);
}
//我知道这样的循环会导致活锁。
//这是另一个问题的一个方面,在另一天。
}
有多个任务,如上面的myTask
。要执行的任务被包装在一个闭包中,并被调度到
ThreadPoolExecutor
(或类似的东西)
我考虑的方法:
LockA
,LockB
,…每个
doWithX
仅仅是一个synchronized(X)
块。OutXn
是myTask
的局部变量问题:其中一个是Swing,我无法将EDT移动到我管理的线程中
doWithSwing(){…}
AsSwingUtilities.invokeAndWait(()->{…}
)编码,从方法(1)解决Swing问题
问题:invokeAndWait
通常被认为容易出现死锁。我如何发现我是否在上述模式中遇到了此类问题threadA
,threadB
,…,每个线程“拥有”一个单例(Swing已经有了这个,它是EDT)。doWithX
在threadX
上将块调度为Runnable
outXn
设置为Future-outXn=new-SettableFuture()
,分配变成outXn.set(…)
问题:我在JDK中找不到类似于
SettableFuture
的东西;我能找到的所有创建Future
的方法都与ThreadPool
有某种关联。也许我看到的顶级界面不正确,而Future
是一种误导是否有一个我没有考虑过的优越方法? 我不知道问题类,也不知道合适的术语 我可能只是将问题类称为并发任务编排 在确定正确的方法时有很多事情要考虑。如果你提供更多的细节,我会尝试用更多的颜色来更新我的答案。< /P> 没有像“必须先访问B,然后才能使用C执行X”或类似的约束 这通常是一件好事。死锁的一个非常常见的原因是不同的线程以不同的顺序获取相同的锁。例如,线程1先锁定a,然后锁定B,而线程2拥有锁B并等待获取a。设计解决方案使这种情况不会发生是非常重要的 我在JDK中找不到类似于
SettableFuture
的东西
看看
java.util.concurrent.CompletableFuture
——这可能就是您在这里想要的。它公开了一个阻塞get()
,以及一些异步完成回调,例如thenacpt(ConsumerQuote:“解决Swing问题”我想你没有提到什么是挥杆问题。我也不清楚什么是“动作顺序”您想同步。@akuzminykh完成-感谢您的反馈,希望现在更清楚。如果还有什么不清楚的地方,请随时询问更多信息!线程争用在这里不是问题。如果它不是线程安全的,您无论如何都必须序列化。我忘了提到它是GUI应用程序,所以多线程是为了减少延迟,不是为了增加吞吐量(这里有新的见解:这是桌面和服务器应用程序之间软件设计的核心区别)。在任务中,操作必须按顺序进行。否则,这是meh…我们不太关心,所有活动都是从GUI或从提供新数据的服务器启动的(它从不干扰用户输入,我想这是一个非常幸运的情况)。如果多个任务几乎同时提交,则可能存在争用条件,其中操作交叉并访问相同的单例。不过,在逻辑级别上没有争用条件,所有数据要么完全在用户控制下,要么完全在服务器控制下,所以这应该不是问题。@toolforger I提供了一个示例根据您的反馈,我们将介绍一些如何使用CompletableFuture
实现这一点的构建块。我认为您可以更进一步,在任务定义周围添加语法糖,但它可能需要类似注释处理的东西
class TaskHelper
{
private final Object lockA;
private final Object lockB;
private final Object lockC;
private final Executor poolExecutor;
private final Executor swingExecutor;
public TaskHelper()
{
poolExecutor = Executors.newFixedThreadPool( 2 );
swingExecutor = SwingUtilities::invokeLater;
lockA = new Object();
lockB = new Object();
lockC = new Object();
}
public <T> CompletableFuture<T> doWithA( Supplier<T> taskStep )
{
return doWith( lockA, poolExecutor, taskStep );
}
public <T> CompletableFuture<T> doWithB( Supplier<T> taskStep )
{
return doWith( lockB, poolExecutor, taskStep );
}
public <T> CompletableFuture<T> doWithC( Supplier<T> taskStep )
{
return doWith( lockC, swingExecutor, taskStep );
}
private <T> CompletableFuture<T> doWith( Object lock, Executor executor, Supplier<T> taskStep )
{
CompletableFuture<T> future = new CompletableFuture<>();
Runnable serialTaskStep = () -> {
T result;
synchronized ( lock ) {
result = taskStep.get();
}
future.complete( result );
};
executor.execute( serialTaskStep );
return future;
}
}
class SampleTask
{
private final TaskHelper helper;
private final String id;
private final int startingValue;
public SampleTask( TaskHelper helper, String id, int startingValue )
{
this.helper = helper;
this.id = id;
this.startingValue = startingValue;
}
private void start()
{
helper.doWithB( () -> {
int square = startingValue * startingValue;
return String.format( "computed-thread: %s computed-square: %d",
Thread.currentThread().getName(), square );
} )
.thenAccept( this::step2 );
}
private void step2( String result )
{
helper.doWithC( () -> {
String message = String.format( "current-thread: %s task: %s result: %s",
Thread.currentThread().getName(), id, result );
JOptionPane.showConfirmDialog( null, message );
return null;
} );
}
}
@Test
public void testConcurrent() throws InterruptedException, ExecutionException
{
TaskHelper helper = new TaskHelper();
new SampleTask( helper, "task1", 5 ).start();
new SampleTask( helper, "task2", 7 ).start();
Thread.sleep( 60000 );
}
@Test
public void testConcurrentRx() throws InterruptedException
{
Scheduler swingScheduler = Schedulers.from( SwingUtilities::invokeLater );
Subject<Integer> inputSubject = PublishSubject.create();
inputSubject
.flatMap( input -> Observable.just( input )
.subscribeOn( Schedulers.computation() )
.map( this::computeSquare ))
.observeOn( swingScheduler )
.subscribe( this::displayResult );
inputSubject.onNext( 5 );
inputSubject.onNext( 7 );
Thread.sleep( 60000 );
}
private String computeSquare( int input )
{
int square = input * input;
return String.format( "computed-thread: %s computed-square: %d",
Thread.currentThread().getName(), square );
}
private void displayResult( String result )
{
String message = String.format( "current-thread: %s result: %s",
Thread.currentThread().getName(), result );
JOptionPane.showConfirmDialog( null, message );
}