Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或者并发执行任务的程序都可以使用线程池。
1.线程池优点:
1)降低资源消耗:通过重复利用已创建的线程降低线程创建于销毁带来的损耗
2)提高相应速度:当任务到达时,无需等待线程创建就可以立即执行
3)提高线程的可管理性:使用线程池可以统一进行线程分配、调度与监控
2.线程池的实现原理
线程池的主要处理流程如下:
1)判断核心线程池是否已满,如果未满,创建一个新的线程来执行任务。如果已满,判断是否有空闲线程,有的话将任务分配给空闲线程,否则,执行步骤2。(corePoolSize)(创建线程需要获得全局锁)
2)判断工作队列(阻塞队列BlockingQueue)是否已满。如果工作队列未满,将任务存储在工作队列中等待空闲线程调度。如果工作队列已满,执行步骤3。(BlockingQueue)
3)判断当前线程池的线程数量是否已达到最大值,如果没有达到最大值,则创建新的线程来执行任务。(创建线程需要获取全局锁)否则执行步骤4。(MaxPoolSzie)
4)调用饱和策略来处理此任务。(RejectPolicy)
3.线程池的使用
- 手工创建一个线程池
通过new一个ThreadPoolExcetor就可以实现自定义线程池
例:
public threadPoolExcetor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable>workQueue,
RejectedExcecutionHandler handler)
1)coolPoolSize(核心线程池大小):当提交一个任务到线程池时,只要核心线程池为达到corePoolSize,创建新线程来执行任务。
preStartAllCoreThreads( ):线程池会提前创建并启动所有核心线程。
2)BlockingQueueworkQueue(任务队列):保存等待执行任务的阻塞队列。
- ArrayBlockingQueue:基于数组的有界阻塞队列,按照FIFO原则对元素进行排序。
- LinkedBlockingQueue:基于链表的无界阻塞队列,按照FIFO排列元素。吞吐量要高于ArrayBlockingQueue.
(内置线程池newFixedThreadPool-固定大小线程池就采用此队列。) - synchronousQueue:一个不存储元素的阻塞队列(无界队列)。每个插入操作需要等待另一个线程移除操作,否则插入操作一直处于阻塞状态。
吞吐量通常高于LinedBlockingqueue。
内置线程池newCachedThreadPool-缓存线程池就采用此队列。
- PriorityBlockingQueue:具有优先级的阻塞队列。3)maximumPoolSize(线程池最大数量)
4)keepAliveTime(线程保持活动的时间):线程池的工作线程空闲后保持存活的时间。任务比较多,并且每个任务执行的时间比较短,可以调大此参数来提高线程利用率。
5)RejectedExcecutionHandler(饱和策略)
AbortPolicy:无妨处理新任务抛出异常(JDK默认采用此策略)
CallerRunsPolicy:使用调用者所在线程来处理任务
DisCardOldestPolicy:丢弃队列中最近的一个任务并执行当前任务DisCardPolicy:不处理任务,丢弃任务,也不报异常
4.向线程池提交任务
可以使用俩个方法向线程池提交任务,分别为execute()和submit()方法。
execute()方法用于提交不需要返回值的任务,座椅无法判断任务是否被线程池执行成功。
eg:使用execute()方法:
class MyThreadExecute implements Runnable{
@Override
public void run() {
for (int i = 0;i < 50;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
public class TestExecute {
public static void main(String[] args) {
MyThreadExecute myThreadExecute = new MyThreadExecute();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3,5,2000,TimeUnit.MILLISECONDS,new LinkedBlockingDeque<Runnable>());
for (int i = 0;i < 5;i++){
threadPoolExecutor.execute(myThreadExecute);
}
}
}
submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过future对象可以判断任务是否执行成功,并且可以通过future的
get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法会阻塞当前线程一段时间后立即返回,这时候有可能任务没有完成。
eg:
class CallableThread implements Callable<String>{
@Override
public String call() throws Exception {
for (int i = 0;i < 50 ;i++){
System.out.println(Thread.currentThread().getName()+","+ i);
}
return Thread.currentThread().getName()+"执行完毕";
}
}
public class TestSubmit {
public static void main(String[] args) {
CallableThread callableThread = new CallableThread();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3,5,2000,
TimeUnit.MILLISECONDS,new LinkedBlockingDeque<Runnable>());
for (int i = 0; i < 5;i++){
Future<String> future = threadPoolExecutor.submit(callableThread);
try {
String str = future.get();
System.out.println(str);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}