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 ...
随机推荐
- hexo命令提示 hexo <command>
所有的hexo命令执行后都会提示 hexo <command> 解决方法: 通过hexo init blog命令初始化一个博客后,应该cd /blog 转到博客目录
- SpringCloud Eureka Client和Server侧配置及Eureka高可用配置
一.Eureka注册中心和客户端配置Demo. 1.Server端 a.使用Idea创建Spring项目,如下所示: b.相关配置 application.yaml配置文件如下: # eureka本身 ...
- 前端HTML学习 table标签 知识点与使用
表格基本结构 <table> <tr> <td>单元格</td> </tr> </table> < tr >表示 行 ...
- “造轮运动”之 ORM框架系列(二)~ 说说我心目中的ORM框架
ORM概念解析 首先梳理一下ORM的概念,ORM的全拼是Object Relation Mapping (对象关系映射),其中Object就是面向对象语言中的对象,本文使用的是c#语言,所以就是.ne ...
- Java实现 蓝桥杯 算法提高 小X的购物计划
试题 算法提高 小X的购物计划 问题描述 小X打算去超市shopping.小X没什么钱,只有N元.超市里有M种物品,每种物品都需要money,在小X心中有一个重要度.有的物品有无限件,有的物品只有几件 ...
- Java实现 LeetCode 677 键值映射(字典树)
677. 键值映射 实现一个 MapSum 类里的两个方法,insert 和 sum. 对于方法 insert,你将得到一对(字符串,整数)的键值对.字符串表示键,整数表示值.如果键已经存在,那么原来 ...
- Java实现 LeetCode 374 猜数字大小 II
375. 猜数字大小 II 我们正在玩一个猜数游戏,游戏规则如下: 我从 1 到 n 之间选择一个数字,你来猜我选了哪个数字. 每次你猜错了,我都会告诉你,我选的数字比你的大了或者小了. 然而,当你猜 ...
- Java实现 LeetCode 127 单词接龙
127. 单词接龙 给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度.转换需遵循如下规则: 每次转换只能改变一个字 ...
- Java中IO软件包的详细介绍
一.Java Io流 Java Io流的概念 java的io是实现输入和输出的基础,可以方便的实现数据的输入和输出操作.在java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为" ...
- Java实现 蓝桥杯 算法提高 成绩排名
试题 算法提高 成绩排名 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 小明刚经过了一次数学考试,老师由于忙碌忘记排名了,于是老师把这个光荣的任务交给了小明,小明则找到了聪明的你, ...