ForkJoin & Quasar https://blog.csdn.net/GC_chao/article/details/78169583
new Thread的弊端如下:
a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比new Thread,Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。
详见测试代码.
常用的BlockingQueue有:
- ArrayBlockingQueue: 基于数组的阻塞队列实现,其对于生产者端和消费者端共用同一个锁对象,由此也意味着两者无法真正并行运行.
- LinkedBlockingQueue:基于链表的阻塞队列实现,其对于生产者端和消费者端分别采用了独立的锁来控制数据同步.
- SynchronousQueue:一种无缓冲的等待队列,类似于无中介的直接交易,有点像原始社会中的生产者和消费者,生产者拿着产品去集市销售给产品的最终消费者,而消费者必须亲自去集市找到所要商品的直接生产者,如果一方没有找到合适的目标,那么大家都在集市等待。
- DelayedWorkQueue:元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue是一个没有大小限制的队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
创建一个定长线程池,支持定时及周期性任务执行。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
###为什么使用ForkJoinPool
系列教程 http://ifeve.com/tag/forkjoinpool/
多线程 ForkJoinPool https://www.cnblogs.com/lixuwu/p/7979480.html
ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。
Java8中java.util.Arrays的parallelSort()方法和java.util.streams包中封装的方法也都用到了Fork/Join。
ForkJoinPool的execute方法接受ForkJoinTask和Runnable类型的参数,其中Runnable会被包装为RunnableExecuteAction类型;ForkJoinPool的submit方法接受ForkJoinTask、Runnable和Callable类型的参数,其中Runnable会被包装为RunnableExecuteAction类型、Callable会被包装为AdaptedCallable类型。
使用getRunningThreadCount(获取非等待、阻塞状态的线程数)、getActiveThreadCount(获取在执行任务或在窃取任务的线程数)方法获取的运行线程数通常比实际线程数大。
ForkJoinTask常用的子类包括RecursiveAction(无返回值)、RecursiveTask(有返回值)。
ForkJoinTask常用方法:
- fork():将当前任务提交到ForkJoinPool中,异步执行。如果池中有空闲的工作线程或池可以创建一个新的工作线程。这个方法会立即返回。
- join():等待任务的结束,并获得它们的结果。join()方法不能被中断,如果你中断调用join()方法的线程,这个方法将抛出InterruptedException异常。
- get():等待任务的结束,并获得它们的结果。这个版本的get()方法,如果ForkJoinTask已经结束它的执行,则返回compute()方法的返回值,否则,等待直到它完成。
- get(long timeout, TimeUnit unit):这个版本的get()方法,如果任务的结果不可用,则在指定的时间内等待它。如果超时并且任务的结果仍不可用,这个方法返回null值。TimeUnit类是一个枚举类,包含以下常量:DAYS,HOURS,MICROSECONDS, MILLISECONDS,MINUTES, NANOSECONDS 和 SECONDS。
- isDone():
public ForkJoinPool(int parallelism,
ForkJoinWorkerThreadFactory factory,
UncaughtExceptionHandler handler,
boolean asyncMode)
parallelism 并行度,默认设置为系统处理器数量与0x7fff之间的最小值,其实质是指定线程池的大小
factory 线程工厂类,用于创建新的线程,默认为defaultForkJoinWorkerThreadFactory
handler 用于处理任务线程抛出的异常的处理类,默认为null
asyncMode 如果为true,使用FIFO调度模式来管理已fork但没join的任务;如果为false,使用LIFO.默认为false
官方文档 http://docs.paralleluniverse.co/quasar/
Coroutine in Java - Quasar Fiber实现 https://blog.csdn.net/jek123456/article/details/80452737
次时代Java编程(一):Java里的协程 http://www.open-open.com/lib/view/open1468892872350.html
Java的纤程库 - Quasar https://blog.csdn.net/hj7jay/article/details/51980038
Fiber并不意味着它可以在所有的场景中都可以替换Thread。当fiber的代码经常会被等待其它fiber阻塞的时候,就应该使用fiber。
对于那些需要CPU长时间计算的代码,很少遇到阻塞的时候,就应该首选thread.
以上两条是选择fiber还是thread的判断条件,主要还是看任务是I/O blocking相关还是CPU相关。