• 理解线程池中线程的复用原理


    线程的运行比较复杂,平常我们调用start(start0;)方法就完事了,啥时候执行run里面的代码?经过各种状态的转换获得cpu时间片,jvm就会帮我们执行run方法,执行完run方法这个线程自动消亡,遇到异常线程也会消亡,这就是一个线程的生命周期。

    线程有两种实现方式,一种是继承Thread,重写run方法,一种是自己写一个Task实现runable接口重写run方法,他们的启动方式 分别是如下

    //第一种方式,继承Thread,重写run方法后的启动
    new MyThread().start();
    //第二种方式,将实现的runable接口的task作为参数传入Thread构造方法
    new Thread(new Runnable() {
        @Override
        public void run(){
            System.out.println("do something");
        }
    }).start();
     

    jvm执行完run方法,就会执行退出方法,该退出方法在jvm内部用C++语言写的

    void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
      //线程退出的方法
    }
     

    我们看这两种线程启动方式,感觉启动一次只能传一个任务进去。而且run方法又是由黑盒的jvm执行的,jvm执行完run方法就退出了,那线程池是怎么传多个任务进去的?怎么控制run方法一直执行的?

    这一系列疑问,要进入线程池源码才能略知一二

    源码实在太复杂了,我们从前文的铺垫开始,以正常人的思路,首先找到我们的主角---线程

    1,线程池中的线程在哪?

    当我们没有将线程工厂传入的时候使用的是默认的线程工厂

    //1,ThreadPoolExecutor的构造方法众多,但都指向一个,那就是如下
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                 Executors.defaultThreadFactory(), handler);
    ​
    //2,将Executors.defaultThreadFactory()进入会发现默认线程工厂
    public static ThreadFactory defaultThreadFactory() {
            return new DefaultThreadFactory();
    }
    //3,在哪触发产生线程?在如下构造方法里,
    //产生线程的s时候将这个内部类this本身传递进去
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }
    //4,进入产生线程的方法
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,namePrefix + threadNumber.getAndIncrement(),0);
        ...//其他代码
        return t;
    }
     

    好了我们的线程主角已经登场,但是他的登场并没有多高大上,而是寄人篱下在一个名叫Worker的内部类里

    这个意思是只要 new Worker(Runnable firstTask),就会产生一个线程,并且产生线程的时候将这个内部类本身this传入进去当task。

    这似乎跟我们前文讲的差不多,new一个线程就放一个task,说好的线程复用呢?好了主角线程有了,我们的task也有了,我们再找找start方法

    //线程启动的时候
    private boolean addWorker(Runnable firstTask, boolean core) {
      w = new Worker(firstTask);
      final Thread t = w.thread;
      ...//代码
      t.start();
      ...//代码
    }
     

    线程是启动了,别忘了,线程启动的时候我们将内部类worker对象传入进去了,内部类Worker是实现了runable接口的,jvm执行run方法的时候就会执行Worker中的run方法

    //1,这是Work中的一个方法,线程启动的时候jvm会执行它,
    public void run() {
      runWorker(this);
    }
    ​
    //2,继续看里面的runWorker(this);很显然this是内部类Work对象本身
    final void runWorker(Worker w) {
            Thread wt = Thread.currentThread();
            Runnable task = w.firstTask;
            w.firstTask = null;
            ../代码
            try {
                while (task != null || (task = getTask()) != null) {
                try{
                    ../代码
                    task.run();
                    }catch(){
                    ../代码
                    } finally {
                        ../代码
                    }
                }
                ../代码
            } finally {
                ../代码
            }
     }
    
    

    很显然这个runWorker很不得了,里面一个大大的while循环,当我们的task不为空的时候它就永远在循环,并且会源源不断的从getTask()获取新的任务,继续看getTask()方法

    //很显然这个方法是从队列中获取任务workQueue
    private Runnable getTask() {
           ...//代码
           // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || 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()方法里面有个三元表达式,当条件为真时从任务对列workQueue.take()里面获取要执行得任务;

    讲到这里线程复用的流程就讲完了,最核心的一点是,新建一个Worker内部类就会建一个线程,并且会把这个内部类本身传进去当作任务去执行,这个内部类的run方法里实现了一个while循环,当任务队列没有任务时结束这个循环,则这个线程就结束。

     

     https://mp.weixin.qq.com/s?__biz=MzI4NTEzMjc5Mw==&mid=2650554832&idx=1&sn=f56a25628e842edd13529e77769b2d55&chksm=f3f83346c48fba507ec005ce9bd91c6e65324b43a46b7f56a9d1542235ec9cf4b1394880b034&token=1663379705&lang=zh_CN#rd

  • 相关阅读:
    Linq To Sql 练习
    伪静态在webconfig中配置
    log4net.dll配置以及在项目中应用
    C#Windows服务安装
    .net平台推送ios消息
    asp.net下js调用session
    MAC地址泛洪攻击测试
    wifipineapple使用教程
    python程序的调试方法
    python import的用法
  • 原文地址:https://www.cnblogs.com/qq289736032/p/11159951.html
Copyright © 2020-2023  润新知