005.聊聊线程与线程池

作为Java 搬砖人员,免不了要接触线程与线程池,今天就来聊聊线程与JDK里的线程池

进入今天内容前,我们先思考下这么个问题:线程池里的线程是如何维持生命,不被GC掉?

what is thread
thread的使用
thread的几种状态
JDK线程池的使用
线程池里的线程

线程 英语:thread )是 操作系统 能够进行运算 调度 的最小单位
一条线程指的是 进程 中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
by wikipedia

一般有两种方式来使用线程处理任务
1.继承Thread,直接重写run方法,然后实例化Thread来启动线程
2.实现Runnable接口,实现run方法,然后把Runnable作为参数入参实例化Thread来启动线程

启动线程使用start方法,而不是run方法
直接使用run方法相当于普通的对象方法调用,而没有创建线程来运行run方法
可以对比下Thread的start方法和run方法源码,就可以发现猫腻
public synchronized void  start() {
    /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
     */
    if ( threadStatus !=  0)
        throw new IllegalThreadStateException() ;

    /* Notify the group that this thread is about to be started
     * so that it can be added to the group's list of threads
     * and the group's unstarted count can be decremented. */
    group.add( this) ;

    boolean started =  false;
    try {
        start0() ;
        started =  true;
   finally {
        try {
            if (!started) {
                group.threadStartFailed( this) ;
            }
        }  catch ( Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

public void  run() {
    if ( target !=  null) {
        target.run() ;
    }
}
start方法里会调用JVM本地方法start0 ,创建线程,后续将会由JVM来发起线程调度,去具体执行run方法
而Thread的run方法就是对传入的Runnable对象的方法调用而已

线程自身有4种状态(Java编程思想)
1.new。线程创建后会短暂处于此状态
2.Runnable。在此状态下,只要调度器把时间片分配给线程,线程就可以运行
3.Blocked。线程能够运行,但有某个条件阻止它的运行
4.Dead。处于死亡或终止状态的线程将不可再是可调度的,并且再也不会得到CPU时间,它的任务已经结束,或不再是可运行的

也就是说,处于Dead的线程,接下来就等着被GC了
所以,如果能够让线程不断的在Runnable、Blocked  运行状态间持续转换,就可以一直维持线程的“生命”了


下面来聊下线程池,大概说下线程池的使用
JDK线程池中一个构造方法
关键的5个参数为corePoolSize、maximumPoolSize、workQueue、threadFactory、handler
public ThreadPoolExecutor( int corePoolSize ,
                          int maximumPoolSize ,
                          long keepAliveTime ,
                          TimeUnit unit ,
                          BlockingQueue< Runnable> workQueue ,
                          ThreadFactory threadFactory ,
                          RejectedExecutionHandler handler)

通过线程池的处理流程分析,我们就能很好的理解这几个参数的意义

1.当我们往线程池添加任务时,开始poolsize=0,需要创建线程进行任务处理
1.1 不断的添加任务,也不断的创建线程,直到poolsize = corePoolSize
2.当添加第 corePoolSize+1 个任务时,任务会被放到queue里,等待空闲线程进行处理
2.1 一般我们会使用有界的队列,不停的添加任务到队列,直到队列满;
     使用无界队列就看内存什么时候耗尽了,这是一个非常危险的行为
3.队列满的情况下,继续添加任务,这时,会重新开始创建线程进行任务处理
3.1不断添加任务,直到poolsize = maximumPoolSize;这时停止线程创建
4.当线程满负荷运行,依然有新任务过来,这时就需要拒绝策略了;
一般有几种拒绝策略,如直接丢弃、由caller直接执行任务、抛出拒绝exception等

通过threadFactory我们可以自定义thread的创建,比如重定义下线程的名字,方便识别

了解了线程池的大概处理流程,就基本知道应该怎么使用线程池了;无非就具体参数值的设定,根据实际场景选择合适的参数组合

下面来具体分析下线程池里的线程
再回头开头提的那个问题:线程池里的线程是如何维持生命,不被GC掉?

线程的执行方法本身非常好理解,不断的获取task,然后执行task
看上去是个while循环,只要有task,线程就能不断执行了
而如果没有task,线程就会完成使命,等待被回收了
final void  runWorker( Worker w) {
    Thread wt =  Thread. currentThread() ;
    Runnable task = w. firstTask ;
    w. firstTask null;
    w.unlock() // allow interrupts
    boolean completedAbruptly =  true;
    try {
        while (task !=  null || (task = getTask()) !=  null) {
            w.lock() ;
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt
            if (( runStateAtLeast( ctl.get() STOP) ||
                 ( Thread. interrupted() &&
                  runStateAtLeast( ctl.get() STOP))) &&
                !wt.isInterrupted())
                wt.interrupt() ;
            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) ;
    }
}

下面看下具体的获取task的方法: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;
        }

        int wc =  workerCountOf(c) ;

        // Are workers subject to culling?
        boolean timed =  allowCoreThreadTimeOut || wc >  corePoolSize ;

        if ((wc >  maximumPoolSize || (timed && timedOut))
            && (wc >  ||  workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

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

getTask方法里是个死循环
A  boolean timed =  allowCoreThreadTimeOut || wc >  corePoolSize ;

B             Runnable r = timed ?
                workQueue.poll( keepAliveTime TimeUnit. NANOSECONDS) :
                workQueue.take() ;

主要看这两行代码
A  首先是判断是否需要考虑超时
B  然后根据是否超时 来选择 从队列获取任务的方式

超时判断主要考虑 wc >  corePoolSize 这个,即poolsize 与 corePoolSize比较
意思就是说,如果poolsize > corePoolSize ,说明当前线程可能是多余的空闲线程
     只会等待一定的间隔从队列获取任务数据
否则就会直接调用队列的非超时获取方法,就会一直阻塞,直到有数据为止
阻塞操作主要是加锁操作的阻塞

因此,线程池里的corePool线程,是通过队列的阻塞操作与不断的任务处理来维持其生命的
当然除去异常退出外 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值