线程池详解

一、为什么要使用线程池,线程池有什么用?

  • 降低资源消耗:通过重用已经创建的线程来降低线程创建和销毁的消耗
  • 提高响应速度:任务到达时不需要等待线程创建就可以立即执行
  • 提高线程的可管理性:线程池可以统一管理、分配、调优和监控                                

 二、   Java中创建线程池有以下的方式:

1、使用ThreadPoolExecutor类(java.util.concurrent包)

//创建ThreadPoolExecutor线程池对象
 ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,keepAliveTime,milliseconds,runnableTaskQueue, threadFactory,handler);

2、使用Executors类创建四种常见线程池

  1. newCachedThreadPool (可缓存线程的线程池)创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。即根据需要创建新线程的线程池
  2. newFixedThreadPool(固定大小线程池) 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。即创建一个固定数量线程的线程池
  3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
  4. newSingleThreadExecutor (单线程线程池)创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。即创建只包含一个线程的线程池。

三、线程池常用参数:

1.corePoolSize 线程池核心线程大小

核心线程会一直存在,即使没有任务执行;

当线程数小于核心线程数的时候,即使有空闲线程,也会一直创建线程直到达到核心线程数;

设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭。

2.maximumPoolSize 线程池最大线程数量

线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。

3.keepAliveTime 空闲线程存活时间

 一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指 定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定

4.unit 空闲线程存活时间单位

5.workQueue 工作队列

       新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。是指被提交但未被执行的任务所存放的队列容器

  1. 直接提交队列:SynchronousQueue是没有容量的容器,每一个插入的操作都需要等待相应的删除操作,SynchronousQueue不保存任务,它总是马上将任务提交给线程执行,如果没有空闲的线程则会尝试创建新的线程,如果线程数量已经达到最大值,则执行拒绝策略,使用SynchronousQueue通常需要设置很大的maximumPoolSize。
  2. 有界的任务队列: 当使用有界队列并有新任务时,若然线程池线程数量小于corePoolSize则会创建线程,若然大于corePoolSize则会将新任务加入任务队列,当任务队列已满无法加入时,则在总线程数不大于maximumPoolSize的前提下创建线程,若大于maximumPoolSize则执行拒绝策略,使用有界队列除非系统非常繁忙,否则确保核心线程数在corePoolSize
  3. 无界的任务队列: LinkedBlockingQueue 若任务创建和处理速度差异很大,无界队列会快速膨胀导致系统资源耗尽 
  4. 优先任务队列: PriorityBlockingQueue是一个特殊的无界队列,创建PriorityBlockingQueue时可以传入Comparator对任务进行优先级处理

6.threadFactory 线程工厂

创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。

7.handler 拒绝策略

当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略:

  • AbortPolicy: 丢弃任务并抛出RejectedExecutionException 异常。 (默认)
  • DiscardPolicy:也是丢弃任务,但是不抛出异常。
  • DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
  • CallerRunsPolicy:由调用线程处理该任务 

 

四、执行过程

1.当线程数小于核心线程数时,创建线程。

2.当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。

线程调用runWoker,会while循环调用getTask方法从workerQueue里读取任务,然后执行任务。只要 getTask方法不返回null,此线程就不会退出。

3.当线程数大于等于核心线程数,且任务队列已满。(1)若线程数小于最大线程数,创建线程。(2)若线程数等于最大线程数,抛出异常,拒绝任务。

 

 

五、shutdown与shutdownNow  的区别?

调用shutdown,池中状态立马变成SHUTDOWN状态,此时再往池中添加任务,会触发拒绝策略。此时池中不会立刻退出,直到池中的任务都已经完成,才会退出,平缓的关闭。调用shutdown方法,将线程池中的空闲线程回收。该方法会使得keepAliveTime参数失效

调用shutdownNow,池中状态立马变成STOP状态,并试图停止所有正在执行的线程(除非有if判断人为的抛出异常),不再处理还在池队列中等待的任务,会返还未执行的任务。暴力关闭

 

六、如何判断线程池中线程全部执行完毕?

awaitTermination方法:接收人timeout和TimeUnit两个参数,用于设定超时时间及单位。当等待超过设定时间时,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。一般情况下会和shutdown方法组合使用

 //等待所有线程执行完毕当前任务。
            threadPool.shutdown();
            boolean loop = true;
            do {
                //等待所有线程执行完毕当前任务结束
                loop = !threadPool.awaitTermination(2, TimeUnit.SECONDS);//等待2秒
            } while (loop);
            if (loop != true) {
                System.out.println("所有线程执行完毕");
            }

 

七、简单实例

 1.使用ThreadPoolExecutor类创建

public class TestThreadPoolExecutor {
    public static void main(String[] args) {
 
        long currentTimeMillis = System.currentTimeMillis();//计算耗时
 
        // 构造一个线程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 6, 3,TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3));
 
        for (int i = 1; i <= 10; i++) {//任务数量。具体项目可能是批量过来的数据
            try {
                String task = "task=" + i;
                System.out.println("创建任务并提交到线程池中:" + task);
                threadPool.execute(new ThreadPoolTask(task));  //提交
 
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
 
        try {
            //等待所有线程执行完毕当前任务。
            threadPool.shutdown();
 
            boolean loop = true;
            do {
                //等待所有线程执行完毕当前任务结束
                loop = !threadPool.awaitTermination(2, TimeUnit.SECONDS);//等待2秒
            } while (loop);
 
            if (loop != true) {
                System.out.println("所有线程执行完毕");
            }
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("耗时:" + (System.currentTimeMillis() - currentTimeMillis));
        }
 
 
    }
}
public class ThreadPoolTask implements Runnable, Serializable {
 
    private Object attachData;
 
    ThreadPoolTask(Object tasks) {    
        this.attachData = tasks;
    }
 
    public void run() {//执行逻辑
 
        try {
 
            System.out.println("开始执行任务:" + attachData + "任务,使用的线程池,线程名称:" + Thread.currentThread().getName());
 
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        attachData = null;
    }
 
}

2.使用Executors类创建

public class testThreadPool {
	
	public static void main(String[] args) {
        long currentTimeMillis = System.currentTimeMillis();//计算耗时
		  ExecutorService executorPool=Executors.newFixedThreadPool(10);
			for (int i = 1; i <=100; i++) {
				String task = "task=" + i;
                System.out.println("创建任务并提交到线程池中:" + task);
                executorPool.execute((Runnable) new testThreadPoolTask(task));
			}
			
			try {
	            //等待所有线程执行完毕当前任务。
				executorPool.shutdown();
	 
	            boolean loop = true;
	            do {
	                //等待所有线程执行完毕当前任务结束
	                loop = !executorPool.awaitTermination(2, TimeUnit.SECONDS);//等待2秒
	            } while (loop);
	 
	            if (loop != true) {
	                System.out.println("所有线程执行完毕");
	            }
	            
	        } catch (InterruptedException e) {
	            e.printStackTrace();
	        } finally {
	            System.out.println("耗时:" + (System.currentTimeMillis() - currentTimeMillis));
	        }
	}
 
}

 

public class testThreadPoolTask implements Runnable{

	  private Object attachData;
	  
	  testThreadPoolTask(Object tasks) {   //获取传递的参数
	        this.attachData = tasks;
	    }
	@Override
	public void run() {
		   try {
			   
	            System.out.println("开始执行任务:" + attachData + "任务,使用的线程池,线程名称:" + Thread.currentThread().getName());
	 
	            
	        } catch (Exception e) {
	            e.printStackTrace();
	        }
	        attachData = null;
		
	}

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值