线程池
(1)什么是线程池
创建和销毁对象是非常耗费时间。
1)创建对象:需要分配内存等资源 。
2)销毁对象:虽然不需要程序员操心,但是垃圾回收器会在后台一直跟踪并销毁 。
对于经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
3)思路:创建好多个线程,放入线程池中,使用时直接获取引用,不使用时放回池中。可以避免频繁创建销毁、实现重复利用 。
4) 生活案例:在尚学堂借用和归还电脑、共享单车
技术案例:线程池、数据库连接池 JDK1.5 起,提供了内置线程池
(2)线程池的好处
1) 提高响应速度(减少了创建新线程的时间)
2) 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3) 提高线程的可管理性:避免线程无限制创建、从而销毁系统资源,降低系统稳定性,甚至内存溢出或者CPU耗尽。
项目中使用ThreadPoolExecutor进行多线程开发。使用起来很方便,但是当用jstack查看堆栈信息或者Jprofiler调试性能的时候,看到的线程都是pool-1-thread-1\2\3\4之类的。如果一个系统中用到了多个线程池,就无法区分哪个线程造成的系统问题。所以每次都需要点 Thread Dumps 去查看线程执行的具体的代码与堆栈信息,推测是哪个地方出的问题。
于是,需要修改默认的线程名字。查看Executor框架设计,ThreadPoolExecutor类的构造函数,可以实例化和配置灵活多样的线程池服务,该类基本的构造函数中包含以下7个参数:(int corePoolSize,int maximumPoolSize,Long KeepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectExecutionHandler handler)
1、线程数量的控制---线程池模式:
线程池实例的创建主要关心线程池的核心线程数和最大线程数的大小,通过对这两个参数的设置,可以调整线程池的线程模式,如若核心线程池等于最大线程池,则该线程池即为固定大小线程池,若最大线程池无限大,则为无界线程池等。 以上两个参数可以通过setCorePoolSize(int)和setMaximumPoolSize(int)动态更改。 Executors类提供了一般典型线程池的静态实例化方法,可以用于实例化多种类型的线程池,包括固定长度线程池(newFixThreadPool),可缓存线程池(newCachedThreadPool)等。
2、新线程的创建---线程工厂
通过实现线程工厂接口自定义线程工厂,可以定制新线程的名称,所属线程组,优先级,守护状态等信息,一般来说,自定义的线程工厂通常用来设置新线程的名称,向日志中写入信息,维护统计信息等,一个典型的自定义线程工厂应该包含一个定制的Thread子类(参考java并发编程实战P146,程序清单8-7)。 Executors类提供了默认的线程池工厂defaultThreadFactory() 。
3、线程等待机制---任务队列
任务队列用于管理等待执行的任务,队列的选择需要结合线程池机制的选择,包括无界队列,有界队列和同步移交队列,Executors中实现的默认固定长度和单例线程池都默认使用了无界队列。 有界队列包括ArrayBlockQueue,linkedBlockingQueue和PriorityBlockingQueue,有界队列可以避免资源耗尽情况的发生。如果线程池无界,可以使用同步移交队列。
线程池
作用
管理线程、减少内存的消耗
ThreadPoolExecutor
创建方式:通过new创建
构造方法
public ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler )int corePoolSize
就是线程池中的核心线程数量,这几个核心线程,只是在没有用的时候,也不会被回收
int maximumPoolSize
就是线程池中可以容纳的最大线程的数量
当线程池中的线程数等于 corePoolSize 并且 workQueue 已满,这时就要看当前线程数是否大于 maximumPoolSize,如果小于maximumPoolSize 定义的值,则会继续创建线程去执行任务, 否则将会调用去相应的任务拒绝策略来拒绝这个任务。
long keepAliveTime
就是线程池中除了核心线程之外的其他的最长可以保留的时间,因为在线程池中,除了核心线程即使在无任务的情况下也不能被清除,其余的都是有存活时间的,意思就是非核心线程可以保留的最长的空闲时间,
TimeUnit unit
时间的一个单位
TimeUnit.DAYS; //天 TimeUnit.HOURS; //小时 TimeUnit.MINUTES; //分钟 TimeUnit.SECONDS; //秒 TimeUnit.MILLISECONDS; //毫秒 TimeUnit.MICROSECONDS; //微妙 TimeUnit.NANOSECONDS; //纳秒BlockingQuue<Runnable> workQueue
就是等待队列,任务可以储存在任务队列中等待被执行,执行的是FIFIO原则(先进先出)
1)ArrayBlockingQueue //基于数组的先进先出队列,此队列创建时必须指定大小; 2)LinkedBlockingQueue //基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE; 3)synchronousQueue //这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。ThreadFactory threadFactory
就是创建线程的线程工厂
Executors.defaultThreadFactory()//默认线程工厂,你想用其他的要导入其他的jar包RejectedExecutionHandler handler
当线程池的缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略
new ThreadPoolExecutor.AbortPolicy: // 丢弃任务并抛RejectedExecutionException异常。 new ThreadPoolExecutor.DiscardPolicy:// 也是丢弃任务,但是不抛出异常。 new ThreadPoolExecutor.DiscardOldestPolicy:// 丢弃队列最前面的任务,然后重新尝试执行任(重复此过程) new ThreadPoolExecutor.CallerRunsPolicy:// 由调用线程处理该任务是一种拒绝策略,我们可以在任务满了之后,拒绝执行某些任务 第一种AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满 第二种DisCardPolicy:不执行新任务,也不抛出异常 第三种DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行 第四种CallerRunsPolicy:直接调用execute来执行当前任务常用方法
execute():提交任务交给线程池运行 不需要运行结果的你就用execute()
submit():提交任务,能够返回执行结果execute+Future需要运行结果的你就用submit(),当然你要想获取返回值在里面传入的就是callable类型的对象了还要再Callable里定义返回参数了
shutdown():不能再往线程池中添加任何任务 否则就抛异常,关闭线程池,等待任务都执行完,包括队列中的
shutdownNow():关闭线程池,不等待任务执行完。执行该方法,线程池的状态立刻变成STOP状态,并试图停止(interrupt()方法)所有正在执行的线程,不再处理还在池队列中等待的任务。
getTaskCount():线程池已执行和未执行的任务总数
getCompletedTaskCount():已完成的任务数量
getPoolSize():线程池当前线程数量
getActiveCount():当前线程池中正在执行任务的线程数量
其他的线程池
创建方式:Executors类方法点出来的,一般用ExecutorService去接
CachedThreadPool
可缓存的线程池,该线程池中没有核心线程,非核心线程的数量为Integer.max_value,就是无限大,当有需要时创建线程来执行任务,没有需要时回收线程,适用于耗时少,任务量大的情况。
本质上是ThreadPoolExecutor的特定参数
SecudleThreadPool
周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程的大小也为无限大。适用于执行周期性的任务。
SingleThreadPool
只有一条线程来执行任务,适用于有顺序的任务的应用场景。
FixedThreadPool
定长的线程池,有核心线程,核心线程的即为最大的线程数量,没有非核心线程 本质上是ThreadPoolExecutor的特定参数
使用举例
ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 10, 10, TimeUnit.MINUTES, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); AscThread ascThread = new AscThread(100); for (int i = 0; i < 10; i++) { executor.execute(ascThread); } }
代码实现 线程池抢票
package demo9;
import java.util.concurrent.*;
public class Thread0301 {
public static void main(String[] args) {
// public ThreadPoolExecutor(
int corePoolSize=3;//核心池子 数量 具有持久性
int maximumPoolSize= 5;//最大池子存量
long keepAliveTime=2000; //多少时间
TimeUnit unit= TimeUnit.SECONDS;//设置时间为秒数
//执行的是FIFIO原则(先进先出)
//超出线程池 可排队的数量
BlockingQueue<Runnable> workQueue=new LinkedBlockingDeque<Runnable>(4);
//创建默认的线程工厂
ThreadFactory threadFactory=Executors.defaultThreadFactory();
//超出设定的线程池加队列 数量的处理方法 抛弃 不报异常
/*
* new ThreadPoolExecutor.AbortPolicy: // 丢弃任务并抛RejectedExecutionException异常。
* new ThreadPoolExecutor.DiscardPolicy:// 也是丢弃任务,但是不抛出异常。 new
* ThreadPoolExecutor.DiscardOldestPolicy:// 丢弃队列最前面的任务,然后重新尝试执行任(重复此过程) new
* ThreadPoolExecutor.CallerRunsPolicy:// 由调用线程处理该任务是一种拒绝策略,我们可以在任务满了之后,拒绝执行某些任务
* 第一种AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满 第二种DisCardPolicy:不执行新任务,也不抛出异常
* 第三种DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行
* 第四种CallerRunsPolicy:直接调用execute来执行当前任务
*/
RejectedExecutionHandler handler=new ThreadPoolExecutor.DiscardPolicy();
// )
ThreadPoolExecutor tpe= new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler
);
//实例化myrun
//多个资源
myrun cc=new myrun();
myrun cc1=new myrun();
myrun cc2=new myrun();
//线程池加入实例化对象的引用
// 给进入的线程改名 在下面的接口实现类中进行
tpe.execute(cc);
tpe.execute(cc1);
tpe.execute(cc2);
//关闭线程池
tpe.shutdown();
}
}
//第一种 实现Callable接口
//class mycall implements Callable<String>{
// private static int ticket=10;
// private boolean flag=true;
// @Override
// public String call() throws Exception {
// while(flag) {
// synchronized(mycall.class){
// if(ticket<1) {
// flag=false;
// return "亲 --票都抢完啦!!!请下次再来吧";
// }
// ticket--;
// System.out.println(Thread.currentThread().getName()+"抢到了第"+(10-ticket)+"张票");
// }
// }
// return "真棒";
// }
//}
//第二种 实现Runnable接口
class myrun implements Runnable{
// static 定义变量 变成公共的 只有一份 独立于类存在
private static int ticket=10;
private boolean flag=true;
@Override
public void run() {
while(flag) {
synchronized(myrun.class){//因为多份资源 锁类 不能用this
if(ticket<1) {
flag=false;
return;
}
ticket--;
// 给进入的线程改名
if(("pool-1-thread-1").equals(Thread.currentThread().getName())){
Thread.currentThread().setName("----可爱黄牛----");
}
if(("pool-1-thread-2").equals(Thread.currentThread().getName())){
Thread.currentThread().setName("----女大学生----");
}
if(("pool-1-thread-3").equals(Thread.currentThread().getName())){
Thread.currentThread().setName("----一顿能吃2只猪的我----");
}
System.out.println(Thread.currentThread().getName()+"抢到了第"+(10-ticket)+"张票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}

被折叠的 条评论
为什么被折叠?



