多线程,Executor线程池
创建线程的三种方式
继承Thread类
/**
* @author : fzz
*/
public class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
new MyThread().start();
}
}
实现Runnable接口
最常用的,因为java单一继承原则
/**
* @author : fzz
*/
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args){
new Thread(new MyRunnable()).start();
}
}
实现Callable接口
和Runnable功能相似,Callable的call又返回值,可以返回给客户端,Runnable没有,一般情况下,Callable与FutureTask一起使用,或者通过线程池的submit方法返回相应的Future
Future就是对具体的Runnable或者Callable任务的执行结果进行取消,是否完成,获取结果,设置结果操作,其中get()方法会阻塞,直到任务返回结果
FutureTask则是一个RunnableFuture,而RunnableFuture实现了Runnable和Future接口
/**
* @author : fzz
*/
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000L);
return "fzz";
}
public static void main(String[] args) throws Exception {
MyCallable myCallable = new MyCallable();
FutureTask<Object> objectFutureTask = new FutureTask<Object>(myCallable);
new Thread(objectFutureTask).start();
//调用get方法会阻塞,直到任务又返回值
System.out.println(objectFutureTask.get());
}
}
/**
* 线程池Demo
* @author : fzz
*/
public class ThreadPoolDemo {
public static void main(String[] args) throws Exception{
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,30,3L, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
Future<String> submit = null;
for (int i = 0; i < 100; i++) {
submit = threadPoolExecutor.submit(new CallableDemo());
}
for (int i = 0; i < 100; i++) {
System.out.println(submit.get());
}
// 为了程序自动执行完成,这里关闭线程池
threadPoolExecutor.shutdown();
}
}
线程池(Executor)
为什么需要使用线程池:
程序面对处理大量短小任务,而每个任务都会创建一个新的线程处理,频繁的创建,销毁线程;给系统带来的开销是非常大的。
线程池为线程生命周期开销问题,和资源你不足问题提供了解决方案。通过对多任务重用线程,消除了创建线程开销;当任务数据达到某个阈值是,强制等待,从而防止资源不足。
风险和机遇
使用线程池,程序容易遭受所有的并发风险,诸如:同步错误,死锁,资源不足,线程泄漏等。
线程池的创建
/**
* 线程池Demo
* @author : fzz
*/
public class ThreadPoolDemo {
public static void main(String[] args) throws Exception{
// LinkedBlockingQueue不设置参数,默认为Integer最大值
LinkedBlockingQueue<Runnable> runnables = new LinkedBlockingQueue<>();
// 初始化时,向任务队列里面添加任务
for (int i = 0; i < 100; i++) {
runnables.put(()->{
System.out.println(Thread.currentThread().getName());
});
}
/**
* @param corePoolSize 保留线程池中等待线程数,即使空闲
* @param maximumPoolSize 线程池中最大线程数
* @param keepAliveTime 当线程数大于保留等待线程数时,超过此时间就会被回收
* @param unit keepAliveTime参数的时间单位
* @param workQueue 保留任务队列,保留由线程池提交的任务
* @param threadFactory 线程创建工厂
* @param handler 当任务线程达到线程池界限时(线程阻塞时),处理方式(拒绝策略)
*/
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,30,3L, TimeUnit.SECONDS,runnables);
/**
* 通过new创建线程池时,除非调用prestartAllCoreThreads() 方法初始化核心线程,否则此时线程池中有0个线程,
* 即使工作队列中存在多个任务,也不会执行。
*/
threadPoolExecutor.prestartAllCoreThreads();
/**
* 线程池运行测试(需要设置LinkedBlockingQueue参数)查看效果
* 注意:当任务数(X) < 核心线程数(cSize) 时,线程池只启动 X 线程
* 当 任务数(X) >= 核心线程数(cSize) 且 X < 任务队列数(work) + cSize 时,
* 回启动 <= cSize 个线程,其他任务在任务队列里
* 当 任务数(X) > 核心线程数(cSize) 且 X > 任务队列数(work) + cSize
* X - 任务队列数(work) <= 最大线程数(mSize) 时,会启动 X - work 个线程
* X - 任务队列数(work) > 最大线程数(mSize) 时,会启动 mSize 个线程,其余用拒绝策略处理
*
*/
for (int i = 0; i < 100; i++) {
threadPoolExecutor.submit(()->{
System.out.println(Thread.currentThread().getName());
});
}
// Future测试
Future<String> submit = null;
for (int i = 0; i < 100; i++) {
submit = threadPoolExecutor.submit(new CallableDemo());
}
for (int i = 0; i < 100; i++) {
System.out.println(submit.get());
}
// 为了程序自动执行完成,这里关闭线程池
threadPoolExecutor.shutdown();
}
}
拒绝策略
- AbortPolicy:该策略直接抛出异常,阻止系统正常工作。
- CallerRunsPolicy:只要线程池没有关闭,该策略直接在调用者线程中执行(直接调用run()方法)
- DiscardPolicy:直接啥都不干,把任务丢弃
- DiscardOldestPolicy:丢弃最老的一个请求(队列中第一个),再尝试提交任务。
/**
* 自定义拒绝策略
* @author : fzz
*/
public class CustomPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 具体业务
System.out.println("线程池满了!");
}
public static void main(String[] args){
// LinkedBlockingQueue不设置参数,默认为Integer最大值
LinkedBlockingQueue<Runnable> runnables = new LinkedBlockingQueue<>(10);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,20,3L, TimeUnit.SECONDS,runnables,new CustomPolicy());
for (int i = 0; i < 100; i++) {
threadPoolExecutor.submit(()->{
System.out.println(Thread.currentThread().getName());
});
}
}
}
submit和execute区别
submit:
如果发生异常,不会立即抛出,而是在get的时候,在抛出
execute:
直接抛出异常
总结:
编码过程中,发生异常要必须处理;方便错误定位。
xml中线程池配置
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 线程池维护线程的最少数量 -->
<property name="corePoolSize" value="1"/>
<!-- 允许的空闲时间 -->
<property name="keepAliveSeconds" value="200"/>
<!-- 线程池维护线程的最大数量 -->
<property name="maxPoolSize" value="20"/>
<!-- 缓存队列 -->
<property name="queueCapacity" value="40"/>
<!-- 对拒绝task的处理策略 -->
<property name="rejectedExecutionHandler">
<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
</property>
</bean>
java配置类
yml文件
spring:
taskPool:
corePoolSize: 1
maxPoolSize: 8
keepAliveSeconds: 60
queueCapacity: 60
java配置
/**
* 线程池
* @author fzz
* 2019年2月16日
*/
@Configuration
@EnableAsync
public class TaskExecutePool {
@Value("${spring.taskPool.corePoolSize}")
private int corePoolSize = 1;
@Value("${spring.taskPool.maxPoolSize}")
private int maxPoolSize = 8;
@Value("${spring.taskPool.keepAliveSeconds}")
private int keepAliveSeconds = 60;
@Value("${spring.taskPool.queueCapacity}")
private int queueCapacity = 100;
@Bean(name="taskExecutor")
public Executor taskAsyncPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);//初始线程大小
executor.setMaxPoolSize(maxPoolSize);//最大线程大小
executor.setQueueCapacity(queueCapacity);//设置队列长度
executor.setKeepAliveSeconds(keepAliveSeconds);//设置多长时间,线程回收
// 当pool已经达到max size的时候,不在新线程中执行任务,而是由调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}