• Java 并发系列之十:java 并发框架(2个)


    1. Fork/Join框架

    2. Executor框架

    3. ThreadPoolExecutor

    4. ScheduledThreadPoolExecutor

    5. FutureTask

    6. txt

      1 java并发框架
      2     Fork/Join框架
      3         定义
      4             一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架
      5          核心思想
      6              分治
      7              fork分解任务,join收集任务
      8          工作窃取算法
      9             定义
     10                  工作窃取算法work-stealing: 某个线程从其他队列里窃取任务来执行
     11             背景
     12                 将一个不较大的任务分割为若干个互不依赖的子任务,为了减少线程之间的竞争,把这些子任务分别放到不同的队列里,并未每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应。
     13                  执行快的线程帮助执行慢的线程执行任务,提升整个任务效率
     14                 为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列
     15 窃取任务线程永远从双端队列的尾部拿任务执行
     16 被窃取任务线程永远从双端队列的头部拿任务执行
     17             优点
     18                 充分利用线程进行并行计算,减少了线程间的竞争
     19             缺点
     20                 在某些情况下还是存在竞争,比如双端队列里只有一个任务时,并且该算法会消耗了更多的资源,比如创建多个线程和多个双端队列
     21         Fork/Join框架设计
     22             2大步骤
     23                 分割任务
     24                     fork类把大任务分割成子任务,子任务继续分割,直到子任务足够小
     25                 执行任务并合并结果
     26                     分割的子任务放在双端队列里,然后几个启动线程分别从双端队列里获取任务执行。子任务执行完的结果都统一放在一个队列里,启动一个线程从队列里拿数据,然后合并这些数据。
     27         核心类
     28             ForkJoinTask
     29                 子类,用于继承
     30                     继承子类 RecursiveAction
     31                         用于没有返回结果的任务
     32                     继承子类 RecursiveTask
     33                         用于有返回结果的任务
     34                 方法
     35                     fork
     36                         分解任务
     37                     join
     38                         合并任务结果
     39                     isCompletedAbnormally()
     40                         检查任务是否已经抛出异常或已经被取消了
     41                     getException()
     42                         获取异常
     43              ForkJoinWorkerThread
     44                  执行任务的工作线程
     45             ForkJoinPool
     46                  执行任务ForkJoinTask的线程池,ForkJoinTask需要通过ForkJoinPool来执行
     47                     submit(task);
     48                 内部结构
     49                     ForkJoinTask数组
     50                         存放任务
     51                     ForkJoinWorkerThread数组
     52                         执行任务
     53     Executor框架
     54         两级调度模型
     55             上层:Java多线程程序通常把应用分解为若干个任务,然后使用用户级的调度器(Executor框架)将这些任务映射为固定数量的线程
     56             底层: 操作系统内核OSkernel将这些线程映射到硬件处理器CPU上。
     57             Executor框架控制上层的调度,下层的调度由操作系统内核控制,下层的调度不受应用程序的控制
     58         Executor框架的结构
     59             任务
     60                 包括被执行任务需要实现的接口
     61                     Runnable接口
     62                     Callable接口
     63             任务的执行
     64                 任务执行机制的核心接口
     65                     Executor接口
     66                 继承自Executor的接口
     67                     ExecutorService接口
     68                         execute(Runnable command)
     69                         submit(Runnable task)
     70                         submit(Callable<T>task)
     71                         返回值是FutureTask对象
     72                 实现了ExecutorService接口的实现类
     73                     ThreadPoolExecutor
     74                     ScheduledThreadPoolExecutor
     75             异步计算的结果
     76                 Future接口
     77                     get
     78                         等待任务执行完成
     79                     cancel
     80                         取消任务完成
     81                 实现了Future接口的实现类
     82                     FutureTask
     83         Executor框架的成员
     84             Executor是一个接口,是框架的基础,将任务的提交和任务的执行分离开来
     85             Runnable接口和Callable接口的实现类
     86                 任务
     87                     Runnable接口
     88                         不会返回结果
     89                     Callable接口
     90                         会返回结果
     91                         工厂类Executor可以把一个Runnable包装成一个Callable
     92                         callable(Runnable task)
     93                             返回值是null
     94                         callable(Runnable task, T result)
     95                             返回值是result对象
     96             ThreadPoolExecutor
     97                 是线程池的核心实现类,用来执行被提交的任务
     98             ScheduledThreadPoolExecutor
     99                 可以在给定的延迟后运行命令,或者定期执行命令
    100             Future接口和实现Future接口的FutureTask
    101                 异步计算的结果
    102     ThreadPoolExecutor
    103         是线程池的核心实现类,用来执行被提交的任务
    104         线程池的创建
    105             ThreadPoolExecutor
    106                 corePoolSize
    107                     线程池中核心线程的数量。当提交一个任务时,线程池会新建一个线程来执行任务,直到当前线程数等于corePoolSize。如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。
    108                 maximumPoolSize
    109                     线程池中允许的最大线程数。线程池的阻塞队列满了之后,如果还有任务提交,如果当前的线程数小于maximumPoolSize,则会新建线程来执行任务。注意,如果使用的是无界队列,该参数也就没有什么效果了。
    110                 keepAliveTime
    111                     线程空闲的时间。线程的创建和销毁是需要代价的。线程执行完任务后不会立即销毁,而是继续存活一段时间:keepAliveTime。默认情况下,该参数只有在线程数大于corePoolSize时才会生效。
    112                 unit
    113                     keepAliveTime的单位。TimeUnit
    114                 workQueue
    115                     用来保存等待执行的任务的阻塞队列,等待的任务必须实现Runnable接口。我们可以选择如下几种:
    116                     分类
    117                         ArrayBlockingQueue:基于数组结构的有界阻塞队列,FIFO。
    118                         LinkedBlockingQueue:基于链表结构的有界阻塞队列,FIFO。
    119                         SynchronousQueue:不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作,反之亦然。
    120                         PriorityBlockingQueue:具有优先界别的无界阻塞队列。
    121                 threadFactory
    122                     用于设置创建线程的工厂。可以通过线程工厂给每个创建出来的线程设置更有意义的名字,该对象可以通过Executors.defaultThreadFactory()
    123                 handler
    124                     RejectedExecutionHandler,线程池的拒绝策略。所谓拒绝策略,是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略。当向线程池中提交任务时,如果此时线程池中的线程已经饱和了,而且阻塞队列也已经满了,则线程池会选择一种拒绝策略来处理该任务。
    125                     四种拒绝策略
    126                         AbortPolicy:直接抛出异常,默认策略;
    127                         CallerRunsPolicy:用调用者所在的线程来执行任务;
    128                         DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
    129                         DiscardPolicy:直接丢弃任务;
    130                     当然我们也可以实现自己的拒绝策略,例如记录日志、持久化存储不能处理的任务等等,实现RejectedExecutionHandler接口自定义即可。
    131             工厂类Executors创建3种类型的ThreadPoolExecutor
    132                 SingleThreadExecutor
    133                     使用单个worker线程的Executor
    134                     应用场景
    135                         适用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的应用场景
    136                     特点
    137                         corePool和maximumPoolSize均被设置为1
    138                         使用的是相当于无界的有界阻塞队列LinkedBlockingQueue,所以带来的影响和FixedThreadPool一样。
    139                 FixedThreadPool
    140                     固定线程数的线程池
    141                     应用场景
    142                         为了满足资源管理的需求,需要限制当前线程数量的应用场景
    143                         适用于负载比较重的服务器
    144                     特点
    145                         corePoolSize 和 maximumPoolSize都设置为创建FixedThreadPool时指定的参数nThreads,意味着当线程池满时且阻塞队列也已经满时,如果继续提交任务,则会直接走拒绝策略
    146                         默认的拒绝策略,即AbortPolicy,则直接抛出异常。
    147                         keepAliveTime设置为0L,表示空闲的线程会立刻终止。
    148                         workQueue则是使用LinkedBlockingQueue,但是没有设置范围,那么则是最大值(Integer.MAX_VALUE),这基本就相当于一个无界队列了。
    149                         无界队列对线程池的影响
    150                             1. 当线程池中的线程数量等于corePoolSize 时,如果继续提交任务,该任务会被添加到无界阻塞队列workQueue中,因此线程中的线程数不会超过corePoolSize
    151                             2. 由于1,使用无界队列时的 maximumPoolSize是一个无效参数
    152                             3. 由于1和2,使用无界队列时的 keepAliveTime 是一个无效参数
    153                             4.  不会拒绝任务
    154                 CachedThreadPool
    155                     根据需要创建新线程,是大小无界的线程池
    156                     应用场景
    157                         适用于执行很多的短期异步任务的小程序
    158                         适用于负载较轻的服务器
    159                     特点
    160                         corePool为0,maximumPoolSize为Integer.MAX_VALUE,这就意味着所有的任务一提交就会加入到阻塞队列中。
    161                         keepAliveTime这是为60L,unit设置为TimeUnit.SECONDS,意味着空闲线程等待新任务的最长时间为60秒,空闲线程超过60秒后将会被终止。
    162                         阻塞队列采用的SynchronousQueue,每个插入操作都必须等待另一个线程对应的移除操作,此处把主线程提交的任务传递给空闲线程去执行。
    163                         SynchronousQueue是一个没有元素的阻塞队列,加上corePool = 0 ,maximumPoolSize = Integer.MAX_VALUE,这样就会存在一个问题,如果主线程提交任务的速度远远大于CachedThreadPool的处理速度,则CachedThreadPool会不断地创建新线程来执行任务,这样有可能会导致系统耗尽CPU和内存资源,所以在使用该线程池是,一定要注意控制并发的任务数,否则创建大量的线程可能导致严重的性能问题。
    164                     重要操作
    165                         offer
    166                             主线程执行offer操作与空闲线程执行的poll操作配对成功后,主线程把任务交给空闲线程执行
    167                         execute
    168                             执行任务
    169                         poll
    170                             让空闲线程在SynchronousQueue中等待60s,如果等待到新任务则执行,否则,空闲线程将终止
    171         向线程池提交任务
    172             execute()
    173                 用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功
    174                 工作原理
    175                     1. 如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(执行这一步骤需要获取全局锁)
    176                     2. 如果运行的线程等于或多于corePoolSize(完成预热之后),则将任务加入BlockingQueue,这一步不需要全局锁
    177                     3. 如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务 (执行这一步骤需要获取全局锁)
    178                     4. 如果创建的线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExcution()方法
    179             submit()
    180                 用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过它可以判定任务是否执行成功。
    181                 future.get()方法用来获取返回值,它会阻塞当前线程直到任务完成
    182                 future.get(long timeout, TimeUnit unit) 方法会阻塞当前线程一段时间后立即返回,这时候可能任务没有执行完
    183         关闭线程池
    184             原理
    185                 遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断地的任务可能永远无法终止
    186             方法
    187                 shutdown
    188                     只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程
    189                 shutdownNow
    190                     首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,任务不一定要执行完
    191         合理配置线程池
    192             任务的性质
    193                 CPU密集型任务
    194                     N(cpu)+1  个线程的线程池
    195                 IO密集型任务
    196                     2*N(cpu) 个线程的线程池
    197                 混合型任务
    198                     拆分成一个CPU密集型任务和一个IO密集型任务
    199             任务的优先级
    200 201 202 203                 用PriorityBlockingQueue
    204             任务的执行时间
    205 206 207 208             任务的依赖性
    209                 是否依赖其他系统资源,比如数据库连接
    210                 等待返回结果的时间越长,CPU空闲时间越长,那么线程数应该设置的越大,以便更好利用线程池
    211             建议使用有界队列。有界队列能够提高系统的稳定性和预警能力 ,无界队列会直接撑爆内存,导致系统不可用
    212             用PriorityBlockingQueue ,让执行时间短的任务先执行
    213         线程池的监控
    214             方便出问题时,可以根据线程池的使用情况,快速定位问题
    215             参数
    216                 taskCount
    217                     线程池需要执行的任务数量
    218                 completedTaskCount
    219                     线程池在运行过程中已经完成的任务数量
    220                 largestPoolSize
    221                     线程池曾将创建过的最大线程数量。如果该数字等于线程池的最大大小,表示线程池曾经满过
    222                 getPoolSize
    223                     线程池的线程数量
    224                 getActiveCount
    225                     获取活动的线程数
    226             重写方法
    227                 beforeExecute()
    228                     任务执行前
    229                 afterExecute()
    230                     任务执行后
    231                 terminated()
    232                     线程池关闭前
    233     ScheduledThreadPoolExecutor
    234         可以在给定的延迟后运行命令,或者定期执行命令,与Timer类似,但比其功能更强大,更灵活
    235         ScheduledThreadPoolExecutor  V.S. Timer
    236             ScheduledThreadPoolExecutor可以再构造函数中指定多个对应的后台进程数
    237             Timer对应的是单个后台进程
    238         内部类
    239             DelayedWorkQueue
    240                 所使用的阻塞队列变成了DelayedWorkQueue
    241                 DelayedWorkQueue为ScheduledThreadPoolExecutor中的内部类,它其实和阻塞队列DelayQueue有点儿类似
    242                 DelayedWorkQueue中的任务必然是按照延迟时间从短到长来进行排序的
    243                 Reentrant+Condition
    244             ScheduledFutureTask
    245                 待调度的任务
    246                 ScheduledFutureTask内部继承FutureTask,实现RunnableScheduledFuture接口
    247                 三个比较重要的变量
    248                             private final long sequenceNumber;
    249                         /** 任务被添加到ScheduledThreadPoolExecutor中的序号 */
    250                             private long time;
    251                                 /** 任务要执行的具体时间 */
    252                             private final long period;
    253                                 /**  任务的间隔周期 */
    254                 compareTo方法,提供一个排序算法,该算法规则是:首先按照time排序,time小的排在前面,大的排在后面,如果time相同,则使用sequenceNumber排序,小的排在前面,大的排在后面。
    255                 compareTo()方法使用于DelayedWorkQueue队列对其元素ScheduledThreadPoolExecutor task进行排序的算法
    256         创建
    257             通常使用工厂类Executors来创建
    258             2种类型
    259                 ScheduledThreadPoolExecutor
    260                     包含固定个数个线程
    261                     适用于资源管理而需要限制线程数的场景
    262                 SingleThreadScheduledExecutor
    263                     包含一个线程
    264                     适用于单个线程,需要保证顺序执行各个任务的场景
    265         4个调度器
    266             schedule(Callable callable, long delay, TimeUnit unit) :创建并执行在给定延迟后启用的 ScheduledFuture。
    267             schedule(Runnable command, long delay, TimeUnit unit) :创建并执行在给定延迟后启用的一次性操作。
    268             scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) :创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推。
    269 scheduleAtFixedRate是周期固定,也就说它是不会受到这个延迟的影响的,每个线程的调度周期在初始化时就已经绝对了,是什么时候调度就是什么时候调度,它不会因为上一个线程的调度失效延迟而受到影响。
    270             scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) :创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。
    271 scheduleWithFixedDelay是每个线程的调度间隔固定,也就是说第一个线程与第二线程之间间隔delay,第二个与第三个间隔delay,以此类推。
    272         调度和执行
    273             run()
    274                 1. 调用isPeriodic()获取该线程是否为周期性任务标志,然后调用canRunInCurrentRunState()方法判断该线程是否可以执行,如果不可以执行则调用cancel()取消任务。
    275                 2. 如果当线程已经到达了执行点,则调用run()方法执行task,该run()方法是在FutureTask中定义的。
    276                 3. 否则调用runAndReset()方法运行并重置状态,调用setNextRunTime()方法重新计算任务的下次执行时间,重新把任务添加到队列中,让该任务可以重复执行。
    277                 reExecutePeriodic重要的是调用super.getQueue().add(task);将任务task加入的队列DelayedWorkQueue中
    278     FutureTask
    279         实现了Future接口和Runnable接口,代表异步计算的结果,
    280         因为实现了Runnable接口,可以交给Executor执行,也可以由调用线程直接执行
    281         重要操作
    282             run()  3种状态
    283                 未启动
    284                 已启动
    285                 已完成
    286                     正常结束
    287                     取消而结束
    288                     异常而结束
    289             get()
    290                 3种状态
    291                     未启动
    292                         阻塞
    293                             底层是 LockSupport.park();
    294                     已启动
    295                         阻塞
    296                             底层是 LockSupport.park();
    297                     已完成
    298                         立即返回结果或者抛出异常
    299             cannel()
    300                 3种状态
    301                     未启动
    302                         任务不会被执行
    303                     已启动
    304                         cannel(true):中断执行任务的线程
    305                         cannel(false):不中断执行任务的线程
    306                     已完成
    307                         返回false
    308         应用场景
    309             当一个线程需要等待另一个线程把某个任务执行完后它才能继续执行
    View Code

    7. 参考网址

    1. 参考来源:http://cmsblogs.com/wp-content/resources/img/sike-juc.png
    2. 《Java并发编程的艺术》_方腾飞PDF 提取码:o9vr
    3. http://ifeve.com/the-art-of-java-concurrency-program-1/
    4. Java并发学习系列-绪论
    5. Java并发编程实战
    6. 死磕 Java 并发精品合集
  • 相关阅读:
    【bzoj5180】[Baltic2016]Cities 斯坦纳树
    【BZOJ1859】【ZJOI2006】碗的叠放
    【bzoj4589】Hard Nim FWT+快速幂
    【BZOJ1502】【NOI2005】月下柠檬树 simpson 积分
    【loj6437】 【PKUSC2018】 PKUSC 计算几何
    【PKUSC2018】【loj6433】最大前缀和 状压dp
    【pkuwc2018】 【loj2537】 Minmax DP+线段树合并
    多项式求逆元详解+模板 【洛谷P4238】多项式求逆
    【bzoj3684】 大朋友和多叉树 生成函数+多项式快速幂+拉格朗日反演
    【codeforces 623E】dp+FFT+快速幂
  • 原文地址:https://www.cnblogs.com/haimishasha/p/11199944.html
Copyright © 2020-2023  润新知