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 ...
随机推荐
- shell日期格式化、加减运算
#!/bin/bash echo i love you输出:i love you =======================================反引号的作用============== ...
- Spring Boot笔记(七) springboot 集成 JavaMail 实现邮箱认证
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一.JavaMail 1.什么是JavaMail? JavaMail,顾名思义,提供给开发者处理 电子邮 ...
- Java实现 LeetCode 738 单调递增的数字(暴力)
738. 单调递增的数字 给定一个非负整数 N,找出小于或等于 N 的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增. (当且仅当每个相邻位数上的数字 x 和 y 满足 x <= ...
- java实现第四届蓝桥杯剪格子
剪格子 题目描述 如图p1.jpg所示,3 x 3 的格子中填写了一些整数. 我们沿着图中的红色线剪开,得到两个部分,每个部分的数字和都是60. 本题的要求就是请你编程判定:对给定的m x n 的格子 ...
- 从源码研究如何不重启Springboot项目实现redis配置动态切换
上一篇Websocket的续篇暂时还没有动手写,这篇算是插播吧.今天讲讲不重启项目动态切换redis服务. 背景 多个项目或微服务场景下,各个项目都需要配置redis数据源.但是,每当运维搞事时(修改 ...
- java解决poi导出excel文字水印,导出excel不可操作问题
首先需求是用户提出导出excel数据需使用水印备注其用途: 其实就是在导出excel的同时带有自定义文字水印的导出. 那么我们首先想到的肯定是以一个什么样的思路去解决该问题,首先查找poi导出exce ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(四)
系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...
- Flutter upgrade更新版本引发的无法启动调试APP的错误 target:kernel_snapshot failed”
前言 我的主机上的Flutter 本地的分支是在 beta,因为去年想尝鲜Flutter Web,所以一直没切回来stable分支. 早上打开VSCode,右下角弹出了Flutter upgrade的 ...
- 深入剖析AQS
目录 摘要 AbstractQueuedSynchronizer实现一把锁 ReentrantLock ReentrantLock的特点 Synchronized的基础用法 ReentrantLock ...
- 关于adb命令的基本使用
在我们使用adb命令之前,我们要安装一个安卓模拟器(夜神.逍遥.海马王......),这里以夜神安卓模拟器为准(个人推荐,没用过可以使用夜神模拟器). 进入夜神安卓模拟器官网:https://www. ...