JUC学习之线程池的使用

#JUC学习之线程池的使用


##为什么要使用线程池
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,那有没有方法可以使线程复用呢?就像数据库连接池一样,在Java中可以通过线程池来达到这样的效果。线程池能有效的处理多个线程的并发问题,避免大量的线程因为互相强占系统资源导致阻塞现象,能够有效的降低频繁创建和销毁线程对性能所带来的开销。


##线程池的好处
1、降低资源消耗;
2、提高响应速度;
3、提高线程的可管理性。


##线程池的核心-ThreadPoolExecutor类
java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。接下来看一下线程池的一个demo,根据demo深入学习ThreadPoolExecutor的源码:
线程类

/**
 * 线程执行
 * @author meng
 *
 */
public class MyRunnable implements Runnable {
 
	// 正在执行的任务数
	private int num;
 
	public MyRunnable(int num) {
		this.num = num;
	}
 
	@Override
	public void run() {
		System.out.println("正在执行的MyRunnable " + num);
		try {
			Thread.currentThread().sleep(4000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("MyRunnable " + num + "执行完毕");
 
	}
 
}

线程池

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
 
/**
 * 线程池
 * @author meng
 *
 */
public class ThreadPoolExecutorTest {
 
	//池中所保存的线程数,包括空闲线程。
	final static int corePoolSize = 5;
	//池中允许的最大线程数。
	final static int maximumPoolSize = 10;
	//当线程数大于核心线程时,此为终止前多余的空闲线程等待新任务的最长时间
	final static long keepAliveTime = 200;
	//执行前用于保持任务的队列5,即任务缓存队列
	final static ArrayBlockingQueue<Runnable> workQueue =new ArrayBlockingQueue<Runnable>(5);
	
	public static void main(String[] args) {
		//构建一个线程池,正常线程数量为5,最大线程数据为10,等待时间200
		ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
				corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MINUTES,	workQueue);
		
		//线程池去执行15个任务
		for (int i = 0; i < 15; i++) {
			MyRunnable myRunnable = new MyRunnable(i);
			threadPoolExecutor.execute(myRunnable);
			System.out.println("线程池中现在的线程数目是:"+threadPoolExecutor.getPoolSize()+",  队列中正在等待执行的任务数量为:"+
					threadPoolExecutor.getQueue().size());
		}
		//关掉线程池
		threadPoolExecutor.shutdown();
		
	}
	//文章最后附赠另一种线程池配置方法
}

执行结果

线程池中现在的线程数目是:1,  队列中正在等待执行的任务数量为:0
正在执行的MyRunnable 0
线程池中现在的线程数目是:2,  队列中正在等待执行的任务数量为:0
线程池中现在的线程数目是:3,  队列中正在等待执行的任务数量为:0
正在执行的MyRunnable 1
线程池中现在的线程数目是:4,  队列中正在等待执行的任务数量为:0
线程池中现在的线程数目是:5,  队列中正在等待执行的任务数量为:0
线程池中现在的线程数目是:5,  队列中正在等待执行的任务数量为:1
线程池中现在的线程数目是:5,  队列中正在等待执行的任务数量为:2
正在执行的MyRunnable 2
线程池中现在的线程数目是:5,  队列中正在等待执行的任务数量为:3
线程池中现在的线程数目是:5,  队列中正在等待执行的任务数量为:4
线程池中现在的线程数目是:5,  队列中正在等待执行的任务数量为:5
正在执行的MyRunnable 3
线程池中现在的线程数目是:6,  队列中正在等待执行的任务数量为:5
线程池中现在的线程数目是:7,  队列中正在等待执行的任务数量为:5
正在执行的MyRunnable 10
正在执行的MyRunnable 4
线程池中现在的线程数目是:8,  队列中正在等待执行的任务数量为:5
线程池中现在的线程数目是:9,  队列中正在等待执行的任务数量为:5
正在执行的MyRunnable 12
线程池中现在的线程数目是:10,  队列中正在等待执行的任务数量为:5
正在执行的MyRunnable 11
正在执行的MyRunnable 13
正在执行的MyRunnable 14

从执行结果来看,当线程池中线程的数量大于5时,便将任务放入任务缓存队列里面,任务缓存队列满了以后,变创建新的线程,来执行,如果让任务数量大于15,程序就会报错(因为设置的最大线程数是10,队列设置的5,也就是可以等待的任务数量是5,所以该线程池最大可接受15个线程并发),抛出任务拒绝异常,这时可以增大任务缓存队列,有兴趣的同学可以去试试。
接下来我们看一下ThreadPoolExecutor的源码:

public class ThreadPoolExecutor extends AbstractExecutorService {
    .....
    //在ThreadPoolExecutor类中提供了四个构造方法:
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
        BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
    ...
}

构造方法中的参数详解:

  • **corePoolSize:**核心池的大小,默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;

  • **maximumPoolSize:**线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;

  • **keepAliveTime:**表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

  • **unit:**参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性

  • **workQueue:**一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:

ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;

ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。

  • **threadFactory:**线程工厂,主要用来创建线程;
  • **handler:**表示当拒绝处理任务时的策略,有以下四种取值:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值