ThreadFactory

线程池中的线程从哪里来呢?就是ThreadFoctory

public interface ThreadFactory {
Thread newThread(Runnable r);
}

Threadfactory里面有个接口,当线程池中需要创建线程就会调用该方法,也可以自定义线程工厂

public class ThreadfactoryText {
public static void main(String[] args) {
Runnable runnable=new Runnable() {
@Override
public void run() {
int num=new Random().nextInt(10);
System.out.println(Thread.currentThread().getId()+"--"+System.currentTimeMillis()+"--睡眠"+num);
try {
TimeUnit.SECONDS.sleep(num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//创建线程池 使用自定义线程工厂 采用默认的拒绝策略
ExecutorService executorService=new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new SynchronousQueue<>(), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t=new Thread(r);
t.setDaemon(true);//设置为守护线程,当主线程运行结束,线程池中线程也会被释放
System.out.println("创建了线程"+t);
return t;
}
});
//提交五个任务
for (int i = 0; i < 5; i++) {
executorService.submit(runnable);
}
}
}

当线程提交超过五个任务时,线程池会默认抛出异常

监控线程池

ThreadPoolExcutor提供了一组方法用于监控线程池

int getActiveCount()//获得线程池只当前的获得线程数量
long getCompletedTaskCount()//返回线程池完成任务数量
int getCorePoolSize()//线程池中核心任务数量
int getLargestPoolSize() //返回线程池中曾经达到线程的最大数
int getMaximumPoolSize()//返回线程池的最大容量
int getPoolSize()//返回线程大小
BlockingQueue<Runnable> getQueue()//返回阻塞队列
long getTaskCount()//返回线程池收到任务总数

public class Text {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getId() + "线程开始执行--" + System.currentTimeMillis());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//创建线程池 使用默认线程工厂 有界队列 采用DiscardPolicy策略
ThreadPoolExecutor executorService = new ThreadPoolExecutor(2, 5, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());
//提交五个任务
for (int i = 0; i < 30; i++) {
executorService.submit(runnable);
System.out.println("当前线程核心线程数"+executorService.getCorePoolSize()+",最大线程数:"+executorService.getMaximumPoolSize()+",当前线程池大小:"+executorService.getPoolSize()+"活动线程数:"+executorService.getActiveCount()+",收到任务:"+executorService.getTaskCount()+"完成任务数:"+executorService.getCompletedTaskCount()+"等待任务数:"+executorService.getQueue().size());
TimeUnit.MILLISECONDS.sleep(500);
}
System.out.println("-------------------");
while (executorService.getActiveCount()>=0)//继续对线程池进行检测
{
System.out.println("当前线程核心线程数"+executorService.getCorePoolSize()+",最大线程数:"+executorService.getMaximumPoolSize()+",当前线程池大小:"+executorService.getPoolSize()+"活动线程数:"+executorService.getActiveCount()+",收到任务:"+executorService.getTaskCount()+"完成任务数:"+executorService.getCompletedTaskCount()+"等待任务数:"+executorService.getQueue().size());
Thread.sleep(1000);//每1秒检测一次
} }
}

当线程池大小达到了核心线程数,线程会被放在等待队列。当线程池等待队列已满会开启新的线程。当当前线程大小达到最大线程数,等待队列也满了,再提交的话会执行DiscardPolicy策略,直接丢弃这个无法处理的任务,最后30个任务只剩下15个了。

原理如图:

扩展线程池

有时候需要对线程池进行扩展,如在监控每个任务开始和结束时间,或者自定义其他增强功能。

ThreadPoolExecutor线程池提供了两个方法:

protected void beforeExecute(Thread t, Runnable r) { }
protected void afterExecute(Runnable r, Throwable t) { }

线程池执行某个任务前会执行beforeExecute()方法,执行后会调用afterExecute()方法

查看ThreadPoolExecutor源码,在该类中定义了一个内部类Worker,ThreadPoolExecutor线程池的工作线程就是Worker类的实例,Worker实例在执行时会调用beforeExecute与afterExecute方法。

public void run() {
runWorker(this);
}
final void runWorker(Worker w) {
try {
beforeExecute(wt, task);
try {
task.run();
afterExecute(task, null);
} catch (Throwable ex) {
afterExecute(task, ex);
throw ex;
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
}

部分代码已省略,线程执行前会调用beforeExecute,执行后会调用afterExecute方法。

扩展线程池示例

package com;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; public class Text07 {
public static void main(String[] args) { //定义扩展线程池 定义线程池类继承ThreadPoolExecutor,然后重写其他方法
ExecutorService threadPoolExecutor=
new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,new LinkedBlockingDeque<>()){
//在内部类重写开始方法
@Override
protected void beforeExecute(Thread t, Runnable r) {
System.out.println(t.getId()+"线程准备执行任务"+((Mytask)r).name);
}
//在内部类重写结束方法
@Override
protected void afterExecute(Runnable r, Throwable t) {
System.out.println(((Mytask)r).name+"执行完成");
}
//线程池退出
@Override
protected void terminated() {
System.out.println("线程池退出");
}
};
for (int i = 0; i < 5; i++) {
Mytask mytask=new Mytask("Thread"+i);
threadPoolExecutor.execute(mytask);
}
}
private static class Mytask implements Runnable
{
private String name; public Mytask(String name)
{
this.name=name;
}
@Override
public void run() {
System.out.println(name+"正在被执行"+Thread.currentThread().getId());
try {
Thread.sleep(1000);//模拟任务时长
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

优化线程池大小

线程池大小对系统性能有一定影响,过大或者过小都无法方法发挥系统最佳性能,不需要非常精确,只要避免极大或者极小就可以了,一般来说线程池大小大姚考虑CPU数量

线程池大小=CPU数量 * 目标CPU使用率*(1+等待时间与计算时间的比)

线程池死锁

如果线程池执行中,任务A在执行过程中提交了任务B,任务B添加到线程池中的等待队列,如果A的结束需要B的执行结果,而B线程需要等待A线程执行完毕,就可能会使其他所有工作线程都处于等待状态,待这些任务在阻塞队列中执行。线程池中没有可以对阻塞队列进行处理的线程,就会一直等待下去照成死锁。

适合给线程池提交相互独立的任务,而不是彼此依赖的任务,对于彼此依赖的任务,可以考虑分别提交给不同的线程池来处理。

线程池异常信息捕获

import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; public class Text09 {
public static void main(String[] args) {
//创建线程池
ExecutorService executorService=new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,new SynchronousQueue<>());
//向线程池中添加两个数相处计算的任务
for (int i = 0; i <5 ; i++) {
executorService.submit(new Text(10,i));
} }
private static class Text implements Runnable
{
private int x;
private int y;
public Text(int x,int y)
{
this.x=x;
this.y=y;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程x/y结果的为"+x+"/"+y+"="+(x/y));
}
}
}

可以看到只有四条结果,实际向线程池提交了五个任务,但是当i==0时,产生了算术异常,线程池把该异常吃掉了,导致我们对该异常一无所知

解决办法:

1.把submit改为execute

2.对线程池进行扩展,对submit进行包装

package com;

import java.util.concurrent.*;

public class Text09 {
public static void main(String[] args) {
//创建线程池 使用自定义的线程池
ExecutorService executorService=new TranceThreadPoorExcuter(5,5,0, TimeUnit.SECONDS,new SynchronousQueue<>());
//向线程池中添加两个数相处计算的任务
for (int i = 0; i <5 ; i++) {
executorService.submit(new Text(10,i));
} }
public static class Text implements Runnable
{
public int x;
public int y;
public Text(int x,int y)
{
this.x=x;
this.y=y;
} @Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程x/y结果的为"+x+"/"+y+"="+(x/y));
}
}
//自定义线程池类 对TranceThreadPoorExcuter进行扩展
private static class TranceThreadPoorExcuter extends ThreadPoolExecutor
{ public TranceThreadPoorExcuter(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
//定义一个方法用于传入两个参数 第一个是要接受的任务 第二个是Exception
public Runnable warp(Runnable r,Exception e)
{
return new Runnable() {
@Override
public void run() { try {
r.run();
}
catch (Exception e1)
{
e.printStackTrace();
throw e1;
}
}
};
}
//重写submit方法
@Override
public Future<?> submit(Runnable task) {
return super.submit(warp(task,new Exception("客户跟踪异常")));
}
//还可以重写excute方法
}
}

此方法使用了自定义的线程池,重写线程池中的submit方法,在submit方法中,把要传入的任务参数带一个捕获异常信息的功能就可以捕获线程池异常。

Java利用线程工厂监控线程池的更多相关文章

  1. InnoDB的后台线程(IO线程,master线程,锁监控线程,错误监控线程)和内存(缓冲池,重做日志缓冲池,额外内存池)

    InnoDB有多个内存块,你可以认为这些内存块组成了一个大的内存池,负责如下工作: 维护所有进程/线程需要访问的多个内部数据结构. 缓存磁盘上的数据,方便快速地读取,并且在对磁盘文件的数据进行修改之前 ...

  2. java利用WatchService实时监控某个目录下的文件变化并按行解析(注:附源代码)

    首先说下需求:通过ftp上传约定格式的文件到服务器指定目录下,应用程序能实时监控该目录下文件变化,如果上传的文件格式符合要求,将将按照每一行读取解析再写入到数据库,解析完之后再将文件改名. 一. 一开 ...

  3. Java并发(六)线程池监控

    目录 一.线程池监控参数 二.线程池监控类 三.注意事项 在上一篇博文中,我们介绍了线程池的基本原理和使用方法.了解了基本概念之后,我们可以使用 Executors 类创建线程池来执行大量的任务,使用 ...

  4. JAVA并发,线程工厂及自定义线程池

    package com.xt.thinks21_2; import java.util.concurrent.ExecutorService; import java.util.concurrent. ...

  5. java利用线程池处理集合

    java利用线程池处理集合 2018年07月23日 17:21:19 衍夏成歌 阅读数:866   版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/s ...

  6. Java并发编程:Java的四种线程池的使用,以及自定义线程工厂

    目录 引言 四种线程池 newCachedThreadPool:可缓存的线程池 newFixedThreadPool:定长线程池 newSingleThreadExecutor:单线程线程池 newS ...

  7. Java并发(四)线程池使用

    上一篇博文介绍了线程池的实现原理,现在介绍如何使用线程池. 目录 一.创建线程池 二.向线程池提交任务 三.关闭线程池 四.合理配置线程池 五.线程池的监控 线程池创建规范 一.创建线程池 我们可以通 ...

  8. 【Java并发编程六】线程池

    一.概述 在执行并发任务时,我们可以把任务传递给一个线程池,来替代为每个并发执行的任务都启动一个新的线程,只要池里有空闲的线程,任务就会分配一个线程执行.在线程池的内部,任务被插入一个阻塞队列(Blo ...

  9. Java线程状态、线程start方法源码、多线程、Java线程池、如何停止一个线程

    下面将依次介绍: 1. 线程状态.Java线程状态和线程池状态 2. start方法源码 3. 什么是线程池? 4. 线程池的工作原理和使用线程池的好处 5. ThreadPoolExecutor中的 ...

随机推荐

  1. css & background-image & full page width & background-size

    css & background-image & full page width & background-size https://css-tricks.com/perfec ...

  2. react hooks useEffect 取消 promise

    react hooks useEffect 取消 promise cancel promise https://github.com/facebook/react/issues/15006#issue ...

  3. Flutter 区分开发环境和生产环境

    Uri _baseUrl; final isProd = const bool.fromEnvironment('dart.vm.product'); if (isProd) { _baseUrl = ...

  4. 伦尼斯酒庄(Chateau Renice)再次赞助亚洲50大餐厅赛事

    连续几年来,伦尼斯酒庄(Chateau Renice)一直是亚洲50大最佳餐厅评选赛(Asia's 50 Best Restaurant Awards)的赞助商.2020年伦尼斯酒庄酒庄(Chatea ...

  5. Codeforces Round #703 (Div. 2) (A~E)

    A. Shifting Stacks 题目链接 点我跳转 题目大意 给定 \(N\) 个土堆,第 \(i\) 个土堆有 \(Ai\) 个木块 你可以将第 \(i\) 个土堆的木块转移至第 \(i + ...

  6. [转]关于特征点法、直接法、光流法slam的对比

    转载网址:https://blog.csdn.net/weixin_38203573/article/details/79787499 特征点法: 通过特征点匹配来跟踪点,计算几何关系得到R,t,BA ...

  7. 微信小程序(五)-常见组件(标签)

    常见组件(标签) https://developers.weixin.qq.com/miniprogram/dev/component/ 1.view 代替以前的div标签 2.text 1.文本标签 ...

  8. 代码安全性和健壮性:如何在if和assert中做选择?

    道哥的第 023 篇原创 目录 一.前言 二.assert 断言 assert 是一个宏,不是一个函数 三.if VS assert 1. 使用 if 语句来检查 2. 使用 assert 断言来检查 ...

  9. jar下载慢,maven配置国内仓库

    使用 maven 下载 jar 包速度会很慢,原因是 maven 默认的仓库地址是国外的,所以速度很慢,解决这个问题我们只需要修改 maven 仓库地址即可 maven 下载 jar 包时会优先去 ~ ...

  10. 区分函数防抖&函数节流

    1. 概念区分 函数防抖:触发事件后,在n秒内函数只能执行一次,如果触发事件后在n秒内又触发了事件,则会重新计算函数延执行时间. 简单说: 频繁触发, 但只在特定的时间内才执行一次代码,如果特定时间内 ...