【问题标题】:Executor Shutdown await termination not working执行程序关闭等待终止不起作用
【发布时间】:2021-05-18 11:09:18
【问题描述】:

我正在调用 await 终止,因为我需要在调用意图之前完成前两个 Runnable。但是它不起作用,意图被调用,当我调用 navEngine.getRoutes 时,navEngine 为空。 Executor 服务初始化如下:

public static ExecutorService buildExecutor = Executors.newFixedThreadPool(2);
Runnable runnable = new Runnable() {
                        @Override
                        public void run() {
                            navEngine = new NavigationManager(start, destination, selectedBuilding);
                        }
                    };
                    buildExecutor.execute(runnable);
                    Boolean isVisionImpaired = defaultPreferences.getString(kSightSetting, "Vision Impaired").equalsIgnoreCase("Vision Impaired");
                    if (isVisionImpaired) {
                        Runnable dRunnable = new Runnable() {
                            @Override
                            public void run() {
                                String[] dxfs = mapManager.generateDs( new String[] { selectedBuilding.getBuildingID() });
                                navEngine.setD_for_these_buildings(dxfs);
                            }
                        };
                        buildExecutor.execute(dRunnable);
                    }

                }
                buildExecutor.shutdown();
                try {
                    buildExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Intent intent = new Intent(ChooseDestinationActivity.this, DefineRouteActivity.class);

任何建议都会很棒。

【问题讨论】:

  • 为什么不在一个 Runnable 中做这两件事?
  • @AndyTurner 谢谢,这两个任务并不快,我需要确保它们都完成,然后才能调用意图,任何关于如何确保这会有所帮助的建议
  • @AndyTurner 所以只是为了检查一下,我希望按顺序或并行运行这两个,但是 NavEngine 构造函数会进一步调用其他方法等。而且都需要时间,所以我需要确保两个完整的任务都已完全完成,然后我才能调用意图。即使这意味着必须引入微调器来让用户知道
  • 您可以根据需要并行运行navEnginedxfs 的评估;只有当你拥有两者时,你才能同时使用两者。 (顺便说一句,您是否有意在评估后实际上不使用dxfs?)
  • 谢谢@AndyTurner,下面的错字行应该设置 Dxfs 而不是 ds。那么这样做的方式,它应该在理论上有效吗?那么我应该如何在我调用转移到下一个活动的意图之前完成这两个任务。了解在下一个活动中我将使用这些流程创建的数据,因此我必须确保它们是完整的 感谢您的帮助

标签: java android multithreading executorservice


【解决方案1】:

我已经看了一些可能的解决方案,它们是:

  1. 增加等待终止时间

  2. 将 try/catch 替换为

    while (!buildExecutor.isTerminated()); {
        //do nothing
    }
    Intent(...)
    
  3. 创建可调用列表添加在此处使用 Executors.callable 添加您的可运行对象,然后调用 invokeAll()

    List<Callable<Object>> calls = new ArrayList<Callable<Object>>();
    calls.add(Executors.callable(your runnable here));
    buildExecutor.invokeAll(calls);
    buildExecutor.shutdown();
    Intent(...)
    

附:我不是本地人并发,但我已经尝试了我提供的所有解决方案,我也会感谢所有建议)

【讨论】:

    【解决方案2】:

    警告:我不做 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 的调用更改为submitsubmit 方法返回一个 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 方法会阻塞,直到所有提交的任务都完成/失败/取消。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-21
      • 1970-01-01
      相关资源
      最近更新 更多