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. node.js 如何处理一个很大的文件

    node.js 如何处理一个很大的文件 思路 arraybuffer 数据分段 时间分片 多线程 web workers sevice workers node.js 如何处理一个很大的文件 http ...

  2. 微信小程序(一)-工具创建和结构配置说明 Stable Build

    按装前特别说明: windows最好下载32位的,不然用到用到后面就出现"网络连接失败",然后就登录不上去了,打不开编辑器了! 问题 : 微信开发者工具网络连接失败, " ...

  3. 配置mysql数据库时出再错误:LookupError: No installed app with label 'admin'.

    版本: windows10+py37+django2.2 错误: 项目启动时出现,No installed app with label 'admin' 解决办法: 安装最新的 pip install ...

  4. Java基础语法:static修饰符

    一.静态变量 描述: 在类中,使用'static'修饰的成员变量,就是静态变量,反之为非静态变量. 区别: 静态变量属于类的,可以使用类名来访问:非静态变量是属于对象的,必须使用对象来访问. 静态变量 ...

  5. 配置Nginx的坑及思路

    我配置的是Django + uwsgi + Nginx 说下思路,先进行模块化测试: Django: Django 下 第一个坑是sql版本低问题,原因用pip安装不正确,在网上查了下按这个文章重装下 ...

  6. 基于docker搭建gitlab

    一.概述 GitLab是一个利用 Ruby on Rails 开发的开源应用程序,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目. 它拥有与Github类似的功能,能够浏览 ...

  7. Linux下搭建RocketMQ环境

    Apache 官网: http://rocketmq.apache.org/ RocketMQ 的 Github 地址: English:https://github.com/apache/rocke ...

  8. redis数据结构和对象二

    跳跃表(skiplist) 跳跃表是一种有序数据结构.跳跃表支持平均O(logN),最坏O(N)复杂度的节点查找,大部分情况下,跳跃表的效率可以和平衡树相媲美,并且因为跳跃表的实现比平衡树简单,所有不 ...

  9. ios打包的IDP证书的创建方法

    在我们打包ios应用的时候,需要一个IDP证书. 那么我们如何生成这个IDP证书呢?网上介绍的方法都是需要使用mac电脑,然后用mac电脑的钥匙串访问的功能先生成csr文件,然后去苹果开发者生成,然而 ...

  10. jQuery实现全网热播视频

    <section id="play"> <h1>全网热播视频</h1> <ul> <li><img src=&qu ...