繁体   English   中英

Thread.join()在ExecutorService执行Thread时不起作用

[英]Thread.join() does not work when executing the Thread by ExecutorService

这是我的代码:

public static void main(String[] args) {
        System.out.println("program started");
        ExecutorService executor = Executors.newCachedThreadPool();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread finished");
            }
        });
        executor.execute(thread);
        try {
            thread.join();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("thread joined");

    }

当我如上所示启动我的Thread时thread.join()不起作用,它不会等待Thread完成。 我需要通过ExecutorService执行我的Thread ,并等待该Thread完成。 但是我的代码效果不好。 谁能帮我?

为什么我不使用Future而不是Thread

因为有时我需要中断我的线程并等待线程完成。 但是当我取消Future时future.get()会获得一个Exception,并且不会等待它的Thread完成。

如果我的句子的语法不正确,我会提前道歉。 因为我不会说英语。

简单回答:不要这样做。

不要混合像这样的抽象层。 Executor接口不execute()线程。 它需要Runnables 将Thread对象传递给它并不重要,除了调用run()方法之外,你的线程根本不会被使用。

混合“低层”裸铁线程与抽象的Executor服务简直是一个坏主意。

线程池概念的重点在于您不会尝试控制底层线程。 等待池化线程结束毫无意义。 线程池保持线程,因为建立线程是一个(相对)昂贵的操作。 所以他们不会结束,而是活下来,将来做其他工作。

这里真正的答案是:要么不使用那个执行器服务,要么寻找一个适用于该概念的解决方案(没有你进入并做一些低级别的东西)。

而“真实的”答案是:退后一步,并告诉我们您打算以这种方式解决的“真实”问题。

你的错误是微妙的。

注意Executor.execute()的API:它需要一个Runnable Thread实现了Runnable ,因此可以传递给Executor 但是, Executors.newCachedThreadPool()返回的实现仅使用您传递的Thread实例的run()方法。 Thread本身永远不会启动,因为执行程序使用它在线程池内部管理的线程。

解决方案很简单: Executors.newCachedThreadPool()返回ExecutorService ,改为使用其submit(Runnable)方法。 您将获得Future<?>作为返回值。 在未来调用get()将与Thread.join()具有相同的效果

有关如何使用ExecutorService的更详细说明,请ExecutorService此处: http//tutorials.jenkov.com/java-util-concurrent/executorservice.html

使用ExecutorService ,应该将其传递给Runnable Thread是一个Runnable但如果将它传递给ExecutorService则不会将它用作线程。

ExecutorService有另一个方法, submit ,允许你等待Runnable的完成(如果你想返回一个结果,可以等一个Callable

您可以将代码更改为:

public static void main(String[] args) {
    System.out.println("program started");
    ExecutorService executor = Executors.newCachedThreadPool();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("thread finished");
        }
    };
    Future<?> future = executor.submit(runnable);
    try {
        future.get();
    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println("waited for completion");
}

提交的任务将由另一个线程作为Runnable对象执行。 这就是为什么join不会阻止主线程。

您的代码相当于:

public static void main(String[] args) {
    System.out.println("program started");
    ExecutorService executor = Executors.newCachedThreadPool();

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("thread finished");
        }
    };

    executor.execute(runnable);

    try {
        Thread thread = new Thread(runnable);
        thread.join();  // the thread never start, so it will not block main thread
    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println("thread joined");
}

您可以尝试打印线程名称:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread()); // Thread[pool-1-thread-1,5,main]
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("thread finished");
    }
});
executor.execute(thread);

System.out.println(thread); // Thread[Thread-0,5,main]
  1. Thread类扩展了Runnable Interface。 因此,任何接受Runnable类型参数的方法都将接受Thread的实例(或实现Runnable接口的类的任何实例)。

  2. Executor.java(接口)有一个方法execute(Runnable command) Executors.newCachedThreadPool()提供的实现使用自己独立的内部线程并启动相同的操作。 它使用在execute(Runnable command)方法中传递的execute(Runnable command)并执行run()方法中的语句。

  3. 您已经提供了一个Thread实例(它实现了Runnable),因此内部线程(如第2点所述)调用run方法但从不在Thread上调用start()方法。 execute()方法接受您传递的实例,但它只知道它是Runnable类型,它不知道方法start()存在 不知道它无法调用它。 任何其他程序员都可以传递一个类的实例,如class Task implements Runnable并且可以使用invokeMeIAmImportantMethod()等方法。 但是,Executor所知道的任何方法都只是并且只有runnable接口的合同中定义的public void run()

  4. start()之前调用你的Instance of Thread调用的方法join()不会让当前的Thread等待你的Thread完成(你的Thread永远不会启动)。

A)当我们使用newCachedThreadPool()时,这意味着我们需要一个ThreadPool服务。 这意味着线程池服务将支持线程,并将执行您的任务(或命令)。 没有必要启动你的线程来完成一项任务,并期望启动一个部分的Threadpool。 它的混合(小混乱)逻辑。 所以要么删除Threadpool又要启动你的线程,否则删除你的线程并依赖于Threadpool。

B)我也建议你使用调试。 你可以调试并找到线程的状态(它本来是新的)。 这将导致您进行更多研究,最终您将查看ExecutorService execute()方法所期望的参数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM