转载文章:Tom_殇子
线程池
1.为什么使用线程池:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。
2.线程池使用方法:通过Executors创建四种线程池
1.newCachedThreadPool: 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。线程池的规模不存在限制。ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
2.newFixedThreadPool: 创建一个固定长度线程池,可控制线程最大并发数,超出的线程会在队列中等待。ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int型线程池长度参数);
3.newScheduledThreadPool: 创建一个固定长度线程池,支持定时及周期性任务执行。ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(int型线程池长度参数);
4.newSingleThreadExecutor: 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
3.线程怎么加入线程池:线程加入后相当于准备运行状态
1.cachedThreadPool、fixedThreadPool、singleThreadExecutor: submit(Runnable task) submit(Callable task)、submit(Runnable)
2.scheduledExecutorService: scheduleWithFixedDelay(Runnable a,intidelay:int型时间,delay:int型时间, TimeUnit.SECONDS)、scheduleAtFixedRate(Runnable a,delay:int型时间, TimeUnit.SECONDS)、schedule(Runnable a,delay:int型时间, TimeUnit.SECONDS)第一个方法表示整个定时间隔任务先延迟initdelay时间,后开始执行定时任务,再间隔delay时间执行定时任务,依次循环。后两个方法一样,相较于第一个方法区别是没有整个定时任务的初始延迟。
代码示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class NewThreadPool {
public static void main(String[] args) {
//创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。线程池的规模不存在限制。
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
//1- 在未来某个时间执行给定的命令。
// 该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。
cachedThreadPool.execute(() -> {//lambda表达式
{
System.out.println(index + ":" + Thread.currentThread().getName());
}
});
//2- 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
// 该 Future 的 get 方法在成功完成时将会返回给定的结果
cachedThreadPool.submit(() -> {
{
System.out.println(index + ":" + Thread.currentThread().getName());
}
});
}
cachedThreadPool.shutdown();//关闭线程池
System.out.println("newCacheThreadool结束");
//创建一个固定长度线程池,可控制线程最大并发数,超出的线程会在队列中等待。
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++) {
final int index = i;
//1- 在未来某个时间执行给定的命令。
// 该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。
fixedThreadPool.execute(() -> {
{
threadRunMethod(index);
}
});
//2- 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
// 该 Future 的 get 方法在成功完成时将会返回给定的结果
fixedThreadPool.submit(() -> {
{
threadRunMethod(index);
}
});
}
fixedThreadPool.shutdown();
System.out.println("newFixedThreadPool结束");
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
//testSchedule(scheduledExecutorService);
//testScheduleAtFixedRate(scheduledExecutorService);
testScheduleWithFixedDelay(scheduledExecutorService);
// 终止线程池
scheduledExecutorService.shutdown();
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("newSingleThreadExecutor: " + index);
Thread.sleep(2*1000);
} catch (Exception e) {
e.printStackTrace();
}
}
});*/
singleThreadExecutor.submit(new Runnable() {
@Override
public void run() {
try {
System.out.println("newSingleThreadExecutor: " + index);
Thread.sleep(2*1000);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
singleThreadExecutor.shutdown();
}
/**
*
* 跟 testScheduleAtFixedRate 非常类似,就是延迟的时间有点区别
* 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;
* 也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,
* 接着在 initialDelay + 2 * period 后执行,依此类推。
*
* 如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
* 推迟的时间 : 等到上次的任务执行完之后再延迟period 的时间后执行。
* @param scheduledExecutorService
*/
private static void testScheduleWithFixedDelay(ScheduledExecutorService scheduledExecutorService) {
scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
System.out.println("延迟2秒,再3秒执行一次"+Thread.currentThread().getName());
//如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
//本次任务执行完后下次的任务还需要延迟period时间后再执行
Thread.sleep(6*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},2,3, TimeUnit.SECONDS);
}
/**
* 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;
* 也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,
* 接着在 initialDelay + 2 * period 后执行,依此类推。
*
* 如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
* 推迟的时间 : 等到上次的任务执行完就立马执行。
* @param scheduledExecutorService
*/
private static void testScheduleAtFixedRate(ScheduledExecutorService scheduledExecutorService) {
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
System.out.println("延迟2秒,再3秒执行一次"+Thread.currentThread().getName());
//如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
//如果任务里面执行的时间大于 period 的时间,本次任务执行完后,下次任务立马执行。
Thread.sleep(6*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},2,3,TimeUnit.SECONDS);
}
/**
* 创建并执行在给定延迟后启用的一次性操作
* @param scheduledExecutorService
*/
private static void testSchedule(ScheduledExecutorService scheduledExecutorService) {
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("delay 3 seconds"+Thread.currentThread().getName());
}
}, 3, TimeUnit.SECONDS);
}
private static void threadRunMethod(int index) {
try {
System.out.println(index+":"+Thread.currentThread().getName());
Thread.sleep(1000);//因为创建线程是串行的,其他线程需要等待现有线程结束才开始创建运行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}