java 线程池使用和详解

作者: cobs | 来源:发表于2017-06-08 07:47 被阅读549次

线程池的使用

构造方法

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
  • corePoolSize:线程池维护线程的最少数量
  • maximumPoolSize:线程池维护线程的最大数量
  • keepAliveTime:线程池维护线程所允许的空闲时间
  • unit:线程池维护线程所信息的空闲时间的单位
  • workQueue:线程池所使用的缓冲队列
  • threadFactory:线程创建工厂类
  • handler:线程池对任务的处理策略

注意

  • 线程池在创建的时候是不创建线程的,只有加入任务才创建线程,当然可以调用prestartCoreThread方法(是在ThreadPoolExecutor类中实现的)来初始化一个线程。
  • 线程池先会创建完corePoolSize个线程,在把任务保存到队列中,队列满后才会再创建线程,直到规定的maximumPoolSize,然后会执行拒绝策略。
  • 设置线程池的大小参考
    • 如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 NCPU+1
    • 如果是IO密集型任务,参考值可以设置为2*NCPU

BlockingQueue常用的3个实现类

  1. ArrayBlockingQueue 构造参数必须带一个int参数来指明其大小,FIFO
  2. LinkedBlockingQuene 大小不定,可以人为指定,若没有有Integer.MAX_VALUE来决定。FIFO
  3. SynchronousQueue 对其操作必须放和取交替完成。在线程池中它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务

LinkedBlockingQueue的数据吞吐量要大于ArrayBlockingQueue,但在线程数量很大时其性能的可预见性低于ArrayBlockingQueue.
SynchronousQueue初始化后必须先有其他线程对其使用take()方法后才能使用put() 或者 offer()方法

java 定义好的4中类型的线程池

  1. newCachedThreadPool创建一个可缓冲线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可用线程,则新建线程
  2. newFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  3. newScheduledThreadPool 创建一个定长线程池,支持定时和周期性任务执行。
  4. newSingleThreadExecutor 一个单线程化的线程池,确保所有任务按照指定的顺序执行。

下面是线程池核心的执行代码,只看懂一点:cry:

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        //它在执行完一个线程会getTask再去取一个,直到没有任务。
        while (task != null || (task = getTask()) != null) {
            w.lock();
            //.......删除N行代码
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

gettask方法

private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            //......省略N行代码

            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

这里用到了BlockingQueue的方法,很巧妙的使用。timed是判断这个线程是否可以回收,由我们设置的allowCoreThreadTimeOut和当前线程池线程大小决定的。当要回收,用的是poll方法,不能回收用的是take方法阻塞线程

线程池的shutdown()和shutdownNow();

  • shutdown()方法会更改线程池的状态为SHUTDOWN状态,并不会立即终止。此时不能往线程池中添加任务,否则会抛出RejectedExecutionException异常。它会等线程池里的所有任务都处理完毕再退出线程。
  • shutdownNow()方法会立即更改状态为STOP状态。并试图停止所有正在执行的线程,不再处理还在池队列中等待的任务,它会返回那些未执行的任务。其试图终止线程的方法是通过调用Thread.interrupt()方法来实现的。

提个问题,线程池有两种提交方式execute()和submit()区别,可以在评论区讨论哦!请关注微信公众号,查看,嘻嘻。不过以后会在评论区回复的!!

如果大家感觉满意,就点个喜欢吧!!哈哈


上一篇文章 java 线程和线程安全初识

如有什么不对的地方,欢迎大家指出来,我们共同学习!!
接下来会写关于 内部类 的文章,谢谢大家关注

欢迎关注我的微信公众号cobs-snail,让我们一起前进吧!!

前进吧蜗牛

相关文章

  • Java调度线程池ScheduleExecutorService

    作者: 一字马胡 转载标志 【2017-11-03】 更新日志 链接 Java线程池详解(一)Java线程池详解...

  • java 线程池使用和详解

    线程池的使用 构造方法 corePoolSize:线程池维护线程的最少数量 maximumPoolSize:线程池...

  • Java 线程池详解

    Java ThreadPoolExecutor详解 ThreadPoolExecutor是Java语言对于线程池的...

  • 2019-05-04 线程池ThreadPoolExecutor

    1.线程池参数详解 在上一篇文章java常用线程池中,可以看到它们都使用了一个核心的线程池类ThreadPoolE...

  • 线程

    Java 并发编程:线程池的使用 Java 并发编程:线程池的使用java 多线程核心技术梳理 (附源码) 本文对...

  • J.U.C——线程池专题

    主要讨论以下问题: 认识Java线程池 线程池的种类,区别,和使用场景 线程池的工作流程 线程池几个参数的理解 分...

  • JAVA多线程-线程池使用详解

    为什么要用线程池:1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。2.可以根据系统的...

  • Android面试Java基础篇(二)

    (一)问:Java线程池的实现原理和使用 线程池即存放和管理线程的一个池子 (1)复用线程池中的线程,避免因为线程...

  • Java线程池解析

    参考文章:Java并发:线程池,饱和策略 前言 Java线程池的使用在工作中还是比较常见的,线程池可以减小线程建立...

  • 谈谈我对Java线程的了解

    本篇文章主要介绍java中线程和线程池的使用 一、线程 1. 线程分类 继承 java.lang.Thread 实...

网友评论

  • cobs: 线程池execute()和submit()区别
    - 接受的参数不一样,submit 可以接实现Callable的线程。
    - submit()有返回值,而execute()没有;
    - submit()可以进行Exception处理; 通过Future.get() 抛出异常

本文标题:java 线程池使用和详解

本文链接:https://www.haomeiwen.com/subject/lcdzfxtx.html