五种线程池的使用:
线程池的好处:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。
newFixedThreadPool:
创建固定线程数量的,适用于负载均衡的服务器,使用了无界队列
特点:
-
线程池中的线程处于一定的量,可以很好的控制线程的并发量
-
线程可以重复被使用,在显示关闭之前,都将一直存在
-
超出一定量的线程被提交时候需在队列中等待
使用示例:
public class FixedThreadPool {
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {//创建10个线程
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(index);
//打印线程名便于分析
System.out.println(Thread.currentThread().getName());
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
执行结果:
0
2
1
pool-1-thread-2
pool-1-thread-1
4
pool-1-thread-3
pool-1-thread-5
3
pool-1-thread-4
5
pool-1-thread-1
6
pool-1-thread-3
7
pool-1-thread-4
8
pool-1-thread-2
9
pool-1-thread-5
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待,而且可重复用线程
newSingleThreadExecutor:
特点:
- 创建单个线程,需要顺序保证执行任务,不会有多个线程活动,使用无界队列
- 如果线程池中工作的线程死亡(发生异常),那么会重新启动一个线程代替它完成剩下的任务。
使用示例:
public class SingleThreadExecutor {
public static void main(String[] args) {
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());
if(index==3)//如果是i就不可以,必须是final。
throw new RuntimeException();//这里扔出一个异常,然后后面的任务换了个线程继续执行。
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
结果:
0
pool-1-thread-1
1
pool-1-thread-1
2
pool-1-thread-1
3
pool-1-thread-1
Exception in thread "pool-1-thread-1" java.lang.RuntimeException
at com.lt.ch1.fivethreadpool.SingleThreadExecutor$1.run(SingleThreadExecutor.java:18)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
4
pool-1-thread-2
5
pool-1-thread-2
6
pool-1-thread-2
当发生异常的时候,后面的线程会替代前一个线程完成未完成的任务。
newCachedThreadPool:
会根据需要来创建新线程,执行很多短期异步任务的程序,使用了SynchronousQueue
特征:
- 线程池中的数量无限制,可达到最大值(Integer.Max_value)
- 终止并从缓存中移除60秒没有未被使用的线程
- 当线程池中没有可用线程,会自动创建一个线程
使用示例:
public class CachedThreadPool {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(index);
System.out.println(Thread.currentThread().getName());
}
});
}
}
}
结果:
0
pool-1-thread-1
1
pool-1-thread-1
2
pool-1-thread-1
3
pool-1-thread-1
4
pool-1-thread-1
5
pool-1-thread-1
6
pool-1-thread-1
7
pool-1-thread-1
8
pool-1-thread-1
9
pool-1-thread-1
线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
newScheduledThreadPool:
特征:
- 创建一个定长线程池,支持定时及周期性任务执行 需要定期执行周期任务,不建议使用Timer,ScheduledExecutorService更安全,功能更强大。
- 可以包含多个线程的,线程执行周期任务,适度控制后台线程数量的时候
- 对比newSingleThreadScheduleExecutor:只包含一个线程,只需要单个线程执行周期任务,保证顺序的执行各个任务
方法说明:
- Schedule:只执行一次,任务还可以延时执行,
- ScheduleAtFixedRate:是以固定频率来执行线程任务,固定频率的含义就是可能设定的固定时间不足以完成线程任务,但是它不管,达到设定的延迟时间了就要执行下一次了。
- scheduleWriteFixedDelay:不管线程任务的执行时间的,每次都要把任务执行完成后再延迟固定时间后再执行下一次。
使用示例:
public class ScheduleThreadPool {
public static void main(String[] args) {
ScheduledExecutorService scheduleThreadPool = Executors.newScheduledThreadPool(3);
for(int i=0;i<3;i++){
scheduleThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("schedule延迟了2秒");
}
}, 2000, TimeUnit.MILLISECONDS);
}
scheduleThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("scheduleAtFixedRate延迟5秒执行,每3秒执行一次");
}
}, 5000, 3000, TimeUnit.MILLISECONDS);
}
}
结果:
schedule延迟了2秒
schedule延迟了2秒
schedule延迟了2秒
scheduleAtFixedRate延迟5秒执行,每3秒执行一次
scheduleAtFixedRate延迟5秒执行,每3秒执行一次
scheduleAtFixedRate延迟5秒执行,每3秒执行一次
scheduleAtFixedRate延迟5秒执行,每3秒执行一次
它可安排在给定延迟后运行命令或者定期地执行。
newWorkStealingPool(JDK8以后):
特征:
- 基于ForkJoinPool实现。
- 会帮助其他线程完成任务
public class WorkStrealingPool {
public static void main(String[] args) throws IOException {
// 获取 CPU核心数
final int index = Runtime.getRuntime().availableProcessors();
System.out.println(index);
ExecutorService newWorkStealingPool = Executors.newWorkStealingPool();
//先让它运行一个任务比较长的线程。
newWorkStealingPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"The One");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//再执行几个任务时间比较短的线程
for(int i=0;i<index;i++){
newWorkStealingPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"i");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
//对主线程阻塞,从而拿到输出信息。
System.in.read();
}
}
结果:
4
ForkJoinPool-1-worker-1The One
ForkJoinPool-1-worker-3i
ForkJoinPool-1-worker-2i
ForkJoinPool-1-worker-0i
ForkJoinPool-1-worker-3i
最后那个3i线程是最后帮The One这个线程,通过阻塞主线程打印出来的线程名。最后如果感兴趣可以了解下ForkJoinPool。