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. how to drag a tab to open it in a new window

    how to drag a tab to open it in a new window 在新的窗口中打开拖拽的 tab? https://superuser.com/questions/131928 ...

  2. nasm astrcmp函数 x86

    xxx.asm: %define p1 ebp+8 %define p2 ebp+12 %define p3 ebp+16 section .text global dllmain export as ...

  3. 大送福利!市场香饽饽VAST到底什么来头,为何被高价估值

    近日,NGK星空计划新币VAST成为了香饽饽,还未正式上线前,市场讨论的热度就居高不下.如今NGK推出1万VAST免费送新人福利更是将这波热度推向了高潮. 具体福利规则:在美国加州时间2021年2月8 ...

  4. Vue框架- 指令操作

    目录 一.Vue指令操作 1. 表单指令 2. 条件指令 3. 循环指令 4. 斗篷指令 5. 实例成员:delimiter分隔符(了解) 6. filter过滤器 7. computed计算属性 8 ...

  5. Centos7 升级 sqlite3

    下载地址:https://www.sqlite.org/download.html [root@djangoServer ~]# wget https://www.sqlite.org/2019/sq ...

  6. EFCodeFirst Migrations数据库迁移

    EFCodeFirst Migrations数据库迁移 数据库迁移 1.生成数据库 修改类文件PortalContext.cs的静态构造函数,取消当数据库模型发生改变时删除当前数据库重建新数据库的设置 ...

  7. Docker 下Elasticsearch 的安装 和ik分词器

    (1)docker镜像下载 docker pull elasticsearch:5.6.8 (2)安装es容器 docker run -di --name=changgou_elasticsearch ...

  8. 剑指 Offer 44. 数字序列中某一位的数字 + 找规律 + 数位

    剑指 Offer 44. 数字序列中某一位的数字 Offer_44 题目描述 题解分析 java代码 package com.walegarrett.offer; /** * @Author Wale ...

  9. Golang+chromedp+goquery 简单爬取动态数据

    目录 Golang+chromedp+goquery 简单爬取动态数据 Golang的安装 下载golang软件 解压golang 配置golang 重新导入配置 chromedp框架的使用 实际的代 ...

  10. 使用createrepo构建本地yum仓库

    rpm包安装的时候会有很多软件会出现因为其他依赖包没有,而导致安装失败的情况.一般可以连接外网的时候我们直接使用 yum 进行安装,可以为我们解决依赖包关系,但是很多工作环境下是没有外网的,内网情况下 ...