[英]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]
Thread类扩展了Runnable Interface。 因此,任何接受Runnable类型参数的方法都将接受Thread的实例(或实现Runnable接口的类的任何实例)。
Executor.java(接口)有一个方法execute(Runnable command)
Executors.newCachedThreadPool()
提供的实现使用自己独立的内部线程并启动相同的操作。 它使用在execute(Runnable command)
方法中传递的execute(Runnable command)
并执行run()
方法中的语句。
您已经提供了一个Thread实例(它实现了Runnable),因此内部线程(如第2点所述)调用run方法但从不在Thread上调用start()
方法。 execute()
方法接受您传递的实例,但它只知道它是Runnable类型,它不知道方法start()
存在 。 不知道它无法调用它。 任何其他程序员都可以传递一个类的实例,如class Task implements Runnable
并且可以使用invokeMeIAmImportantMethod()
等方法。 但是,Executor所知道的任何方法都只是并且只有runnable接口的合同中定义的public void run()
。
在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.