import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 第4种获得使用多线程的方式 线程池
* Executors.newFixedThreadPool(5):
* 1 创建一个定长线程池,可控线程最大并发数,超出的线程会在队列种等待。
* 2 newFixedThreadPool创建的线程池的corePoolSize和maximumPoolSize相等,使用LinkedBlockingQueue。
* 3 执行长期任务,性能好。
* Executors.newSingleThreadExecutor():
* 1 创建一个单线程的线程池,只有唯一一个线程来执行任务,保证所有任务按照指定顺序执行。
* 2 newSingleThreadExecutor创建的线程池的corePoolSize和maximumPoolSize都为1,使用LinkedBlockingQueue。
* 3 一个任务一个任务执行场景。
* Executors.newCachedThreadPool():
* 1 创建一个可缓存线程池,如果线程池长度超过处理需求,可灵活回收空闲线程,若无可回收,则新建线程。
* 2 newCachedThreadPool的corePoolSize为0,maximumPoolSize为Integer.MAX_VALUE(21亿),使用SynchronousQueue,来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。
* 3 执行很多短期异步的小程序或者负载较轻的服务器。
*/
/**
*线程池的7大参数:
* 1 corePoolSize:线程池中的常驻核心线程数
* 2 maximumPoolSize:线程池能容纳的同时执行的最大线程数,此值必须大于等于1。
* 3 keepAliveTime:多余的空闲线程的存活时间。当线程池数量超过corePoolSize时,当空闲时间达到keepAliveTime值时,多余空闲线程会被销毁直到仅仅剩下corePoolSize个线程为止。
* 4 TimeUnit:keepAliveTime的单位
* 5 BlockingQueue<Runnable>:任务队列,被提交但未被执行的任务
* 6 ThreadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程,一般用默认即可。
* 7 RejectedExecutionHandler:拒绝策略。表示当队列满了并且工作线程大于等于线程池的最大线程数maximumPoolSize时如何来拒绝请求执行的runnable的策略。
*
*/
/**
* 线程池的工作原理:
* 1 在创建线程池h后,等待提交过来的任务请求。
* 2 当调用execute()方法添加一个任务请求时,线程池会做如下判断:
* 1如果正在运行的线程数小于corePoolSize,那么马上创建线程运行这个任务;
* 2如果正在运行的线程数大于或者等于corePoolSize,那么将这个任务放入队列;
* 3如果这时队列满了且正在运行的线程数小于maximumPoolSize,那么创建非核心线程运行这个任务;
* 4如果这时队列满了且正在运行的线程数大于或者等于maximumPoolSize,那么线程池会启用饱和拒绝策略来执行。
* 3 当一个线程完成任务时,它会从队列中取下一个任务来执行。
* 4 当一个线程无事可做超过keepAliveTime时,线程池会判断:
* 如果正在运行的线程数大于corePoolSize,那么这个线程会被停掉。
* 线程池所有的任务完成后,线程池收缩到corePoolSize的大小。
*
*/
/**
*JDK内置的拒绝策略RejectedExecutionHandler:
* (拒绝依据:超过maximumPoolSize+BlockingQueue的长度)
* 1 ThreadPoolExecutor.AbortPolicy(默认):直接抛出RejectedExecutionException异常,阻止系统正常运行。
* 2 ThreadPoolExecutor.CallerRunsPolicy:返回给调用者运行。不抛出异常,不抛弃任务,把任务返回给调用者。
* 3 ThreadPoolExecutor.DiscardOldestPolicy:抛弃队列中等待最久的任务。然后把当前任务加入到队列中尝试再次提交当前任务。
* 4 ThreadPoolExecutor.DiscardPolicy:直接丢弃任务,不予任何处理,不抛出异常。如果允许任务丢失,这是最好的解决方案。
*以上内置拒绝策略均实现了RejectedExecutionHandler接口。
*
*
*/
/**
* 工作中3个线程池都不用,需要手写ThreadPoolExecutor的原因:
* (线程资源必须通过线程池提供,不允许在应用中自行显示创建线程:使用线程池的好处是减少创建和销毁线程时消耗的时间以及系统资源的开销,解决资源不足问题。
* 如果不食用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题)
* 1 newFixedThreadPool和newSingleThreadExecutor: 允许请求队列LinkedBlockingQueue默认的最大长度Integer.MAX_VALUE(21亿),堆积大量请求,导致OOM
* 2 newCachedThreadPool和ScheduledThreadPool:允许创建线程为最大2Integer.MAX_VALUE(21亿),创建大量线程,导致OOM。
*
*/
/**
* 合理配置线程池:
* 1 CPU密集型:CPU核数Runtime.getRuntime().availableProcessors()+1个线程的线程池
* CPU密集型指该任务需要大量的运算,没有阻塞,CPU一直全速运行。实际受限于cpu总运算能力
* 2 IO密集型:cpu核数/(1-阻塞系数) 阻塞系数0.8-0.9之间 例如:8核 8/(1-0.9)=80个线程数
* 第一种情况:线程并非一直执行任务,应尽可能配置多的线程,cpu核数*2;
* 第二种情况:任务需要大量的IO,即大量的阻塞。在单线程上运行IO密集型任务导致浪费大量的cpu运算能力,浪费在等待。
* 所以在IO密集型任务中使用多线程可大大加速程序运行,即时在单核cpu上,加速主要是利用了被浪费掉的阻塞时间。大部分线程被阻塞,多配置线程数。
*
*
*
*/
public class MyThreadPoolDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(5); //一池5个处理线程
//ExecutorService executorService = Executors.newSingleThreadExecutor(); //1池一个处理线程
//ExecutorService executorService = Executors.newCachedThreadPool(); //一池1个处理线程,但是可以自动扩展线程的数量
try {
for (int i = 1; i <= 10; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"\t 办理业务。");
}
});
//暂停一会线程
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}