2016-04-15 53 views
7

什么是在Java应用程序中实现并发的正确方法?我知道线程和东西,当然,我已经为Java编程了10年,但并没有太多的并发经验。如何等待Java中完成多个任务?

例如,我必须异步加载一些资源,并且只有在加载完所有资源后才能继续并做更多工作。不用说,他们将如何完成任务。我该怎么做呢?

在JavaScript中,我喜欢用jQuery.deferred基础设施,说

$.when(deferred1,deferred2,deferred3...) 
.done(
    function(){//here everything is done 
    ... 
    }); 

但是我在Java中吗?

+0

什么版本的Java? –

+0

看看RxJava – tddmonkey

+0

在谷歌上查找Java线程。在Java中很容易实现并发。 –

回答

2

如果我不使用的并行数据流或Spring MVC的TaskExecutor中,我通常使用CountDownLatch。使用#个任务实例化,为每个完成其任务的线程减少一次。 CountDownLatch.await()等待,直到锁定为0.真的很有用。

在这里阅读更多:JavaDocs

+0

这和Semaphore有什么区别? –

+0

@ doom777 CountdownLatch用于启动一系列线程,然后等待所有线程完成(或直到他们调用countDown()给定次数)。 信号量用于控制使用资源的并发线程数。该资源可以像文件一样,或者可以通过限制执行的线程数量成为cpu。信号量的计数可以随着不同的线程调用acquire()和release()而升降。 –

+0

是的,但不是'CountdownLatch'只是一个不能上升的信号量? –

3

我会使用并行流。

Stream.of(runnable1, runnable2, runnable3).parallel().forEach(r -> r.run()); 
// do something after all these are done. 

如果您需要这是异步的,那么您可以使用池或线程。

我有异步加载一些资源,

你可以收集这些资源是这样的。

List<String> urls = .... 

Map<String, String> map = urls.parallelStream() 
           .collect(Collectors.toMap(u -> u, u -> download(u))); 

这会给你一个同时下载的所有资源的映射。并发将是您默认的CPU数量。

+0

@RobinJonsson forEach在所有任务完成之前不会返回。不支持异步运行流代码,这就是为什么你需要做其他事情,如果你需要这样做。 –

+1

真的!我混淆了思想“t”是一个线索。 “t”也许是一个可运行的... –

+0

@RobinJonsson谢谢你,我认为它现在更清晰了。 –

0

我通常选择异步通知开始,通知正在进行中,通知末端方法:

class Task extends Thread { 
    private ThreadLauncher parent; 

    public Task(ThreadLauncher parent) { 
     super(); 
     this.parent = parent; 
    } 

    public void run() { 
     doStuff(); 

     parent.notifyEnd(this); 
    } 

    public /*abstract*/ void doStuff() { 
     // ... 
    } 
} 


class ThreadLauncher { 

    public void stuff() { 
     for (int i=0; i<10; i++) 
      new Task(this).start(); 
    } 

    public void notifyEnd(Task who) { 
     // ... 
    } 
} 
+2

我建议不要直接扩展Thread。 –

+1

是的,你可以实现Runnable如果你想这样做 – Exceptyon

1

这是我使用线程的例子。它是一个具有50个线程的固定大小的静态执行器服务。

public class ThreadPoolExecutor { 

private static final ExecutorService executorService = Executors.newFixedThreadPool(50, 
     new ThreadFactoryBuilder().setNameFormat("thread-%d").build()); 

private static ThreadPoolExecutor instance = new ThreadPoolExecutor(); 

public static ThreadPoolExecutor getInstance() { 
    return instance; 
} 

public <T> Future<? extends T> queueJob(Callable<? extends T> task) { 
    return executorService.submit(task); 
} 

public void shutdown() { 
    executorService.shutdown(); 
} 
} 

的执行器业务逻辑这样使用:(你可以用赎回或Runnable的可赎回可返回的东西,可运行没有。)

public class MultipleExecutor implements Callable<ReturnType> {//your code} 

而且执行器的召唤:

ThreadPoolExecutor threadPoolExecutor = ThreadPoolExecutor.getInstance(); 

List<Future<? extends ReturnType>> results = new LinkedList<>(); 

for (Type Type : typeList) { 
      Future<? extends ReturnType> future = threadPoolExecutor.queueJob(
        new MultipleExecutor(needed parameters)); 
      results.add(future); 
     } 

     for (Future<? extends ReturnType> result : results) { 
      try { 
       if (result.get() != null) { 
        result.get(); // here you get the return of one thread 
       } 
      } catch (InterruptedException | ExecutionException e) { 
       logger.error(e, e); 
      } 
     } 
4

您可以通过多种方式实现它。

1。ExecutorServiceinvokeAll() API

执行给定的任务,返回保持状态期货和结果当所有完整的列表。

2. CountDownLatch

一个同步辅助,允许一个或多个线程等待,直到在其他线程正在执行的一组操作完成。

A CountDownLatch用给定的计数初始化。 await方法阻塞,直到当前计数由于调用countDown()方法而达到零,此后所有等待的线程被释放,并且任何后续的await调用立即返回。这是一次性现象 - 计数无法重置。如果您需要重置计数的版本,请考虑使用CyclicBarrier。

3. ExecutorsForkJoinPoolnewWorkStealingPool()是其他方式

看一看相关SE的问题:

How to wait for a thread that spawns it's own thread?

Executors: How to synchronously wait until all tasks have finished if tasks are created recursively?

+0

很好的答案。我还添加了CyclicBarrier - 它有一个构造函数,它接受一个Runnable,当所有任务完成时将执行:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent /CyclicBarrier.html#CyclicBarrier-int-java.lang.Runnable- –

+0

哪种方法可以做同样的事情,但不会阻塞任何线程? –

1

同样的行为与$.DeferredjQuery你可以在Java 8存档一个班级称为CompletableFuture。该课程提供了与Promises一起使用的API。为了创建异步代码,您可以使用,#supplyAsync等创建方法中的一个。然后用#thenApply应用一些计算结果。