java 中的线程池
1、实现下面的一个需求,控制一个执行函数只能被五个线程访问
package www.weiyuan.test;
public class Test {
public static void main(String[] args) {
for(int i = 0 ;i < 100 ;i++){
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
method();
}
}).start();
}
}
public static void method(){
System.out.println(""+Thread.currentThread().getName()+"进来了");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(""+Thread.currentThread().getName()+"出去了");
}
}
上面我们创建了100个线程,随机的执行method方法
现在我们要控制每次只要5个线程可以method方法,如何实现了,可以采用信号量的方法
操作系统的信号量是个很重要的概念,在进程控制方面都有应用。Java 并发库 的Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,acquire()获取一个许可,如果没有就等待,而release()释放一个许可。比如在Windows下可以设置共享文件的最大客户端访问个数。
Semaphore维护了当前访问的个数,提供同步机制,控制同时访问的个数。在数据结构中链表可以保存“无限”的节点,用Semaphore可以实现有限大小的链表。另外重入锁ReentrantLock也可以实现该功能,但实现上要负责些,代码也要复杂些。
下面的Demo中申明了一个只有5个许可的Semaphore,而有100个线程要访问这个资源,通过acquire()和release()获取和释放访问许可。
第一种方式使用信号量的方式:
package www.weiyuan.test;
import java.util.concurrent.Semaphore;
public class Test {
private static Semaphore semaphore = new Semaphore(5);
public static void main(String[] args) {
for(int i = 0 ;i < 100 ;i++){
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
method();
}
}).start();
}
}
public static void method(){
try {
semaphore.acquire();
System.out.println(""+Thread.currentThread().getName()+"进来了");
Thread.sleep(1000);
System.out.println(""+Thread.currentThread().getName()+"出去了");
semaphore.release();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
第二种方式使用线程池的方式:
package www.weiyuan.test; import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore; public class Test {
private static Executor executor = Executors.newFixedThreadPool(5);
public static void main(String[] args) {
for(int i = 0 ; i< 100 ;i++){
executor.execute(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
method();
}
});
} } public static void method(){ try {
System.out.println(""+Thread.currentThread().getName()+"进来了");
Thread.sleep(1000);
System.out.println(""+Thread.currentThread().getName()+"出去了");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}
接下来我们自己编写代码自己封装一个线程池:
深入分析java线程池的实现原理
ThreadPoolExecutor
Executors是java线程池的工厂类,通过它可以快速初始化一个符合业务需求的线程池,如Executors.newFixedThreadPool方法可以生成一个拥有固定线程数的线程池。

我们来分析下上面的几个参数的意思
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory)
我们来分析上面参数的意思:
线程池存在下面的两个数据结构
1、第一个数据结构是任务队列,用来存储任务的
2、第二个数据队列来存储线程的集合
线程池会创建线程执行任务,因为执行的任何很多,创建的线程如果达到了corePoolSize的数目,列如现在的corePoolSize的数目是5,但是需要执行的任务数量是100,
当创建了5个线程执行前五个任务之后,如果来了第6个任务,这个时候不会在创建新的线程了,会把多余的任务添加到任务队列中,任务队列也是有上限值的
上面的分析可以用下图来表示

如果工作队列已经达到了最大值,此时如果继续向任务队列中添加任务,即下来会做啥操作了
这个时候线程池会创建新的线程,用新的线程来执行新添加的任务,直到线程数目maximumPoolSize,如果线程的数目已经是maximumPoolSize,如果此时在添加任务,这个时候线程池就会抛出异常了

线程池中的线程数目不是越大越好,推荐的数目是电脑cpu的值加上1
如果线程池的线程数量少于corePoolSize的时候,线程池会使用threadFactory这个线程工厂创建新的线程执行Runnable任务。
如果线程池的线程数量大于corePoolSize的时候,线程池会把Runnable任务存放在队列workQueue中。
线程池的线程数量大于corePoolSize,队列workQueue已满,而且小于maximumPoolSize的时候,线程池会创建新的线程执行Runnable任务。否则,任务被拒。
keepAliveTime
线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间;默认情况下,该参数只在线程数大于corePoolSize时才有用;
unit
keepAliveTime的单位;
workQueue
用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口,在JDK中提供了如下阻塞队列:
1、ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
2、LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
3、SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
4、priorityBlockingQuene:具有优先级的无界阻塞队列;
threadFactory
创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名。
我们来看一下我们自定定义的一个一个线程池对象实现上面的代码:
package www.weiyuan.test; import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; public class Test { public static void main(String[] args) {
LinkedBlockingDeque<Runnable> blockingDeque = new LinkedBlockingDeque<Runnable>(100);//100是该任务队列的最大数目,达到100之后不会自动扩容了
ThreadFactory threadFactory = new ThreadFactory() {
AtomicInteger atomicInteger = new AtomicInteger(); //线程安全的变量自增
@Override
public Thread newThread(Runnable task) {
//收到创建线程,将需要执行的任务交给线程去处理,这个地方一定注意线程安全 thread.setName("MyTHread"+i++);i++是线程不安全的
Thread thread = new Thread(task); thread.setName("MyTHread"+atomicInteger.getAndIncrement());
return thread;
}
};
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 1, TimeUnit.SECONDS, blockingDeque, threadFactory ); for(int i = 0 ;i < 100;i++){
threadPoolExecutor.execute(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
method();
}
});
}
} public static void method(){ try {
System.out.println(""+Thread.currentThread().getName()+"进来了");
Thread.sleep(1000);
System.out.println(""+Thread.currentThread().getName()+"出去了");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}
1.Android中的资源池其实来源于Java。线程池的实现是ThreadPoolExecutor。Java中有一个Executors工厂,这个工厂类提供了许多静态方法用于创建不同类型的线程池。创建自定义的线程池一般需要指定以下的参数。
a.corePoolSize:代表的是线程池的核心线程数量,一般来说,核心线程会在线程池中一直存活,即便处于空闲状态(没有执行任务的状态)。但是也可以通过将ThreadPoolExecutor的allowCoreThreadTimeOut()设置为true,那么核心线程在空闲的时候会有超时的策略。
b.maximumPoolSize:代表线程池能够容纳的最多的线程数量。当线程数量达到这个数值后,后续的任务将会被阻塞。
c.keepAliveTime:设置非核心线程空闲时的超时时长,一旦达到这个限制,线程就会被回收。当设置ThreadPoolExecutor的allowCoreThreadTimeOut()为true时,这个超时限制也可以作用于核心线程。
d.unit:指定keepAliveTime参数的时间单位。
e.workQueue:代表线程池中的任务队列,通过execute()提交的任务都会加到这个队列中。
f.threadFactory:线程工厂,为线程池提供创建线程的功能。
LinkedBlockingDeque<Runnable>(100) 所以该线程池能够最大存储的任务数目是110,最大10个线程执行10个任务,其余100个线程存储在任务队列中 如果你一开始执行的任务大于110,线程池就会报异常
package www.weiyuan.test; import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; public class Test { public static void main(String[] args) {
LinkedBlockingDeque<Runnable> blockingDeque = new LinkedBlockingDeque<Runnable>(100);//100是该任务队列的最大数目,达到100之后不会自动扩容了
ThreadFactory threadFactory = new ThreadFactory() {
AtomicInteger atomicInteger = new AtomicInteger(); //线程安全的变量自增
@Override
public Thread newThread(Runnable task) {
//收到创建线程,将需要执行的任务交给线程去处理,这个地方一定注意线程安全 thread.setName("MyTHread"+i++);i++是线程不安全的
Thread thread = new Thread(task); thread.setName("MyTHread"+atomicInteger.getAndIncrement());
return thread;
}
};
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 1, TimeUnit.SECONDS, blockingDeque, threadFactory ); for(int i = 0 ;i < 115;i++){
threadPoolExecutor.execute(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
method();
}
});
}
} public static void method(){ try {
System.out.println(""+Thread.currentThread().getName()+"进来了");
Thread.sleep(1000);
System.out.println(""+Thread.currentThread().getName()+"出去了");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}
我们执行115个任务
就会包下面的异常:

查看这篇博客相当的经典:http://www.jianshu.com/p/87bff5cc8d8c
java 中的线程池的更多相关文章
- 【万字图文-原创】 | 学会Java中的线程池,这一篇也许就够了!
碎碎念 关于JDK源码相关的文章这已经是第四篇了,原创不易,粉丝从几十人到昨天的666人,真的很感谢之前帮我转发文章的一些朋友们. 从16年开始写技术文章,到现在博客园已经发表了222篇文章,大多数都 ...
- 《Java并发编程的艺术》 第9章 Java中的线程池
第9章 Java中的线程池 在开发过程中,合理地使用线程池能带来3个好处: 降低资源消耗.通过重复利用已创建的线程 降低线程创建和销毁造成的消耗. 提高响应速度.当任务到达时,任务可以不需要等到线程创 ...
- JAVA中创建线程池的五种方法及比较
之前写过JAVA中创建线程的三种方法及比较.这次来说说线程池. JAVA中创建线程池主要有两类方法,一类是通过Executors工厂类提供的方法,该类提供了4种不同的线程池可供使用.另一类是通过Thr ...
- Java中的线程池用过吧?来说说你是怎么理解线程池吧?
前言 Java中的线程池用过吧?来说说你是怎么使用线程池的?这句话在面试过程中遇到过好几次了.我甚至这次标题都想写成[Java八股文之线程池],但是有点太俗套了.虽然,线程池是一个已经被说烂的知识点了 ...
- 浅析Java中的线程池
Java中的线程池 几乎所有需要异步或并发执行任务的程序都可以使用线程池,开发过程中合理使用线程池能够带来以下三个好处: 降低资源消耗 提高响应速度 提高线程的可管理性 1. 线程池的实现原理 当我们 ...
- 第9章 Java中的线程池 第10章 Exector框架
与新建线程池相比线程池的优点 线程池的分类 ThreadPoolExector参数.执行过程.存储方式 阻塞队列 拒绝策略 10.1 Exector框架简介 10.1.1 Executor框架的两级调 ...
- Java中的线程池
package com.cn.gbx; import java.util.Date; import java.util.Random; import java.util.Timer; import j ...
- java 中的线程池和线程 调用小demo
public class Main { public static void main(String[] args) { try { /// ThreadPoolExecutor executor = ...
- Java中的线程池ExecutorService
示例 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.u ...
随机推荐
- spring boot 入口源码分析
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); / ...
- discuz mlv3.x命令注入
本次漏洞是由于Discuz! ML对于cookie字段的不恰当处理造成的cookie字段中的language参数未经过滤,直接被拼接希尔缓存文件中,而缓存文件随后被加载,造成代码执行. 共有60出利用 ...
- Java实现 LeetCode 804 唯一摩尔斯密码词 (暴力)
804. 唯一摩尔斯密码词 国际摩尔斯密码定义一种标准编码方式,将每个字母对应于一个由一系列点和短线组成的字符串, 比如: "a" 对应 ".-", " ...
- Java实现 LeetCode 762 二进制表示中质数个计算置位(位运算+JDK的方法)
762. 二进制表示中质数个计算置位 给定两个整数 L 和 R ,找到闭区间 [L, R] 范围内,计算置位位数为质数的整数个数. (注意,计算置位代表二进制表示中1的个数.例如 21 的二进制表示 ...
- Java实现 蓝桥杯VIP基础练习 矩形面积交
描述 平面上有两个矩形,它们的边平行于直角坐标系的X轴或Y轴.对于每个矩形,我们给出它的一对相对顶点的坐标,请你编程算出两个矩形的交的面积. 输入 输入仅包含两行,每行描述一个矩形. 在每行中,给出矩 ...
- PAT 跟奥巴马一起编程
美国总统奥巴马不仅呼吁所有人都学习编程,甚至以身作则编写代码,成为美国历史上首位编写计算机代码的总统.2014 年底,为庆祝“计算机科学教育周”正式启动,奥巴马编写了很简单的计算机代码:在屏幕上画一个 ...
- 面试了 6 轮 Google 中国 之后,还是挂了
去年换工作的时候, 面试了一下 Google (这里说的是 Google 中国哈), 来了个 Google 面试六轮游, 结果是没通过.
- 如何从Debian 9 Stretch升级到Debian10 Buster
Let's first fully upgrade our current Debian Stretch system: # apt-get update # apt-get upgrade # ap ...
- 基于移动最小二乘法的点云曲面拟合(python)
1.移动最小二乘法介绍 为了更好地对数据量大且形状复杂的离散数据进行拟合,曾清红等人[1]开发出一种新的算法——移动最小二乘法.这种新的最小二乘算法为点云数据的处理提供了新的方法.使用点云数据拟合曲面 ...
- PowerApps Component Framework PCF 部署
PowerApps PCF 可以满足复杂的功能, 我们可以使用PCF来创建复杂的PowerApps. 这里附上微软的package code componet 教程(https://docs.micr ...