一、线程池简介
线程池就是预先创建好多n个空闲线程,节省了每次使用线程时都要去创建的时间,使用时只要从线程池中取出,用完之后再还给线程池。就像现在的共享经济一样,需要的时候只要去“借”,用完之后只需还回去就行。“池”的概念都是为了节省时间而创建的。
二、Executor
Java SE5增加了juc包来简化并发编程,而juc包中的Executor执行器来管理Thread对象。Executor在客户端和任务执行之间提供了一个间接层,与客户端直接执行任务不同,这个中介对象将执行任务。Executor允许我们管理异步执行的任务,而无须显示的管理线程的生命周期,是启动线程的优先选择。Executors 是Executor、ExecutorService、ScheduledExecutorService以及ThreadFactory、Callable的工厂及实用方法
三、线程池使用
1、newCachedThreadPool
newCachedThreadPool 会为每个任务都创建一个线程,如果有空闲线程的话也会重新使用空闲线程,如果线程没有被使用的话会在60s之后终止并从线程池中移除。它在回收旧线程时会停止创建新线程。
例子:
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int count = i;
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.printf("thread_%d_start%n" ,count);
}
});
}
executorService.shutdown(); //用完记得关闭,要不然会一直挂起
}
}
2、newFixedThreadPool
newFixedThreadPool 创建了一个固定数量的线程池,它重用了固定数量的线程操作一个无界队列,无论什么时候,它最多只能运行固定数量的运行中任务,当所有线程都处于活跃状态,如果有新的任务要添加进来的话,只能在队列中等待,直到有空闲线程。newFixedThreadPoll会一个运行,除非显示的调用shutdown方法。
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
final int count = i;
executorService.execute(() -> {
System.out.printf("thread_%d_start%n" ,count);
try {
Thread.sleep(3*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executorService.shutdown(); //用完要关闭线程池,要不然会一直悬挂
}
}
休眠3s 是为了更能看到效果,你会发现每次会先执行5个线程,结束之后会继续执行剩余5个。
3、newSingleThreadExecutor
newSingleThreadExecutor 使用单个线程操作了一个无界队列创建了一个Executor,它保证了任务执行的有序性。
如果向newSingleThreadExecutor中提交多个任务的话,每个任务都会保证在下个任务开始之前结束,所有的任务都将使用相同的线程
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int count = i;
executorService.execute(() -> {
System.out.printf("thread_%d_start%n" ,count);
try {
Thread.sleep(3*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executorService.shutdown(); //用完要关闭线程池,要不然会一直悬挂
}
}
执行结果:
线程按照指定顺序有序的执行,说明newSingleThreadExecutor在维护着一个有序队列。
4、newScheduledThreadPool
创建了一个能够延迟或周期性执行任务的线程池。
例子:
1、延时
public class ThreadPoolDemo {
public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 3; i++) {
final int count = i;
executorService.schedule(() -> {
try {
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread_"+count);
}, 3 , TimeUnit.SECONDS);
}
System.out.println("------------");
executorService.shutdown(); //用完要关闭线程池,要不然会一直悬挂
}
}
线程启动后 会延迟3s后再执行。
2、周期性执行
public class ThreadPoolDemo {
public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 3; i++) {
final int count = i;
executorService.scheduleAtFixedRate(() -> {
System.out.println("thread_"+count);
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 1 , 3 , TimeUnit.SECONDS);
}
System.out.println("------------");
// executorService.shutdown(); //用完要关闭线程池,要不然会一直悬挂
}
}
线程池不关闭,线程延迟1s执行后,会每隔3s周期性执行。
四、线程池的关闭
线程池关闭有两个方法:
它们的原理都是通过遍历线程池中的工作任务,然后通过调用线程的interrupt方法来中断线程,如果无法响应中断的任务可能永远无法关闭。
1、void shutdown():shutdown方法会将线程池状态设置为SHUTDOWN,然后中断线程池中没有正在执行的线程,新添加进来的任务将不会再被接受。
2、List<Runnable> shutdownNow():shutdownNow方法会将线程池状态设置为STOP,然后尝试停止正在执行或者暂停任务的线程,并返回等待执行任务的列表。