线程池——底层逻辑
创建一个线程,是需要一定开销的;因为需要在内存中分配很多必要的环境;因此创建一个就丢掉在高并发环境下将频繁的发生,稀贵资源的频繁创建与丢弃;因此如果能提前创建好,并实现线程的复用,那将有利于提高性能;但是注意线程池中线程建的越多带来的弊端就是,内存负载增大;而如果你过多的创建了线程远大于你的cpu,实际上就反而造成了内存浪费,因为再多线程已经调度不过来了,倒不如直接拒绝掉,所以在线程定义中针对这些情况,我们可以配置线程参数来达到多方面平衡的目的;
核心线程:线程在初始化期间创建出来,这样使用完了扔给下一个可以重用;开销少,但会占用内存;
临时线程:线程用完了就扔掉,每次请求都会新建一个线程;开销大,但是节约内存;
线程池分类:周期、临时、定长;
线程池核心:队列、Wokder装饰器
对象池思想:池属性装饰器-引用-清洗
线程池应用:线程池一般只创建一次;但是执行submit、execute方法可以执行多次来创建多个线程;
线程池——线程池类型
单线程池: ExecutorService executor=Executors.newSingleThreadExecutor();//串行执行线程,保证了访问顺序
定长线程池: ExecutorService executor=Executors.newFixedThreadPool(5);//--->全是核心!超过则阻塞队列,空闲则重用
缓存型池: ExecutorService executor=Executors.newCachedThreadPool();//--->没有核心线程,全是临时线程;周期性短的异步任何可以用这个,超时移除
定时线程池:ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
Runnable task = new Runnable() {
public void run() {
System.out.println("HeartBeat.........................");
}
};
executor.scheduleAtFixedRate(task, 5, 3, TimeUnit.SECONDS); //5秒后第一次执行,之后每隔3秒执行一次
线程池——自定义线程池
基础定义
时间和空间的选择;
new ThreadPoolExecutor(
int corePoolSize,//核心线程池大小,核心线程池内及时没任务也不会清除,加入阻塞队列
int maximumPoolSize,//总体线程池大小(核心+临时不能大于这个数,不然直接拒绝);
long keepAliveTime,//临时线程存货时间
TimeUnit unit,//时间的单位
BlockingQueue<Runnable> workQueue,//超过线程池大小的时的线程会被放到这里队列内,由外部传入
);
时间策略单位:
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒
应用举例
这种是继承实现,也可以用嵌入法实现
public class MyExcutorServcie extends ThreadPoolExecutor{
public MyExcutorServcie(){
super(
1,//核心线程
1,//总共线程
1,//临时线程存货时间
TimeUnit.MINUTES,//存活的时间单位
new SynchronousQueue<Runnable>(),//阻塞队列
new ThreadPoolExecutor.DiscardPolicy()//丢失任务时,抛出异常
);
};
}
public static void main(String[] args) {
MyExcutorServcie myExcutorServcie=new MyExcutorServcie();
myExcutorServcie.execute(new Runnable() {
@Override
public void run() {
System.out.println("Hello word");
}
});
}
线程池——加入任务方式
Runnable用法
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
myExcutorServcie.execute(new Runnable() {
@Override
public void run() {
System.out.println("Hello word");
}
});
}
Callable用法
public static void main(String[] args) {
List<Future<String>> resultList=new ArrayList<Future<String>>() ;
ExecutorService executorService = Executors.newFixedThreadPool(8);
/**
* 创建10个线程,拿到fututre
* fututre是一个访问结果的引用;只有线程执行完了才能通过fututre.get()拿到call返回的结果值
*/
for(int i=1;i<=10;i++){
Future<String> fututre = executorService.submit(new Callable<String>() {
/**
* 线程结果访问值
* @return
* @throws Exception
*/
public String call() throws Exception {
String serviceResponse = service();
return serviceResponse;
}
/**
* 业务
* @return
* @throws Exception
*/
public String service() throws Exception{
Thread.sleep(500);
return "MOBIN";
}
});
/**
* 结果添加到队列中单线程轮询
*/
resultList.add(fututre);
}
/**
* 单线程轮询阻塞结果
* 1000毫秒一次
*/
executorService.submit(new Runnable() {
@Override
public void run() {
for(;;){
resultList.forEach(result->{
try{
if(result.isDone()){
System.out.println(result.get()); //打印各个线程(任务)执行的结果,把他从集合中删除也可以
}
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}catch(ExecutionException e){
e.printStackTrace();
}
});
}
}
});
}
Future用法
future.isDone();//判断线程是否执行完
future.get();//获取结果,如果未执行完则直接阻塞线程,很显然最好isDone判断一下再调用get是最好的
线程池——任务聚集策略
new ThreadPoolExecutor.AbortPolicy();//丢弃任务并抛出RejectedExecutionException异常。
new ThreadPoolExecutor.DiscardPolicy();//也是丢弃任务,但是不抛出异常。
new ThreadPoolExecutor.DiscardOldestPolicy();//丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
new ThreadPoolExecutor.CallerRunsPolicy();//由调用线程处理该任务
线程池——常见设计
CPU密集任务设计测路(核心线程:CPU核数 +1 ):
CPU密集型也叫计算密集型,指的是系统的硬盘、内存性能相对CPU要好很多,此时,系统运作大部分的状况是CPU Loading 100%,CPU要读/写I/O(硬盘/内存),I/O在很短的时间就可以完成,而CPU还有许多运算要处理,CPU Loading很高。因为这种任务已经跑完加线程也没啥意义,反而导致内存增多;
IO密集任务设计策略(核心线程:CPU核数 * 2):
IO密集型指的是系统的CPU性能相对硬盘、内存要好很多,此时,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高,所以不能让线程闲置,赋予更多的线程熟练
大神公式:核心线程数 = CPU核数 / (1-阻塞系数) 例如阻塞系数 0.8,CPU核数为4
线程池——原理(待更新)
JVM并发编程专题章节:
多线程框架
多线程测试
GodSchool
致力于简洁的知识工程,输出高质量的知识产出,我们一起努力
博主私人微信:supperlzf