使用Spring线程和TaskExecutor,我如何知道线程何时完成?

好吧,这里可能是一个天真的问题。 我有一项服务需要登录到多个网络设备,在每个设备上运行命令并收集结果。 为了提高速度,我需要同时访问它们并在完成后使用结果,而不是按顺序收集每个设备上的信息。

使用Spring框架和Jsch我可以很容易地正确查询每个设备。 我遇到一些困惑的地方是尝试重新连接bean以使用TaskExecutor来完成此任务。 我无法弄清楚如何知道如何知道线程何时完成。

到目前为止我所拥有的是:

public class RemoteCommand { private String user; private String host; private String password; private String command; private List commandResults; private TaskExecutor taskExecutor; public RemoteCommand(String user, String host, String password, TaskExecutor taskExecutor) { setUser(user); setHost(host); setPassword(password); setTaskExecutor(taskExecutor); } /** * @param user the user to set */ public void setUser(String user) { this.user = user; } /** * @return the user */ public String getUser() { return user; } /** * @param host the host to set */ public void setHost(String host) { this.host = host; } /** * @return the host */ public String getHost() { return host; } /** * @param password the password to set */ public void setPassword(String password) { this.password = password; } /** * @return the password */ public String getPassword() { return password; } /** * @param command the command to set */ private void setCommand(String command) { this.command = command; } /** * @return the command */ private String getCommand() { return command; } /** * @param commandResults the commandResults to set */ private void setCommandResults(List commandResults) { this.commandResults = commandResults; } /** * @return the commandResults */ public List getCommandResults(String command) { taskExecutor.execute(new CommandTask(command) ); return commandResults; } /** * @param taskExecutor the taskExecutor to set */ public void setTaskExecutor(TaskExecutor taskExecutor) { this.taskExecutor = taskExecutor; } /** * @return the taskExecutor */ public TaskExecutor getTaskExecutor() { return taskExecutor; } private class CommandTask implements Runnable { public CommandTask(String command) { setCommand(command); System.out.println("test: " + getCommand()); } /** * * @param command */ public void run() { List results = new LinkedList(); String command = getCommand(); try { System.out.println("running"); JSch jsch = new JSch(); String user = getUser(); String host = getHost(); java.util.Properties config = new java.util.Properties(); config.put("StrictHostKeyChecking", "no"); host = host.substring(host.indexOf('@') + 1); Session session = jsch.getSession(user, host, 22); session.setPassword(getPassword()); session.setConfig(config); session.connect(); Channel channel = session.openChannel("exec"); ((ChannelExec) channel).setCommand(command); channel.setInputStream(null); ((ChannelExec) channel).setErrStream(System.err); InputStream in = channel.getInputStream(); channel.connect(); byte[] tmp = new byte[1024]; while (true) { while (in.available() > 0) { int i = in.read(tmp, 0, 1024); if (i < 0) break; results.add(new String(tmp, 0, i)); System.out.print(new String(tmp, 0, i)); } if (channel.isClosed()) { //System.out.println("exit-status: " // + channel.getExitStatus()); break; } try { Thread.sleep(1000); } catch (Exception ee) { ee.printStackTrace(); } } channel.disconnect(); session.disconnect(); } catch (Exception e) { System.out.println(e); } setCommandResults(results); System.out.println("finished running"); } } } 

在我的junit测试中,我有:

 @Test public void testRemoteExecution() { remoteCommand = (RemoteCommand) applicationContext.getBean("remoteCommand"); remoteCommand.getCommandResults("scripts/something.pl xxx.xxx.xxx.xxx"); //List results = remoteCommand.getCommandResults("scripts/something.pl xxx.xxx.xxx.xxx"); //for (String line : results) { // System.out.println(line.trim()); //} } 

我的applicationContext.xml文件:

           Remote Command ${remote.user} ${remote.host} ${remote.password}   

我得到run()方法中的第一个println。 然后测试干净利落,没有错误。 我从来没有达到那个例程底部的第二个println。 我在这里看过这个post,这个非常有用,但没有以Spring特定的方式实现。 我确定我错过了一些简单的东西,或者在这里完全没有了。 任何帮助表示赞赏。

 public List getCommandResults(String command) { FutureTask task = new FutureTask(new CommandTask(command)) taskExecutor.execute(task); return task.get(); //or task.get(); return commandResults; - but it not a good practice } 

TaskExecutor接口是一个即发即TaskExecutor界面,供您在任务完成时不关心时使用。 这是Spring提供的最简单的异步抽象。

但是,有一个增强的接口AsyncTaskExecutor ,它提供了其他方法,包括返回Future submit()方法,让你等待结果。

Spring提供了ThreadPoolTaskExecutor类,它实现了TaskExecutorAsyncTaskExecutor

在您的特定情况下,我将重新实现Runnable作为Callable ,并从Callable.call()方法返回commandResults 。 然后可以将getCommandResults方法重新实现为:

 public List getCommandResults(String command) { Future> futureResults = taskExecutor.submit(new CommandTask(command)); return futureResults.get(); } 

此方法将异步提交任务,然后等待它完成,然后返回从Callable.call()方法返回的结果。 这也可以让你摆脱commandResults字段。