juc线程池的使用

juc线程池

Executor接口 是整个线程池的父接口
juc线程池的使用_第1张图片juc线程池的使用_第2张图片

ExecutorService是他的子接口 其下面有两个实现类的分支 :
1. ThreadPoolExecutor
2. ForkJoinPool

1. ThreadPoolExecutor

juc工具类中提供了三种默认的poolExecutor创建方式 :

	ExecutorService executor = Executors.newFixedThreadPool(5);
    ExecutorService singleExecutor = Executors.newSingleThreadExecutor();
    ExecutorService cacheExecutor = Executors.newCachedThreadPool();
第一种 : fixed 固定线程数的线程池
第二种 : 单例只有一个线程的线程池
第三种 : 可扩容的线程池

我们在使用中应该用哪个呢 ?

-----答案是都不用----

我们看一下三种创建对象的源码

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
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>());
    }

可以从源码中看到 他们调用的都是 ThreadPollExecutor的构造器 只是设置参数不同
那么为什么不使用工具类呢 问题就出现在设置参数上

  • fix和single都使用的是SynchronousQueue阻塞队列作为缓冲队列 这个队列的默认边界为 Integer.MAX_VALUE 是一个巨大的值 若等待的线程非常多 会导致内存溢出 (oom)
  • 同样的 cache虽然创建SynchronousQueue作为缓冲队列 等待队列是单例的 但是他允许的最大线程数是 Integer.MAX_VALUE 同样当线程非常多时 会导致内存溢出(oom)

那么就应该在开发过程中使用自定义的 ThreadPoolExecutor
最复杂的ThreadPoolExecutor的构造器参数有七个(不管几个参数的构造器最后都是调用的重载的7个参数的构造器 )

public ThreadPoolExecutor(
        int corePoolSize,   //常驻线程数
        int maximumPoolSize, //最大线程数
        long keepAliveTime, //存活时间
       	TimeUnit unit,  //存活时间单位
        BlockingQueue<Runnable> workQueue, // 任务缓冲队列
        ThreadFactory threadFactory,  // 生成工作线程的线程工厂
        RejectedExecutionHandler handler){ //拒绝策略 当队列满执行拒绝策略(调用的offer方法不会死等所以需要拒绝策略)

四个拒绝策略

  • AbortPolicy : 默认AbortPolicy 满载直接抛异常RejectedExecutionException
  • CallerRunsPolicy : 调用者运行机制 不会抛弃任务也不会抛异常 将某任务回退到调用者 降低新任务流量 (某线程调用另一个线程执行 回退给这个线程去调用)
  • DiscardPolicy : 就是使用offer()方法 满了就不加了
  • DiscardOldestPolicy : 相反 满了将旧的抛弃 放入新的(注意是队列旧的是队尾出队 新的队头入队)

2.ForkJoinPool

另一种线程池 他用于实现分支合并框架
需要传入一个 ForkJoinTask 对象
他的子类 RecursiveTask 主要思想是将 任务分支交给不同线程去调用 再合并 这样比一个线程 栈 递归调用要快

以1+到100为例

将其进行拆分 每20个相加为一个线程去执行
由 fork 函数 进行分支
由 join 函数 进行合并

放入池中 调用 submit 方法 返回 任务对象
再调用 get 方法获得结果 (这里是futureTask的方法 他实现了FutureTask接口 原理 和 FutureTask 放入 Thread中执行是一样的)

class Task extends RecursiveTask<Integer> {
    int end;
    int begin;
    int result;
    /////省略构造器 get set 方法
    @Override
    protected Integer compute() {
        if((end - begin) < 10){
            for (int i = begin; i < end; i++) {
            result = result + i;
            }
        } else {
            int middle = (end + begin)/2;
            Task task = new Task(begin,middle);
            Task task1 = new Task(middle,end);
            task.fork();
            task1.fork();
            result = task.join() + task1.join();
        }
        return  result;
    }
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        Task task = new Task(0,100);
        ForkJoinTask<Integer> submit = forkJoinPool.submit(task);
        System.out.println(submit.get());
    }
}

你可能感兴趣的:(java,算法)