线程的创建、启动、销毁等是一个非常消耗资源的过程。引出线程池。
目前线程池的共有六种创建方式。先来说明常见的四种创建方式,本质都是通过改变构造函数的参数来创建不同的线程的。最终调用的构造函数都是:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
以下以demo分别说明线程池的创建:
ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i=0;i<10;i++){
int temp = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+","+temp);
}
});
}
threadPool.shutdown();// 停掉线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
int temp = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+","+temp);
}
});
}
threadPool.shutdown();// 停掉线程池
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3);
threadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("我是定时线程,三秒后启动");
}
},3, TimeUnit.SECONDS); // 第一个参数是任务,第二个参数是时间长度,第三个参数时间单位
threadPool.shutdown();// 停掉线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("我是单线程线程");
}
});
}
threadPool.shutdown();// 停掉线程池
重点:几乎是面试必问的。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {}
corePoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTimea:线程空闲时间
unit:TimeUnit枚举类型的值,代表keepAliveTime时间单位,可以取下列值:
workQueue:阻塞队列,用来存储等待执行的任务,决定了线程池的排队策略,有以下取值:
threadFactory:线程工厂,是用来创建线程的
handler:线程拒绝策略。当创建的线程超出maximumPoolSize,且缓冲队列已满时,新任务会拒绝,有以下取值:
下图是线程池的执行流程:
流程说明:
用户提交任务,先到核心线程池,判断核心线程池是都已满;
如何核心线程池未满,线程任务执行;如何核心线程已满,走下一步;
进入线程缓存队列,判断缓存队列是否已满;
如果线程缓存队列已满,进入最大线程池;
如果最大线程池未满,创建线程任务;
如果最大线程池已满,则拒绝。
ExecutorService m = Executors.newWorkStealingPool(2);
1)创建一个带并行级别的线程池,并行级别决定了同一时刻最多有多少个线程在执行,如不传如并行级别参数,将默认为当前系统的CPU个数。
2)每个线程维护自己的一个队列,任务执行结束了,就自己主动去取别的任务。
3)产生的都是守护线程。
4)不是 ThreadPoolExecutor 的扩展,是 ForkJoinPool 的扩展。
5)演示:
public class Main {
public static void main(String[] args) throws Exception {
// 设置并行级别为2,即默认每时每刻只有2个线程同时执行
ExecutorService m = Executors.newWorkStealingPool(2);
for (int i = 1; i <= 10; i++) {
final int count=i;
m.submit(new Runnable() {
@Override
public void run() {
Date now=new Date();
System.out.println("线程" + Thread.currentThread() + "完成任务:"+ count+" 时间为:"+ now.getSeconds());
try {
Thread.sleep(1000);//此任务耗时1s
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
while(true){
//主线程陷入死循环,来观察结果,否则是看不到结果的
}
}
}
该线程池的思想是,把运用递归的思想,将大的任务拆分成小的任务(可以根据业务需求来控制拆分的粒度)。下面以一个面试题演示:一个长度100万的数组,元素是一百以内的随机数,将各个元素相加。
public class ForkJoin {
static int[] nums = new int[1000000];
static final int MAX_NUM = 50000;
private static Random random = new Random();
static {
for (int i = 0; i < nums.length; i++) {
nums[i] = random.nextInt(100);
}
System.out.println(Arrays.stream(nums).sum()); // 传统的方式计算
}
// 递归思想,不断将大任务分成小任务。
// RecursiveAction 无返回值;RecursiveTask 有返回值。
static class AddTask extends RecursiveAction{
int start,end;
AddTask(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected void compute() {
if(end-start<=MAX_NUM){
long sum = 0L;
for (int i = start; i < end; i++) {
sum += nums[i];
}
System.out.println("from+"+start+"to"+end+"="+sum);
}else{
int middle =start + (end-start)/2;
AddTask addTask = new AddTask(start,middle);
AddTask addTask2 = new AddTask(middle,end);
addTask.fork();
addTask2.fork();
}
}
}
public static void main(String[] args) throws IOException {
ForkJoinPool forkJoinPool = new ForkJoinPool();
AddTask addTask = new AddTask(0,nums.length);
forkJoinPool.execute(addTask);
System.in.read();
}
}
该线程池的优势是,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。
原文:https://www.cnblogs.com/xzy-/p/10928978.html