通过ExecutorService执行线程时Thread.join()不起作用

时间:2018-08-14 12:11:13

标签: java multithreading 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()不起作用,它不等待线程完成。我需要通过 ExecutorService 执行我的 Thread ,还要等待该线程完成。但是我的代码效果不好。谁能帮我吗?

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

因为有时我需要打断我的 Thread 并等待该 Thread 完成。但是当我取消未来时,future.get()会获得异常,而不会等待线程完成。

如果我的句子语法不正确,我事先表示歉意。因为我的英语说得不好。

5 个答案:

答案 0 :(得分:14)

简单的答案:不要那样做。

请勿混合这样的抽象层。 Executor接口没有execute()线程。它需要 Runnables 。传递一个Thread对象没关系,除了调用run()方法之外,根本不会使用线程。

用抽象的Executor服务混合“低层”裸铁线程根本不是一个好主意。

线程池概念的全部要点是,您尝试控制底层线程。等待池中的线程 end 根本没有意义。线程池保留线程,因为建立线程是(相对)昂贵的操作。因此,他们并没有结束,而是继续生活,将来可以做其他工作。

真正的答案是:要么不使用该执行程序服务,要么寻找一个符合该概念的解决方案(无需费力地在一边做底层工作)。

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

答案 1 :(得分:6)

您的错误很微妙。

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

解决方案很简单:Executors.newCachedThreadPool()重新调整ExecutorService,使用其submit(Runnable)方法。您将获得一个Future<?>作为返回值。将来致电get()Thread.join()

具有相同的效果

有关如何使用ExecutorService的更详尽的解释,您可以在这里查看:http://tutorials.jenkov.com/java-util-concurrent/executorservice.html

答案 2 :(得分:3)

使用ExecutorService时,应将其传递给RunnableThreadRunnable,但是如果将其传递给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");
}

答案 3 :(得分:1)

提交的任务将由另一个线程作为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]

答案 4 :(得分:1)

  1. Thread类扩展了Runnable接口。因此,任何接受Runnable类型参数的方法都将接受Thread实例(或实现Runnable接口的类的任何实例)。

  2. Executor.java(接口)具有方法execute(Runnable command),由Executors.newCachedThreadPool()提供的实现使用其自己单独的内部线程并启动该内部线程。它使用execute(Runnable command)方法中传递的Runnable命令并执行run()方法中存在的语句。

  3. 您提供了一个Thread实例(实现Runnable),因此内部线程(如第2点所述)将调用run方法,但绝不会在Thread上调用start()方法。 execute()方法接受您传递的实例,但它仅知道它属于Runnable类型,不知道方法start()存在。不知道它不能调用它。任何其他程序员都可以传递诸如class Task implements Runnable之类的实例,并且可以具有诸如invokeMeIAmImportantMethod()之类的方法。但是Executor会知道的任何方法只能是而且只能是public void run(),如Runnable接口协定中所定义。

  4. join()之前在线程实例上调用的
  5. 方法start()不会使当前线程等待线程完成(您的线程从未启动)。

A)使用newCachedThreadPool()时,这意味着我们需要ThreadPool服务。这意味着线程池服务将维护线程并执行您的任务(或命令)。没有必要启动线程来执行任务并期望也可以启动单独的线程池。这种混合(有点混乱)的逻辑。 因此,要么删除Threadpool并仅启动线程,要么删除线程并仅依赖Threadpool。

B)另外,我建议您使用调试。您可能已经调试过,并且已经找到了线程的状态(这将是新的)。这将带您进行更多研究,最终您将看到ExecutorService execute()方法所需的参数。

相关问题