博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java 线程池
阅读量:4059 次
发布时间:2019-05-25

本文共 3073 字,大约阅读时间需要 10 分钟。

为什么要用线程池(自己的理解, 如有错误,评论区指导~)

  以web服务器为例, 其实在低并发量的时候,我觉的不使用线程池也没有太大关系。但是一旦并发量上来了,或者有人对你的网站发起恶意流量攻击,

那么假如我们单纯地对于每个请求都创建一个线程来处理、会怎么样呢?

  首先java是如何创建线程的呢?  

    1、继承Thread类

    2、实现Runnable接口,然后再new Thread(Runnable command)

  也就是先创建一个线程实例,然后去调用实例的start()方法,这个start方法内部会调用本地方法start0(),也就是执行start0, java虚拟机会调用本地操作系统的创建线程接口,创建一个操作系统线程,这个线程约占内存几MB,我们先算它为1MB,那么对于一个4GB内存的机器,也仅仅只能容纳4000个线程,而且实际上在Linux中, 所有线程的内核栈以及thread_info描述符所占用的内存默认不能超过物理内存的1/8。

  所以在达到一定的并发级别时, 如果采用这种简单的对于每个请求创建一个线程进行处理,那么必然会导致CPU有大量的时间用于创建线程,导致其他处理任务无法及时得到调度,超时。对于用户的体验就是网站打不开,因为任务在后台都超时了。

 

什么是线程池,在Java中是如何实现的

   顾名思义就是把线程放在一个池子里;在Java中用一个类ThreadPoolExecutor来实现。这个池就是ThreadPoolExecutor的一个成员变量HashSet实现的。

/**     * Set containing all worker threads in pool. Accessed only when     * holding mainLock.     */    private final HashSet
workers = new HashSet
();

  介绍线程池的两个重要参数

  1、corePoolSize: 在线程池中(实际上存在ThreadPoolExecutor的hashSet中)的worker数(一个worker就对应一个线程)小于等于corePoolSize, 那么对应新来的请求就会去创建一个线程。

  2、maximumPoolSize:对于这个参数有多种策略,我们暂时大体认为这样:线程池中的线程数绝对不能超过maximumPoolSize。

怎么使用线程池呢?

  如果想要使用线程池,当然需要先new一个,构造参数我们先不管  

1

ThreadPoolExecutor s = new ThreadPoolExecutor(2,4, 5,TimeUnit.MICROSECONDS, new LinkedBlockingDeque());

  想要使用一个线程池执行一个任务, 调用execute()方法, 这个command就是一个任务task

1

s.exexute(Runnable command)

  那么这个execute()具体干了啥呢?它大体会干三件事, 这里可以整体上描述一下,具体可查看源码

    1、如果线程池的hashSet中的worker数(线程数)小于corePoolSize,直接创建新增一个worker(addWorker()方法)来运行这个任务

    2、如果大于corePoolSize, 如果线程池处于运行状态,就把task塞进线程池的队列中,等待其他worker空闲的时候再去该队列中取出任务来执行

      这里出现了一个新的数据结构: workQueue  

    3、其他情况会拒绝掉该task(这个先带过,后面再讲)

private final BlockingQueue
workQueue;

  下面我们再来看看这个addWorker()方法:就是给线程池新建一个worker(线程)来执行任务,这个方法干了哪些事呢?

    1、进入一个循环检查线程池状态、现有worker数量等来判断是不是真的应该去新建一个线程,如果不应该, return false,表示新建失败

    2、如果确实应该新建一个worker,那就调用addWorker(firstTask),以该task为参数构建一个worker,  然后调用worker成员变量thread.start()

       worker是Worker类的实例,Worker类是ThreadPoolExecutor的内部final类。 看一下这个Worker类的构建方法

Worker(Runnable firstTask) {            setState(-1); // inhibit interrupts until runWorker            this.firstTask = firstTask;            this.thread = getThreadFactory().newThread(this);        }

   看第三句:worker内部的成员变量thread,它是用worker实例作为参数调用newThread()构建的线程实例,Worker类 implement Runnable,如此一来, 执行thread的start()方法、便会新建一个线程去执行worker的run()。

 

  接下来再看看worker的run()方法干了啥, 这个方法很简单

/** Delegates main run loop to outer runWorker  */        public void run() {            runWorker(this);        }

 

  那么我们再看看这个runWorker()方法干了啥

  这个方法其实就是进入一个循环,这个循环干嘛呢?它先看看有没有firstTask, 就是每个worker创建之初需要执行的那个任务;如果这个初始任务执行完了, 它就会检查队列中有没有其他任务,如果有的话、取出队列中一个任务执行该task的run()方法、会在本线程内执行,这样就实现了使用一个线程去执行多个任务的功能。当然,task仅仅是实现了Runnable接口,并没有调用new Thread(Runnable command)去构建一个线程实例,所以task也没有start()可以调用。如果firstTask执行完了,  队列中没有其他任务了,那么该worker就寿终正寝,线程将会死亡,当然在死亡之前,会进行一些清理工作和登记工作,比如登记这个worker完成了多少task,汇总到线程池的总完成任务数中。

 

拒绝策略:

1、AbortPolicy:直接抛出异常, 阻止系统正常运行。

2、CallerRunPolicy: 只要线程池没有关闭, 该策略直接在调用者线程中, 运行当前被丢弃的任务, 显然这样做不会真的丢弃任务, 但是, 任务提交线程的性能极有可能会急剧下降。

3、DiscardOldestPolicy: 丢弃最老的一个请求, 也就是即将被执行的一个任务, 并尝试再次提交当前任务。

4、DiscardPolicy: 该策略默默地丢弃无法处理的任务, 不予任何处理。如果允许任务丢失, 这是最好的一种方案。

 

转载地址:http://lnzji.baihongyu.com/

你可能感兴趣的文章
【opencv学习笔记】014之上采样与降采样
查看>>
【opencv学习笔记】015之基本阈值操作
查看>>
【CryptoZombies - 1 Solidity 教程】016 函数多返回值&奖励实战
查看>>
【CryptoZombies - 2 Solidity 进阶】001 智能合约的不可篡改性与Ownable
查看>>
【积跬步以至千里】App Crashed - WriteMiniDump
查看>>
我努力是因为, 我想通过自己,带给这个世界点什么!
查看>>
数据结构基础笔记、基础知识总结、周周练汇总,通过代码,更快速掌握数据结构和算法知识!
查看>>
赛前必看!!NOIP竞赛及CSP认证初赛赛前辅导详细视频教程!!!
查看>>
完美解决AttributeError: module ‘torchvision.models‘ has no attribute ‘detection‘
查看>>
VMWare报错:无法获得VMCI驱动程序的版本:句柄无效。
查看>>
重磅!AI与区块链技术知识分享交流会!特邀贾志刚老师、双一流211高校研究生!
查看>>
入门卷积神经网络必备,基础、理论、实战一网打尽!
查看>>
Java报错:No enclosing instance of type learnJ is accessible.
查看>>
java学习(2)类变量与实例变量
查看>>
java学习(3)类的四大特性1
查看>>
java学习(4)类的四大特性2之继承
查看>>
java学习(5)类的四大特性2之继承(方法重载)
查看>>
java学习(6)类的四大特性2之继承(方法覆盖)
查看>>
java学习(7)类的四大特性2之继承(抽象类)
查看>>
java学习(8)类的四大特性2之继承(接口)
查看>>