• 多线程:线程池原理


    1、基础

    (1)线程模型的分类

      用户线程(ULT)。用户程序实现,不依赖操作系统核心,应用提供创建、同步、调度和管理线程的函数来控制用户线程。不需要用户态/核心态切换,速度快。内核对ULT无感知,线程阻塞则进程〈包括它的所有线程)阻塞。
      内核线程(KLT),系统内核管理线程(KLT),内核保存线程的状态和上下文信息,线程阻塞不会引起进程阻塞。在多处理器系统上,多线程在多处理器上并行运行。线程的创建、调度和管理由内核完成,效率比ULT要慢,比进程操作快。

    (2)java线程与内核线程的关系

    jvm运用的是内核级线程模型,java线程与内核线程是一对一的关系

     (2)Executor框架

    2、线程池的五种状态

    (1)五种状态

    • Running:能接受新任务以及处理已添加的任务
    • Shutdown:不接受新任务,可以处理已经添加的任务
    • Stop:不接受新任务,不处理已经添加的任务,并且中断正在处理的任务
    • Tidying:所有的任务已经终止, ctl记录的”任务数量”为0 ctl负责记录线程池的运行状态与活动线程数量
    • Terminated:线程池彻底终止,则线程池转变为terminated状态

    (2)流程

    (3)源码(保证线程的安全,将线程池的所有状态保存到一个变量中)

    Interger类型的变量的解析:

    3、线程池工作原理

    (1)五种创建方式

    • newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需求可灵活回收空闲线程,若无可回收,则新建线程
    • newFixedThreadPool:创建一个定长线程池,可控制线程的最大并发数,超出的线程会在线程池中等待。
    • newScheduleThreadPool:创建一个定长线程池,支持定时及周期性任务处理
    • newSingleThreadScheduledExecutor:创建一个单线程化的线程池,他只用唯一的工作栈来执行任务,一池一线程
    • newSingleThreadExecutor):串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

    直接使用Executors创建线程的话,线程的数量没有上限,因此需要手动创建线程池

    (2)源码ThreadPoolExecutor

    public ThreadPoolExecutor(int corePoolSize,//核心线程池的大小
                                  int maximumPoolSize,//最大线程数量
                                  long keepAliveTime,//线程最长可以空闲多久
                                  TimeUnit unit,//时间单位
                                  BlockingQueue<Runnable> workQueue,//阻塞队列,在任意时刻不管并发有多高,永远只有一个线程能够进行队列的入队或者出队操作!

                                   队列满,只能进行出队操作,所有入队的操作必须等待,也就是被阻塞
                                   队列空,只能进行入队操作,所有出队的操作必须等待,也就是被阻塞


    ThreadFactory threadFactory) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); }

      流程:

    4、execute方法

    public void execute(Runnable command) {
            if (command == null)
                throw new NullPointerException();
            /*
             * Proceed in 3 steps:
             *
             * 1. If fewer than corePoolSize threads are running, try to
             * start a new thread with the given command as its first
             * task.  The call to addWorker atomically checks runState and
             * workerCount, and so prevents false alarms that would add
             * threads when it shouldn't, by returning false.
             *
             * 2. If a task can be successfully queued, then we still need
             * to double-check whether we should have added a thread
             * (because existing ones died since last checking) or that
             * the pool shut down since entry into this method. So we
             * recheck state and if necessary roll back the enqueuing if
             * stopped, or start a new thread if there are none.
             *
             * 3. If we cannot queue task, then we try to add a new
             * thread.  If it fails, we know we are shut down or saturated
             * and so reject the task.
             */
            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);
        }

    下面代码的ctl是和下面代码片段的ctl联系的:

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
        private static final int COUNT_BITS = Integer.SIZE - 3;
        private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
    
        // runState is stored in the high-order bits
        private static final int RUNNING    = -1 << COUNT_BITS;
        private static final int SHUTDOWN   =  0 << COUNT_BITS;
        private static final int STOP       =  1 << COUNT_BITS;
        private static final int TIDYING    =  2 << COUNT_BITS;
        private static final int TERMINATED =  3 << COUNT_BITS;

    将线程池的所有状态保存到一个Integer类型的变量中去,保证线程的安全。

    每个人都会有一段异常艰难的时光 。 生活的压力 , 工作的失意 , 学业的压力。 爱的惶惶不可终日。 挺过来的 ,人生就会豁然开朗。 挺不过来的 ,时间也会教你 ,怎么与它们握手言和 ,所以不必害怕的。 ——杨绛
  • 相关阅读:
    Educational Codeforces Round 58 A,B,C,D,E,G
    Codeforces Round #530 (Div. 2) F (树形dp+线段树)
    Codeforces Round #530 (Div. 2) A,B,C,D
    bzoj 3262: 陌上花开
    bzoj 2653: middle (主席树+二分)
    费马小定理的证明
    分组背包
    二维费用的背包问题
    luoguP1164 小A点菜(背包问题)
    完全背包问题
  • 原文地址:https://www.cnblogs.com/zhai1997/p/13655762.html
Copyright © 2020-2023  润新知