说明:本文对线程池的四种使用方式分别进行了实现,每种方式的详细介绍及关键点在代码的备注中
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//线程池的四种使用方式(说明及关键点在实现方法中):
//方式一:
newCachedThreadPool();
//方式二:
// fixdThreadPool(5);
//方式三:
// newScheduledThreadPool(6);
//方式四
// newSingleThreadExecutor();
}
//方式一:newCachedThreadPool
// 可缓存的线程池,该线程池中“没有核心线程,非核心线程的数量为无限大”,(核心线程在没用时候也不回收)
// 就是说当有需要时创建线程来执行任务,没有需要时“回收”线程,适用于耗时少,任务量大的情况。
//注意:线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
// 加上//****处那段代码就是体现此特点
private static void newCachedThreadPool() throws ExecutionException, InterruptedException {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 20; i++) {//线程池中线程数可以有无限大个
// //**** 线程先睡一秒,再执行。由于睡一秒的过程前面线程已经执行完毕,所以不用开启新的线程,直接复用前面线程即可。
// 所以才会重复用第一个线程
// final int index = i;
// try {
// Thread.sleep(index * 1000);
// }
// catch (InterruptedException e) {
// e.printStackTrace();
// }
//执行线程的方法1:调用runnableh中的run方法,此方法不向客户端返回执行结果
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("执行线程:" + Thread.currentThread().getName());
}
});
}
}
//方式二:fixdThreadPool
// 定长的线程池,“有核心线程,核心线程的即为最大的线程数量,没有非核心线程”
// 创建一个定长线程池,超出的线程会在队列中等待,即使线程空闲也不会回收。
//注意:1.此处线程池传入创建的线程有5个,但是有十个任务等待执行,先FIFO的顺序执行前五个线程,后五个在队列中等待。
// 前五个线程执行完毕后再按照FIFO的顺序执行。
// 2.定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。可参考PreloadDataCache。
private static void fixdThreadPool(int size) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(size);
for (int i = 0; i < 10; i++) {
//3.此处算法在线程执行直线进行睡眠,但是不会像newCatchThreadPool中那样重复在执行线程1,
// 因为这里线程直接是创建了固定大小的,直接用(不用也不会回收)
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//执行线程的方法2:调用Callable中的call方法,此方法可以向客户端返回执行结果
Future<Integer> task = executorService.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("执行线程:" + Thread.currentThread().getName());
return null;
}
});
System.out.println("第" + i + "次计算,结果:" + task.get());
}
}
//方式三:newScheduledThreadPool,创建一个定长的线程池,支持定时及周期性任务执行
private static void newScheduledThreadPool(int size) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(size);
Date d = new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(df.format(d));
//法一:schedule()延迟一定时间后执行任务
//参数1:为执行的任务;参数2:任务的延迟间隔;参数3:为延迟执行的时间单位:
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
Date d = new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(df.format(d));
System.out.println("执行线程:" + Thread.currentThread().getName());
}
}, 10, TimeUnit.SECONDS); //表示线程延迟十秒执行,任务只执行一次
//法二:scheduleAtFixedRate(...):按指定频率周期执行某个任务。如12:00执行第一次,15:00执行第二次,18:00执行第三次...
//(个人认为与法三的区别是:每个任务的开始时间是以一定周期开始的,后面任务开始时可能前面任务还未完成。
// 而法三则是在前面任务完成的时间点上延迟一定时间执行)
// 参数1:为执行的任务;参数2:初始任务延迟时间;参数3:执行的周期;参数4:为延迟执行的时间单位:
// scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
// @Override
// public void run() {
// Date d = new Date();
// SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// System.out.println(df.format(d));
// System.out.println("执行线程:"+Thread.currentThread().getName());
// }
// },2,3,TimeUnit.SECONDS);//初始化延迟2s开始执行,每隔3s重新执行一次任务。
//上面如果当前时间是10点,设置initialDelay:2,period:3,unit:小时
//第一次:10:00 + 2 = 12:00
//第二次:10:00 + 2 + 3 =15:00
//第三次:10:00 + 2 + 2*3 =18:00
//第n次:10:00 + 2 + n*3...
//法三:scheduleWithFixedDelay(...):按指定频率间隔执行某个任务
// 参数1:为执行的任务;参数2:为初始延迟时间;参数3:后续任务的延迟执行时间;参数4:为延迟执行的时间单位:
// 解释:
// 第一次调度开始时间点=当前时间+initialDelay
// 下一次调度开始时间点=上一次task完成时间点+delay
// scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
// @Override
// public void run() {
// Date d = new Date();
// SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// System.out.println(df.format(d));
// System.out.println("执行线程:"+Thread.currentThread().getName());
// }
// },5,3,TimeUnit.SECONDS);//初始化延迟5s开始执行,后续任务开始时间是在其前一个任务结束后延迟2s重新执行。
}
//方式四:newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,
// 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
private static void newSingleThreadExecutor() {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index=i;
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println("任务:"+index);
System.out.println("执行线程:"+Thread.currentThread().getName());
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
}