2014-01-13 187 views
0

我有一个简单的应用程序,我在一个类中创建3个线程来ping 3个不同的网站,并注意这样做的时间。并行执行和终止多线程

我希望通过查看3个执行中的哪个线程成功执行并终止其他两个线程来增强它。

JDK的哪一类会对此有所帮助?如何 ?

示例代码平网站:

public static boolean pingUrl(final String address) { 

try { 

    final URL url = new URL("http://" + address); 

    final HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); 
    urlConn.setConnectTimeout(1000 * 10); // mTimeout is in seconds 

    final long startTime = System.currentTimeMillis(); 

    urlConn.connect(); 

    final long endTime = System.currentTimeMillis(); 

    if (urlConn.getResponseCode() == HttpURLConnection.HTTP_OK) { 

    System.out.println("Time (ms) : " + (endTime - startTime)); 
    System.out.println("Ping to "+address +" was success"); 

    return true; 
    } 
} catch (final MalformedURLException e1) { 
    e1.printStackTrace(); 
} catch (final IOException e) { 
    e.printStackTrace(); 
} 
return false; 
} 
+0

你是如何ping网站的? – vandale

+0

@vandale添加了示例代码 –

+0

为了正确地终止线程,你必须中止'urlConn.connect()',我认为这是不可能的 – vandale

回答

3

我希望通过查看3个执行中的哪个线程先成功执行并终止另外两个执行程序。

我会使用ExecutorService结合ExecutorCompletionService。然后,当完成第一项任务时从完成服务中返回第一个Future时,您可以在ExecutorService上拨打shutdownNow()

javadocs for ExecutorCompletionService很不错,并展示了如何使用它。

// maybe you want 10 threads working on your tasks 
ExecutorService threadPool = Executors.newFixedThreadPool(10); 
CompletionService<Result> ecs 
    = new ExecutorCompletionService<Result>(threadPool); 
for (Callable<Result> task : tasks) { 
    // submit your tasks to the completion service, they run in the thread-pool 
    ecs.submit(task); 
} 
// once you get one result 
Future<Result> future = ecs.take(); 
// kill the rest of the tasks 
threadPool.shutdownNow(); 
Result result = future.get(); 
// probably will need to close the thread connections, see below 
// maybe call threadPool.awaitShutdown(...) here to wait for the others to die 

这个机制的唯一问题是,这只会中断线程。在你的情况下,他们将被卡在urlConn.connect();这是不可中断的。一旦ecs.take()返回,你将不得不跑回你的任务,并在仍在进行的HttpURLConnection上致电disconnect()。即使那样我也不确定它是否会阻止目前​​正在进行的连接。如果这不起作用,那么您可能需要切换到使用Apache HttpClient或其他可以关闭的类来阻止线程等待更长时间。

for (Callable<Result> task : tasks) { 
    // you'll need to do something like this 
    task.closeConnection(); 
} 

在你的情况,你的任务可能看起来像:

public class MyPingTask implements Callable<Boolean> { 
    private String address; 
    public MyPingTask(String address) { 
     this.address = address; 
    } 
    public Boolean call() throws Exception { 
     // obviously the pingUrl code could go right here 
     return pingUrl(address); 
    } 
} 

这里是Java tutorial on ExecutorService和相关类。

+0

好的答案!解决问题的可能解决方法:使用带有重写方法'newTaskFor(Callable)'的自定义'ThreadPoolExecutor'来代替'newFixedThreadPool(...)'。它返回一个自定义的'RunnableFuture',它'知道''Callable'拥有一个'HttpURLConnection'(或'HttpClient'请求对象)并关闭它的连接/中止它的'cancel'方法。 – isnot2bad

+0

不要这样做@ isnot2bad,但它听起来像它会工作。当然不适合初级线程程序员。 – Gray

1

我想BlockingQueue可能是有用的。这产生的线程的主要思路完成后,一些值写入BlockingQueue和优雅逢InterruptedException

例如:

public void runPing(List<String> urls) { 
    Collection<Thread> runningThreads = new ArrayList<>(urls.size()); 
    final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(urls.size()); 
    for (int i = 0; i < 3; i++) { 
     final String url = urls.get(i); 
     Thread t = new Thread(new Runnable() { 
      public void run() { 
       pingUrl(url); 
       queue.add(1); 
      } 
     }); 
     runningThreads.add(t); 
    } 
    try { 
     queue.poll(1, TimeUnit.HOURS); 
     interruptChilds(runningThreads); 
    } catch (Exception e) { 
     interruptChilds(runningThreads); 
    } 
} 

private void interruptChilds(Collection<Thread> runningThreads) { 
    for (Thread t : runningThreads) { 
     t.interrupt(); 
    } 
} 

请注意,在没有处理的InterruptedException。它应该被添加在你的方法中