这是我的代码:
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()
会获得异常,而不会等待线程完成。
如果我的句子语法不正确,我事先表示歉意。因为我的英语说得不好。
答案 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
时,应将其传递给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");
}
答案 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)
Thread类扩展了Runnable接口。因此,任何接受Runnable类型参数的方法都将接受Thread实例(或实现Runnable接口的类的任何实例)。
Executor.java(接口)具有方法execute(Runnable command)
,由Executors.newCachedThreadPool()
提供的实现使用其自己单独的内部线程并启动该内部线程。它使用execute(Runnable command)
方法中传递的Runnable命令并执行run()
方法中存在的语句。
您提供了一个Thread实例(实现Runnable),因此内部线程(如第2点所述)将调用run方法,但绝不会在Thread上调用start()
方法。 execute()
方法接受您传递的实例,但它仅知道它属于Runnable类型,不知道方法start()
存在。不知道它不能调用它。任何其他程序员都可以传递诸如class Task implements Runnable
之类的实例,并且可以具有诸如invokeMeIAmImportantMethod()
之类的方法。但是Executor会知道的任何方法只能是而且只能是public void run()
,如Runnable接口协定中所定义。
join()
之前在线程实例上调用的方法start()
不会使当前线程等待线程完成(您的线程从未启动)。
A)使用newCachedThreadPool()时,这意味着我们需要ThreadPool服务。这意味着线程池服务将维护线程并执行您的任务(或命令)。没有必要启动线程来执行任务并期望也可以启动单独的线程池。这种混合(有点混乱)的逻辑。 因此,要么删除Threadpool并仅启动线程,要么删除线程并仅依赖Threadpool。
B)另外,我建议您使用调试。您可能已经调试过,并且已经找到了线程的状态(这将是新的)。这将带您进行更多研究,最终您将看到ExecutorService execute()方法所需的参数。