警告:我不做 Android 工作。我说的是标准 Java。
没有足够的细节来重现问题
runnables 和 executor 服务的基础是正确的。下面是您的代码的简化版本来说明这一点。
所以我怀疑你的问题出在其他地方。您没有显示代码的所有重要部分。特别缺少navEngine 的定义,这似乎是您投诉的根源。
package work.basil.example.threading;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class App
{
public static void main ( String[] args )
{
App app = new App();
app.demo();
}
private void demo ( )
{
System.out.println( "`demo` method ending. " + Instant.now() );
ExecutorService executorService = Executors.newFixedThreadPool( 2 );
// Navigation Manager work.
Runnable navManTask = new Runnable()
{
@Override
public void run ( )
{
System.out.println( "navManTask - beginning. Will sleep to simulate lengthy work. " + Instant.now() );
try
{
Thread.sleep( Duration.ofSeconds( 10 ).toMillis() );
}
catch ( InterruptedException e )
{
System.out.println( "navManTask - sleep interrupted. Ending this task prematurely. " + Instant.now() );
return;
}
System.out.println( "navManTask - ending. " + Instant.now() );
}
};
executorService.execute( navManTask );
// Vision Impaired work.
boolean isVisionImpaired = true;
if ( isVisionImpaired )
{
Runnable visionTask = new Runnable()
{
@Override
public void run ( )
{
System.out.println( "visionTask - beginning. Will sleep to simulate lengthy work. " + Instant.now() );
try
{
Thread.sleep( Duration.ofSeconds( 10 ).toMillis() );
}
catch ( InterruptedException e )
{
System.out.println( "visionTask - sleep interrupted. Ending this task prematurely. " + Instant.now() );
return;
}
System.out.println( "visionTask - ending. " + Instant.now() );
}
};
executorService.execute( visionTask );
System.out.println( "`demo` method asking executor service to shut down. " + Instant.now() );
executorService.shutdown();
try
{
System.out.println( "`main` method waiting for the executor service to shut down. " + Instant.now() );
executorService.awaitTermination( 30 , TimeUnit.SECONDS );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
System.out.println( "`demo` method ending. " + Instant.now() );
}
}
}
其他问题
您可能还有其他并发问题。
争用通过两个线程访问navEngine 对象
您有两个同时访问同一资源的线程,即变量navEngine。那里有两个问题:可见性和线程安全。
Java 内存模型下的可见性是指跨线程访问的原语或对象引用可能会显示两个不同的缓存值。对此的一种解决方案是volatile 关键字。另一种解决方案可能是Atomic… 类。搜索以了解有关可见性问题的更多信息。
我们看不到 navEngine 背后的类。但是,如果可以跨线程操作该类的对象,则必须使该类成为线程安全的。
第二个线程依赖于第一个
在一个线程的代码中,您正在实例化一个对象以放置在navEngine 变量中。在另一个线程的代码中,您假设对象存在。但是您无法预测线程将以什么顺序进行工作。线程的调度实际上是任意且不可预测的,具体取决于主机操作系统和JVM 的瞬时条件和 CPU 调度决策策略。第二个线程完全有可能尝试访问navEngine在第一个线程使用对象引用填充变量。
由于第二个 runnable 取决于第一个是否完整且成功,因此拥有两个单独的线程是没有意义的。您不妨将两者组合成一个 Runnable 任务。
如果您坚持使用这两个线程,那么您需要将您对execute 的调用更改为submit。 submit 方法返回一个 Future 对象。您可以使用这个Future 对象来检查第一个任务是否成功。
这里修改了上面的代码,以捕获在将 navMan 任务提交给执行器服务时返回的Future。视觉工作等待 Future 发出 navMan 工作完成的信号。
package work.basil.example.threading;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.*;
public class App
{
public static void main ( String[] args )
{
App app = new App();
app.demo();
}
private void demo ( )
{
System.out.println( "`demo` method ending. " + Instant.now() );
ExecutorService executorService = Executors.newFixedThreadPool( 2 );
// Navigation Manager work.
Runnable navManTask = new Runnable()
{
@Override
public void run ( )
{
System.out.println( "navManTask - beginning. Will sleep to simulate lengthy work. " + Instant.now() );
try
{
Thread.sleep( Duration.ofSeconds( 10 ).toMillis() );
}
catch ( InterruptedException e )
{
System.out.println( "navManTask - sleep interrupted. Ending this task prematurely. " + Instant.now() );
return;
}
System.out.println( "navManTask - ending. " + Instant.now() );
}
};
Future < String > navManFuture = executorService.submit( navManTask , "whatever" );
// Vision Impaired work.
boolean isVisionImpaired = true;
if ( isVisionImpaired )
{
Runnable visionTask = new Runnable()
{
@Override
public void run ( )
{
System.out.println( "visionTask - beginning. Will sleep to simulate lengthy work. " + Instant.now() );
if ( navManFuture.isCancelled() )
{
System.out.println( "visionTask - Found the navMan task got cancelled. Ending this task prematurely. " + Instant.now() );
return;
}
try
{
System.out.println( "visionTask - Waiting for the navMan task to be done. " + Instant.now() );
String whatever = navManFuture.get(); // Wait for `namManTask` to get done. This code blocks here, waiting.
Thread.sleep( Duration.ofSeconds( 10 ).toMillis() );
}
catch ( InterruptedException e )
{
System.out.println( "visionTask - sleep interrupted. Ending this task prematurely. " + Instant.now() );
return;
}
catch ( ExecutionException e )
{
e.printStackTrace();
}
System.out.println( "visionTask - ending. " + Instant.now() );
}
};
executorService.submit( visionTask );
System.out.println( "`demo` method asking executor service to shut down. " + Instant.now() );
executorService.shutdown();
try
{
System.out.println( "`main` method waiting for the executor service to shut down. " + Instant.now() );
executorService.awaitTermination( 30 , TimeUnit.SECONDS );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
System.out.println( "`demo` method ending. " + Instant.now() );
}
}
}
织机项目
顺便说一句,如果Project Loom 技术出现在Java 中,这里看到的代码会更简单。在 Loom 中,ExecutorService 接口也是AutoCloseable。这意味着我们可以使用方便的 try-with-resources 语法。 close 方法会阻塞,直到所有提交的任务都完成/失败/取消。