扩展和增强线程池

线程池扩展——回调:

public class EXTThreadPool {
    //核心线程数
    private static final int THREADPOOLCORESIZE=5;
    //最大线程数
    private static final int THREADPOOLMAXSIZE=5;

    public static void main(String[] args) {

        ExecutorService exec=new ThreadPoolExecutor(THREADPOOLCORESIZE,THREADPOOLMAXSIZE,0L, TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>()){
            //线程执行前调用
            @Override
            protected void beforeExecute(Thread t, Runnable r) {
                System.out.println(((ExtThread)r).getThreadName()+"准备执行任务");
            }
            //线程执行完成后调用
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                System.out.println(((ExtThread)r).getThreadName()+"完成任务");
            }
            //线程池关闭时调用
            @Override
            protected void terminated() {
                System.out.println("线程池关闭");

            }
        };

        for (int i = 0; i < 5; i++) {

            ExtThread td=new ExtThread();

            td.setThreadName("Thread-"+i);

            exec.execute(td);

        }

        exec.shutdown();

    }

}

class ExtThread implements Runnable{

    private String ThreadName;

    public String getThreadName() {
        return ThreadName;
    }

    public void setThreadName(String threadName) {
        ThreadName = threadName;
    }

    @Override
    public void run() {

        System.out.println(ThreadName+"正在执行任务");

    }
}
执行结果
Thread-1准备执行任务
Thread-3准备执行任务
Thread-2准备执行任务
Thread-0准备执行任务
Thread-0正在执行任务
Thread-2正在执行任务
Thread-3正在执行任务
Thread-3完成任务
Thread-1正在执行任务
Thread-1完成任务
Thread-4准备执行任务
Thread-2完成任务
Thread-0完成任务
Thread-4正在执行任务
Thread-4完成任务
线程池关闭

拒绝策略

  1. 线程池四种拒绝策略:
    • AbortPolicy(线程池默认使用的拒绝策略)
      • 该策略在当任务访问超出最大值时,则会抛出异常并打印当前任务信息。
      public static class AbortPolicy implements RejectedExecutionHandler {
      
          public AbortPolicy() { }
      
          public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
              throw new RejectedExecutionException("Task " + r.toString() +
                                                   " rejected from " +
                                                   e.toString());
          }
      }
      
    • CallerRunsPolicy
      • 通知当前线程执行该任务不进行终止。
      public static class CallerRunsPolicy implements RejectedExecutionHandler {
      
          public CallerRunsPolicy() { }
      
          public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
              if (!e.isShutdown()) {
                  r.run();
              }
          }
      }
      
    • DiscardPolicy
      • 直接丢弃该任务不做任何操作
      public static class DiscardPolicy implements RejectedExecutionHandler {
      
          public DiscardPolicy() { }
          
          public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
          }
      }
      
    • DiscardOldestPolicy
      • 丢弃队列中的第一个任务,之后对该任务进行重新提交
      public static class DiscardOldestPolicy implements RejectedExecutionHandler {
      
          public DiscardOldestPolicy() { }
      
          public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
              if (!e.isShutdown()) {
                  e.getQueue().poll();
                  e.execute(r);
              }
          }
      }
      
    1. 自定义拒绝策略
    • 如果以上的拒绝策略都不满足的话可以通过自定义拒绝策略。首先需要实现RejectedExecutionHandler接口,之后rejectedExecution方法中添加对应的业务实现即可。
    1. 触发拒绝策略的条件:
      public void execute(Runnable command) {
          if (command == null)
              throw new NullPointerException();
          int c = ctl.get();
          //判断如果当前正在运行的线程数小于核心线程数则启动一个新线程
          if (workerCountOf(c) < corePoolSize) {
              if (addWorker(command, true))
                  return;
              c = ctl.get();
          }
          //如果当前运行的线程数大于或等于核心线程数时则判断线程是否在运行并尝试将任务加入队列中
          if (isRunning(c) && workQueue.offer(command)) {
              int recheck = ctl.get();
              //再次检查线城市是否在运行,如果没有在尝试回退任务如果成功则拒绝该任务
              if (! isRunning(recheck) && remove(command))
                  reject(command);
              //如果没有停止则启动一个新的线程  
              else if (workerCountOf(recheck) == 0)
                  addWorker(null, false);
          }
          //最后当启动的线程数量等于最大线程数时则直接拒绝该任务
          else if (!addWorker(command, false))
              reject(command);
      }
      

ForkJoinPool(可任务拆分的线程池)

  1. ForkJoinPool通过一个任务分成若干个小任务,最后在将小任务的结果进行汇总,得到大任务的结果。通过fork方法分配子任务,在通过join方法获取到任务的结果。
    在这里插入图片描述
  2. ForkJoinPool的应用:
    • 代码说明:
      public class ForJoinTest {
      
          private  static List<Integer> list=new ArrayList<Integer>();
      
          public static void main(String[] args)throws Exception {
      
              for (int i=0;i<1000;i++){
      
                  list.add(new Random().nextInt(10));
      
              }
              //创建ForkJoin线程池。
              ForkJoinPool exec=new ForkJoinPool(10);
               /**
               * JDK1.8后可以通过以下静态方式获取ForkJoinPool,在使用System.setProperty设置并行度、线程工厂和异常处理类等,
               * 其使用的是同步的方式进行,也就是可以通过join获取结果
               * */
              /*ForkJoinPool exec = ForkJoinPool.commonPool();
      
              System.setProperty("parallelism","10");*/
      
              SumThread sumThread=new SumThread(list);
      
              ForkJoinTask<Long> result = exec.submit(sumThread);
      
              System.out.println("最后的结果是:"+result.get());
      
      
          }
      
      }
      
      class SumThread extends RecursiveTask<Long> {
      
          private static final int THRESHOLD=50;
      
          private List<Integer> list;
      
          private int start;
      
          private int end;
      
          public SumThread(List<Integer> list) {
              this.list = list;
              this.start=0;
              this.end=list.size();
          }
      
          public SumThread(List<Integer> list, int start, int end) {
              this.list = list;
              this.start = start;
              this.end = end;
          }
      
          protected Long compute() {
      
              long sum=0;
      
              if((end-start)<THRESHOLD){
      
                  for (int i=start;i<end;i++){
      
                      sum+=list.get(i);
      
                  }
      
                  System.out.println("子任务"+Thread.currentThread().getName()+"执行完成");
      
              }else{
      
                  ArrayList<SumThread> sumThreads=new ArrayList<SumThread>();
                  //根据list的大小拆分任务数量
                  int taskNum=end/(THRESHOLD-1)+(end%(THRESHOLD-1)>0?1:0);
      
                  int endPos=THRESHOLD-1;
      
                  int startPos=0;
      
                  for (int i=0;i<taskNum;i++){
      
                      SumThread sumThread=new SumThread(list,startPos,endPos);
                      //将每次拆分的任务都放入任务集中
                      sumThreads.add(sumThread);
                      //将子任务提交给线程池
                      sumThread.fork();
      
                      startPos+=THRESHOLD-1;
      
                      endPos=(endPos+THRESHOLD-1)>end?end:(endPos+THRESHOLD-1);
      
                  }
                  //将子任务的结果进行总和
                  for (SumThread s:sumThreads){
      
                      sum+=s.join();
      
                  }
      
              }
      
              return sum;
      
          }
      
      }
      
      子任务ForkJoinPool-1-worker-2执行完成
      子任务ForkJoinPool-1-worker-11执行完成
      子任务ForkJoinPool-1-worker-4执行完成
      子任务ForkJoinPool-1-worker-13执行完成
      子任务ForkJoinPool-1-worker-13执行完成
      子任务ForkJoinPool-1-worker-6执行完成
      子任务ForkJoinPool-1-worker-11执行完成
      子任务ForkJoinPool-1-worker-13执行完成
      子任务ForkJoinPool-1-worker-4执行完成
      子任务ForkJoinPool-1-worker-15执行完成
      子任务ForkJoinPool-1-worker-13执行完成
      子任务ForkJoinPool-1-worker-8执行完成
      子任务ForkJoinPool-1-worker-10执行完成
      子任务ForkJoinPool-1-worker-11执行完成
      子任务ForkJoinPool-1-worker-2执行完成
      子任务ForkJoinPool-1-worker-6执行完成
      子任务ForkJoinPool-1-worker-8执行完成
      子任务ForkJoinPool-1-worker-1执行完成
      子任务ForkJoinPool-1-worker-13执行完成
      子任务ForkJoinPool-1-worker-15执行完成
      子任务ForkJoinPool-1-worker-4执行完成
      最后的结果是:4572
      
    • API说明:
      • 构造器
        • 源码
          public ForkJoinPool(int parallelism,
                          ForkJoinWorkerThreadFactory factory,
                          UncaughtExceptionHandler handler,
                          boolean asyncMode) {
              this(checkParallelism(parallelism),
                   checkFactory(factory),
                   handler,
                   asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
                   "ForkJoinPool-" + nextPoolId() + "-worker-");
              checkPermission();
          }
          
        • parallelism:并行数,默认为CPC的核心线程数,最小为1;
        • factory:线程工厂;
        • handler 默认为null,处理任务时出现的异常情况处理类;
        • asyncMode:是否为异步模式,默认为false,为同步时采用的是先进后出原则,而异步时则是先进先出原则进行。异步时就不能通过join获取到对应的结果集可根据实际情况进行使用。
      • 执行:
        • ForkJoinPool提供了excute和submit两个执行方法,submit用于返回执行的结果,ForkJoin提供了对Runnable、Callable以及ForkJoinTask的三种参数形式的执行,这里就说一下关于ForkJoinTask的参数,ForkJoinTask的抽象子类:RecursiveTask和RecursiveAction,RecursiveTask代表该工作任务有具体的返回值,而RecursiveAction没有具体的返回值。
    • 结构原理:
      • 工作窃取算法:工作窃取算法指的一个线程到另一个线程的队列去窃取任务来进行执行,其优势在于,当一个线程执行完成自己的工作队列后一般情况下就会等待其他线程执行完成在进行返回,而工作窃取算法则使得处于等待状态的线程去为完成工作的线程队列中去窃取队列,而为了防止线程之间因为竞争同一工作而耗费资源,所以窃取的一方则通过队列的某端获取工作任务,而被窃取的线程永远从队列头部获取工作任务,而这样的对垒称为双端队列。但缺点在于当队列中只存在一个任务时,就会出现线程的竞争。
      • ForkJoinPool中采用的hash数组+双端队列的结构存放任务,在ForkJoinPool中的WorkQueue[] workQueues 代表的就是hash数组,该数组的长度是2的指数,且支持扩容;workQueues内部装的就是工作任务,其内部又将任务拆分为外部任务和内部任务,如像excute、submit提交的任务都称为外部任务,内部任务则时通过fork/join方法产生的。ForkJoin将外部任务和内部任务进行分离,外部任务会利用Thread内部的随机probe值映射hash数组的偶数槽位的提交队列中,这种队列使用的是通过数据加以实现的双端队列;而内部的工作任务在映射到数组的奇数位上。之后每个工作任务所产生的子工作任务也会自动分配到自己所在的任务队列中。而我们上面提出的工作窃取算法。当线程A执行完自己的工作队列后就会去窃取线程B的工作队列中的任务,如果该任务又拆分了许多工作任务,当线程B完成自己的工作队列后,发现线程A窃取了自己的工作队列,且有子任务队列没有执行完毕,则又会从线程A中窃取任务,此工作机制被称为互助机制。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值