大数据处理之Java线程池使用
线程池的运行流程
线程池的运行流程线程池是一种用来管理和复用线程的技术,它可以有效地提高多线程程序的性能和资源利用率。
线程池通过预先创建一定数量的线程,并将任务分配给这些线程来处理,避免了不断创建和销毁线程的开销。
线程池的运行流程如下:1. 线程池的创建:线程池的创建需要确定线程池的大小、任务队列的长度、线程的优先级等参数。
可以通过ThreadPoolExecutor类创建线程池,其中ThreadPoolExecutor构造函数的参数可以指定线程池的大小、任务队列的长度等。
2.初始化线程池:创建线程池之后,线程池会创建一定数量的线程,这些线程处于等待状态,等待任务的到来。
线程池还会创建一个任务队列,用来存放待执行的任务。
3. 提交任务:当需要执行任务时,可以通过execute方法提交任务到线程池。
线程池会将任务放入任务队列中,等待线程来执行。
如果任务队列已满,线程池会根据配置的策略进行处理,比如直接丢弃任务、抛出异常等。
4.选择线程执行任务:线程池会从任务队列中选择一个任务,然后从线程池中选择一个空闲的线程来执行任务。
线程池可以根据多种策略来选择线程,比如先进先出、最近未使用等。
5.执行任务:选中线程开始执行任务。
线程执行任务期间,会不断从任务队列中获取任务并执行,直到任务队列为空或线程池被关闭。
6.线程复用:任务执行完毕之后,线程不会被销毁,而是回到线程池中,等待新的任务。
这样可以减少线程的创建和销毁的开销,提高了线程的复用率。
7.监控和管理:线程池通常会提供一些监控和管理的功能,比如可以监控线程池的运行状态、线程的活动数、任务队列的长度等,还可以根据需求进行动态调整线程池的大小、任务队列的长度等。
8. 关闭线程池:当不再需要线程池时,可以调用shutdown方法来关闭线程池。
关闭线程池后,线程池不再接受新的任务,但会继续执行已提交的任务。
可以通过awaitTermination方法等待线程池中所有任务执行完毕,然后关闭线程池。
java线程池的使用例子
java线程池的使用例子随着计算机技术的不断发展,我们的软件系统越来越复杂,程序的性能要求也越来越高。
在这样的背景下,线程池成为了一种非常重要的工具。
Java线程池是Java提供的一种简单易用的线程管理工具,可以帮助我们更好地管理程序中的线程,提高程序的性能和稳定性。
本文将通过一个实际的例子来介绍Java线程池的使用方法和注意事项。
希望读者可以通过本文的学习,更好地掌握Java线程池的使用技巧。
一、什么是线程池?在介绍Java线程池之前,我们需要先了解什么是线程池。
线程池是一种管理线程的机制,可以帮助我们更好地管理程序中的线程,提高程序的性能和稳定性。
线程池的主要作用是为每个任务分配一个线程,当任务完成后,线程会被回收并可供下一个任务使用。
这样,线程的创建和销毁的开销就可以得到控制,避免了频繁创建和销毁线程所带来的性能损失。
二、Java线程池的使用方法1. 创建线程池Java线程池的创建方式非常简单,只需要使用ThreadPoolExecutor类即可。
以下是一个简单的线程池创建代码: ```ExecutorService executor =Executors.newFixedThreadPool(5);```这个代码创建了一个固定大小为5的线程池。
如果需要创建其他类型的线程池,可以使用其他的静态工厂方法,如newCachedThreadPool()、newSingleThreadExecutor()等。
2. 提交任务创建好线程池之后,我们就可以向线程池提交任务了。
以下是一个简单的线程池提交任务代码:```executor.submit(new Runnable() {@Overridepublic void run() {// 执行任务}});```这个代码提交了一个Runnable类型的任务,线程池会自动为其分配一个线程执行。
如果需要提交其他类型的任务,可以使用Callable、Future等接口。
JAVA多线程的使用场景与注意事项总结
JAVA多线程的使用场景与注意事项总结Java多线程是指在一个程序中同时运行多个线程,每个线程都有自己的执行代码,但是又共享同一片内存空间和其他系统资源。
多线程的使用场景和注意事项是我们在开发中需要关注的重点,下面将详细进行总结。
一、Java多线程的使用场景:1.提高程序的执行效率:多线程可以充分利用系统资源,将一些耗时的操作放到一个线程中执行,避免阻塞主线程,提高程序的执行效率。
2.实现并行计算:多线程可以将任务拆分成多个子任务,每个子任务分配给一个线程来执行,从而实现并行计算,提高计算速度。
3.响应性能提升:多线程可以提高程序的响应性能,比如在用户界面的开发中,可以使用多线程来处理用户的输入和操作,保证界面的流畅性和及时响应。
4.实时性要求高:多线程可以实现实时性要求高的任务,比如监控系统、实时数据处理等。
5.任务调度与资源管理:多线程可以实现任务的调度和资源的管理,通过线程池可以更好地掌控任务的执行情况和使用系统资源。
二、Java多线程的注意事项:1.线程安全性:多线程操作共享资源时,要注意线程安全问题。
可以通过使用锁、同步方法、同步块等方式来解决线程安全问题。
2.死锁:多线程中存在死锁问题,即多个线程相互等待对方释放资源,导致程序无法继续执行。
要避免死锁问题,应尽量减少同步块的嵌套和锁的使用。
3.内存泄漏:多线程中存在内存泄漏问题,即线程结束后,线程的资源没有得到释放,导致内存占用过高。
要避免内存泄漏问题,应及时释放线程资源。
4.上下文切换:多线程的切换会带来上下文切换的开销,影响程序的执行效率。
要注意合理分配线程的数量,避免过多线程的切换。
5. 线程同步与通信:多线程之间需要进行同步和通信,以保证线程之间的正确协调和数据的一致性。
可以使用synchronized关键字、wait(和notify(方法等方式进行线程同步和通信。
6.线程池的使用:在多线程编程中,可以使用线程池来管理线程的创建和销毁,可以减少线程的创建和销毁的开销,提高程序的性能。
Java千万级别数据处理与优化
Java千万级别数据处理与优化随着互联网的发展,数据规模异常的庞大。
对于Java开发人员来说,面对这种情况,我们需要从性能和优化的角度思考,从而使我们的程序在处理海量数据时更有效率、更加可靠。
一、数据处理1. 数据读取优化数据读取是数据处理的第一步,良好的数据读取优化可以最大限度地提高程序的效率。
在数据读取方面,我们需要注重以下几个方面的优化:(1)缓存读取:对于内存中需要反复读取的数据,我们应该缓存读取,避免多次访问磁盘带来的性能损耗。
(2)文件切割:对于大文件的读取,我们可以将其切割成多个小文件,以便于多线程同时读取,并且有效减少了每个线程读取文件大小的开销。
(3)使用BufferedInputStream和BufferedReader:Java中提供了BufferedInputStream和BufferedReader这两种缓存读取的方式,可以有效地提高文件读取的效率。
2. 数据存储优化在面对千万级别的数据时,数据的存储也是我们需要优化的重要环节。
在数据存储方面,我们需要注重以下几个方面的优化:(1)使用内存存储:对于频繁读写的数据,我们应该尽量使用内存存储,避免频繁的磁盘读写,从而提高程序效率。
(2)使用NoSQL数据库:对于大规模的数据存储,我们可以使用NoSQL数据库来代替传统的关系型数据库,NoSQL数据库对分布式存储的支持更加完善,可以提高数据存储的效率。
(3)批量操作:在实际开发中,我们应该尽量采用批量操作的方式进行数据存储,这样可以有效提高数据处理的效率。
二、数据处理算法1. 分治算法分治算法是一种非常常见的算法,可以用于解决很多常见的数据处理问题。
在数据分析处理过程中,分治算法可以用来将庞大的数据集划分成多个小数据集,然后并行处理每个小数据集,最后合并结果。
通过这种方法,可以有效提高程序的处理效率。
2. MapReduce算法MapReduce算法是一种分布式数据处理算法,可以对千万级别的数据进行优化。
Java大规模数据处理解析海量数据的技巧
Java大规模数据处理解析海量数据的技巧在处理大规模数据时,Java是一种常用的编程语言。
然而,由于海量数据的处理可能涉及到效率、内存管理以及算法优化等方面的挑战,开发人员需要掌握一些技巧来解析这些数据。
本文将介绍一些Java大规模数据处理的技巧,帮助开发人员更好地处理海量数据。
一、数据分块处理在处理大规模数据时,内存管理是一个重要的问题。
当数据量超过内存限制时,我们需要将数据分块处理,以避免内存溢出。
可以使用Java的流式处理机制,通过迭代的方式读取数据,每次处理一块数据,减少内存的消耗。
例如,可以使用BufferedReader的readLine()方法逐行读取文件,然后对每行数据进行处理。
二、并行处理并行处理是指同时处理多个数据块的技术,可以显著提高处理大规模数据的效率。
Java提供了多线程和线程池的机制,可以将数据分成多个部分,并行地处理每个部分。
通过合理设置线程池的大小,可以充分利用计算资源,提高程序的运行效率。
三、使用适当的数据结构在处理大规模数据时,选择适当的数据结构非常重要。
不同的数据结构对于不同的操作具有不同的时间复杂度,选择合适的数据结构可以提高程序的效率。
例如,如果需要频繁地插入和删除数据,可以选择链表或树等数据结构;如果需要随机访问数据,可以选择数组或哈希表等数据结构。
根据不同的需求,选择合适的数据结构可以提高程序的性能。
四、优化算法算法的选择也是解析海量数据的关键。
优化算法可以提高程序的效率,减少资源的消耗。
例如,对于排序操作,可以选择高效的排序算法,如快速排序或归并排序,而不是简单的冒泡排序。
另外,可以使用适当的数据结构和算法来进行数据过滤、去重等操作,减少不必要的计算。
五、使用缓存缓存是提高程序性能的有效方式之一。
当程序需要频繁地访问某些数据时,可以使用缓存将这些数据存储起来,避免重复计算和访问。
在Java中,可以使用HashMap等数据结构来实现缓存。
通过在内存中存储一部分数据,可以提高程序的响应速度和效率。
java 线程池 purge方法
java 线程池 purge方法Java线程池是Java多线程编程中非常重要的概念和工具。
在多线程编程中,线程池可以有效地管理和控制线程的创建和销毁,提高程序的性能和稳定性。
而线程池的`purge()`方法则是线程池中的一个重要方法,用于清除线程池中已经完成的任务。
我们来了解一下线程池的基本概念。
线程池是一种线程管理的机制,它包含了一组线程,用于执行提交的任务。
通过线程池,我们可以避免频繁创建和销毁线程的开销,提高线程的复用性和执行效率。
Java提供了`java.util.concurrent`包下的`Executor`接口和`ExecutorService`接口来支持线程池的使用。
在Java线程池中,`purge()`方法是一个非常有用的方法。
它的作用是清除线程池中已经完成的任务,以便释放内存资源。
当线程池中的任务执行完毕后,这些任务会被保留在线程池中,等待下次使用。
而`purge()`方法可以清除这些已经完成的任务,使得线程池中只保留尚未执行的任务,避免占用过多的内存资源。
那么,我们如何使用`purge()`方法呢?在Java中,线程池是通过`ExecutorService`接口的实现类来创建和管理的。
要使用`purge()`方法,我们首先需要创建一个线程池对象。
可以使用`Executors`类提供的静态方法来创建不同类型的线程池,例如`newFixedThreadPool()`、`newCachedThreadPool()`等。
创建完线程池对象后,我们可以通过调用`purge()`方法来清除已经完成的任务。
`purge()`方法会返回线程池中已经完成的任务数量,并将这些任务从线程池中移除。
这样,我们就可以及时释放内存资源,防止内存泄漏的发生。
需要注意的是,`purge()`方法只能清除已经完成的任务,对于正在运行的任务无法清除。
如果想要终止正在运行的任务,可以使用`shutdownNow()`方法来强制停止线程池中的所有任务。
java线程池异步调用方法
java线程池异步调用方法Java线程池可以用于异步调用方法。
实现步骤如下:1. 定义一个线程池对象javaExecutorService executorService = Executors.newFixedThreadPool(10);2. 定义一个Callable对象,用于异步运行方法javaCallable<String> callable = () -> {执行异步方法return "result";};3. 提交Callable对象到线程池中,并获得Future对象javaFuture<String> future = executorService.submit(callable);4. 使用Future对象获取异步执行方法的结果javaString result = future.get();完整示例代码如下:javaimport java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class ThreadPoolExample {public static void main(String[] args) throws Exception {ExecutorService executorService =Executors.newFixedThreadPool(10);Callable<String> callable = () -> {执行异步方法return "result";};Future<String> future = executorService.submit(callable);String result = future.get();System.out.println(result);executorService.shutdown();}}。
使用java多线程分批处理数据工具类
使⽤java多线程分批处理数据⼯具类最近由于业务需要,数据量⽐较⼤,需要使⽤多线程来分批处理,提⾼处理效率和能⼒,于是就写了⼀个通⽤的多线程处理⼯具,只需要实现⾃⼰的业务逻辑就可以正常使⽤,现在记录⼀下主要是针对⼤数据量list,将list划分多个线程处理ResultBean类:返回结果统⼀beanpackage mon.model;import java.io.Serializable;import com.alibaba.fastjson.JSON;/*** 返回结果统⼀bean** ResultBean<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年4⽉12⽇-下午3:49:46 <BR>* @version 2.0**/public class ResultBean<T> implements Serializable {private static final long serialVersionUID = 1L;// 成功状态public static final int SUCCESS = 1;// 处理中状态public static final int PROCESSING = 0;// 失败状态public static final int FAIL = -1;// 描述private String msg = "success";// 状态默认成功private int code = SUCCESS;// 备注private String remark;// 返回数据private T data;public ResultBean() {super();}public ResultBean(T data) {super();this.data = data;}/*** 使⽤异常创建结果*/public ResultBean(Throwable e) {super();this.msg = e.toString();this.code = FAIL;}/**** 实例化结果默认成功状态<BR>* ⽅法名:newInstance<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年4⽉12⽇-下午3:51:26 <BR>* @return ResultBean<T><BR>* @exception <BR>* @since 2.0*/public static <T> ResultBean<T> newInstance() {ResultBean<T> instance = new ResultBean<T>();//默认返回信息instance.code = SUCCESS;/**** 实例化结果默认成功状态和数据<BR>* ⽅法名:newInstance<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年5⽉10⽇-下午2:13:16 <BR>* @param data* @return ResultBean<T><BR>* @exception <BR>* @since 2.0*/public static <T> ResultBean<T> newInstance(T data) {ResultBean<T> instance = new ResultBean<T>();//默认返回信息instance.code = SUCCESS;instance.msg = "success";instance.data = data;return instance;}/**** 实例化返回结果<BR>* ⽅法名:newInstance<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年4⽉12⽇-下午4:00:53 <BR>* @param code* @param msg* @return ResultBean<T><BR>* @exception <BR>* @since 2.0*/public static <T> ResultBean<T> newInstance(int code, String msg) {ResultBean<T> instance = new ResultBean<T>();//默认返回信息instance.code = code;instance.msg = msg;return instance;}/**** 实例化返回结果<BR>* ⽅法名:newInstance<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年4⽉12⽇-下午4:00:35 <BR>* @param code* @param msg* @param data* @return ResultBean<T><BR>* @exception <BR>* @since 2.0*/public static <T> ResultBean<T> newInstance(int code, String msg, T data) { ResultBean<T> instance = new ResultBean<T>();//默认返回信息instance.code = code;instance.msg = msg;instance.data = data;return instance;}/**** 设置返回数据<BR>* ⽅法名:setData<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年4⽉12⽇-下午3:52:01 <BR>* @param data* @return ResultBean<T><BR>* @exception <BR>* @since 2.0*/public ResultBean<T> setData(T data){this.data = data;return this;}/**** 设置结果描述<BR>* @return ResultBean<T><BR>* @exception <BR>* @since 2.0*/public ResultBean<T> setMsg(String msg){this.msg = msg;return this;}/**** 设置状态<BR>* ⽅法名:setCode<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年4⽉12⽇-下午4:17:56 <BR>* @param code* @return ResultBean<T><BR>* @exception <BR>* @since 2.0*/public ResultBean<T> setCode(int code){this.code = code;return this;}/**** 设置备注)<BR>* ⽅法名:setRemark<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年4⽉12⽇-下午5:47:29 <BR>* @param remark* @return ResultBean<T><BR>* @exception <BR>* @since 2.0*/public ResultBean<T> setRemark(String remark){this.remark = remark;return this;}/**** 设置成功描述和返回数据<BR>* ⽅法名:success<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年4⽉12⽇-下午3:52:58 <BR>* @param msg* @param data* @return ResultBean<T><BR>* @exception <BR>* @since 2.0*/public ResultBean<T> success(String msg, T data){ this.code = SUCCESS;this.data = data;this.msg = msg;return this;}/**** 设置成功返回结果描述<BR>* ⽅法名:success<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年4⽉12⽇-下午3:53:31 <BR>* @param msg* @return ResultBean<T><BR>* @exception <BR>* @since 2.0*/public ResultBean<T> success(String msg){this.code = SUCCESS;this.msg = msg;return this;}/**** 设置处理中描述和返回数据<BR>* @param data* @return ResultBean<T><BR>* @exception <BR>* @since 2.0*/public ResultBean<T> processing(String msg, T data){ this.code = PROCESSING;this.data = data;this.msg = msg;return this;}/**** 设置处理中返回结果描述<BR>* ⽅法名:success<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年4⽉12⽇-下午3:53:31 <BR>* @param msg* @return ResultBean<T><BR>* @exception <BR>* @since 2.0*/public ResultBean<T> processing(String msg){this.code = PROCESSING;this.msg = msg;return this;}/**** 设置失败返回描述和返回数据<BR>* ⽅法名:fail<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年4⽉12⽇-下午3:54:04 <BR>* @param msg* @param data* @return ResultBean<T><BR>* @exception <BR>* @since 2.0*/public ResultBean<T> fail(String msg, T data){this.code = FAIL;this.data = data;this.msg = msg;return this;}/**** 设置失败返回描述<BR>* ⽅法名:fail<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年4⽉12⽇-下午3:54:32 <BR>* @param msg* @return ResultBean<T><BR>* @exception <BR>* @since 2.0*/public ResultBean<T> fail(String msg){this.code = FAIL;this.msg = msg;return this;}public T getData() {return data;}public String getMsg() {return msg;}public int getCode() {return code;}public String getRemark() {return remark;}/**** 时间:2018年4⽉12⽇-下午4:42:28 <BR>* @return String<BR>* @exception <BR>* @since 2.0*/public String json(){return JSON.toJSONString(this);}}View CodeITask接⼝:实现⾃⼰的业务package mon.multi.execute;import java.util.Map;/*** 任务处理接⼝* 具体业务逻辑可实现该接⼝* T 返回值类型* E 传⼊值类型* ITask<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年8⽉4⽇-下午6:12:32 <BR>* @version 2.0**/public interface ITask<T, E> {/**** 任务执⾏⽅法接⼝<BR>* ⽅法名:execute<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年8⽉4⽇-下午6:13:44 <BR>* @param e 传⼊对象* @param params 其他辅助参数* @return T<BR> 返回值类型* @exception <BR>* @since 2.0*/T execute(E e, Map<String, Object> params);}View CodeHandleCallable类:实现Callable接⼝,来处理任务package mon.multi.execute;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.concurrent.Callable;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import mon.model.ResultBean;/***** HandleCallable<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年8⽉4⽇-上午11:55:41 <BR>** @version 2.0**/@SuppressWarnings("rawtypes")public class HandleCallable<E> implements Callable<ResultBean> {private static Logger logger = LoggerFactory.getLogger(HandleCallable.class); // 线程名称private String threadName = "";private Map<String, Object> params;// 具体执⾏任务private ITask<ResultBean<String>, E> task;public HandleCallable(String threadName, List<E> data, Map<String, Object> params, ITask<ResultBean<String>, E> task) {this.threadName = threadName;this.data = data;this.params = params;this.task = task;}@Overridepublic ResultBean<List<ResultBean<String>>> call() throws Exception {// 该线程中所有数据处理返回结果ResultBean<List<ResultBean<String>>> resultBean = ResultBean.newInstance();if (data != null && data.size() > 0) {("线程:{},共处理:{}个数据,开始处理......", threadName, data.size());// 返回结果集List<ResultBean<String>> resultList = new ArrayList<>();// 循环处理每个数据for (int i = 0; i < data.size(); i++) {// 需要执⾏的数据E e = data.get(i);// 将数据执⾏结果加⼊到结果集中resultList.add(task.execute(e, params));("线程:{},第{}个数据,处理完成", threadName, (i + 1));}("线程:{},共处理:{}个数据,处理完成......", threadName, data.size()); resultBean.setData(resultList);}return resultBean;}}View CodeMultiThreadUtils类: 多线程⼯具类package mon.multi.execute;import java.util.ArrayList;import java.util.List;import java.util.Map;import pletionService;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorCompletionService;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import mon.model.ResultBean;/***** MultiThreadUtils<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年8⽉8⽇-下午8:20:42 <BR>* @version 2.0**/public class MultiThreadUtils<T> {private static Logger logger = LoggerFactory.getLogger(MultiThreadUtils.class);// 线程个数,如不赋值,默认为5private int threadCount = 5;// 具体业务任务private ITask<ResultBean<String>, T> task;// 线程池管理器private CompletionService<ResultBean> pool = null;/**** 初始化线程池和线程个数<BR>* ⽅法名:newInstance<BR>* @return MultiThreadUtils<BR>* @exception <BR>* @since 2.0*/public static MultiThreadUtils newInstance(int threadCount) {MultiThreadUtils instance = new MultiThreadUtils();threadCount = threadCount;instance.setThreadCount(threadCount);return instance;}/**** 多线程分批执⾏list中的任务<BR>* ⽅法名:execute<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年8⽉8⽇-下午8:22:31 <BR>* @param data 线程处理的⼤数据量list* @param params 处理数据是辅助参数传递* @param task 具体执⾏业务的任务接⼝* @return ResultBean<BR>* @exception <BR>* @since 2.0*/@SuppressWarnings("rawtypes")public ResultBean execute(List<T> data, Map<String, Object> params, ITask<ResultBean<String>, T> task) { // 创建线程池ExecutorService threadpool = Executors.newFixedThreadPool(threadCount);// 根据线程池初始化线程池管理器pool = new ExecutorCompletionService<ResultBean>(threadpool);// 开始时间(ms)long l = System.currentTimeMillis();// 数据量⼤⼩int length = data.size();// 每个线程处理的数据个数int taskCount = length / threadCount;// 划分每个线程调⽤的数据for (int i = 0; i < threadCount; i++) {// 每个线程任务数据listList<T> subData = null;if (i == (threadCount - 1)) {subData = data.subList(i * taskCount, length);} else {subData = data.subList(i * taskCount, (i + 1) * taskCount);}// 将数据分配给各个线程HandleCallable execute = new HandleCallable<T>(String.valueOf(i), subData, params, task);// 将线程加⼊到线程池pool.submit(execute);}// 总的返回结果集List<ResultBean<String>> result = new ArrayList<>();for (int i = 0; i < threadCount; i++) {// 每个线程处理结果集ResultBean<List<ResultBean<String>>> threadResult;try {threadResult = pool.take().get();result.addAll(threadResult.getData());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}// 关闭线程池threadpool.shutdownNow();// 执⾏结束时间long end_l = System.currentTimeMillis();("总耗时:{}ms", (end_l - l));return ResultBean.newInstance().setData(result);}public int getThreadCount() {return threadCount;}public void setThreadCount(int threadCount) {this.threadCount = threadCount;View Code测试类TestTaskpackage mon.multi.execute;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import mon.model.ResultBean;/**** 具体执⾏业务任务需要实现ITask接⼝在execute中重写业务逻辑* TestTask<BR>* 创建⼈:wangbeidou <BR>* 时间:2018年8⽉8⽇-下午8:40:32 <BR>* @version 2.0**/public class TestTask implements ITask<ResultBean<String>, Integer> {@Overridepublic ResultBean execute(Integer e, Map<String, Object> params) {/*** 具体业务逻辑:将list中的元素加上辅助参数中的数据返回*/int addNum = Integer.valueOf(String.valueOf(params.get("addNum")));e = e + addNum;ResultBean<String> resultBean = ResultBean.newInstance();resultBean.setData(e.toString());return resultBean;}public static void main(String[] args) {// 需要多线程处理的⼤量数据listList<Integer> data = new ArrayList<>(10000);for(int i = 0; i < 10000; i ++){data.add(i + 1);}// 创建多线程处理任务MultiThreadUtils<Integer> threadUtils = MultiThreadUtils.newInstance(5);ITask<ResultBean<String>, Integer> task = new TestTask();// 辅助参数加数Map<String, Object> params = new HashMap<>();params.put("addNum", 4);// 执⾏多线程处理,并返回处理结果ResultBean<List<ResultBean<String>>> resultBean = threadUtils.execute(data, params, task); }}。
Java线程池使用和常用参数
Java线程池使⽤和常⽤参数多线程问题:1、java中为什么要使⽤多线程使⽤多线程,可以把⼀些⼤任务分解成多个⼩任务来执⾏,多个⼩任务之间互不影像,同时进⾏,这样,充分利⽤了cpu资源。
2、java中简单的实现多线程的⽅式继承Thread类,重写run⽅法;12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28class MyTread extends Thread{public void run() { System.out.println(Thread.currentThread().getName());}}实现Runable接⼝,实现run⽅法;class MyRunnable implements Runnable{ public void run() { System.out.println(Thread.currentThread().getName()); }}class ThreadTest { public static void main(String[] args) { MyTread thread = new Mythread(); thread.start(); //开启⼀个线程 MyRunnable myRunnable = new MyRunnable(); Thread runnable = new Thread(myRunnable); runnable.start(); //开启⼀个线程 }}3、java线程的状态创建:当new了⼀个线程,并没有调⽤start之前,线程处于创建状态;就绪:当调⽤了start之后,线程处于就绪状态,这是,线程调度程序还没有设置执⾏当前线程;运⾏:线程调度程序执⾏到线程时,当前线程从就绪状态转成运⾏状态,开始执⾏run⽅法⾥边的代码;阻塞:线程在运⾏的时候,被暂停执⾏(通常等待某项资源就绪后在执⾏,sleep、wait可以导致线程阻塞),这是该线程处于阻塞状态;死亡:当⼀个线程执⾏完run⽅法⾥边的代码或调⽤了stop⽅法后,该线程结束运⾏4、为什么要引⼊线程池当我们需要的并发执⾏线程数量很多时,且每个线程执⾏很短的时间就结束了,这样,我们频繁的创建、销毁线程就⼤⼤降低了⼯作效率(创建和销毁线程需要时间、资源)。
完整的后端开发流程-深入浅出Java线程池:使用篇
完整的后端开发流程-深⼊浅出Java线程池:使⽤篇⼿动步骤⾛⼀种完整的后端开发流程服务端1、将远程仓库的jar包拷贝到本地仓库2、将项⽬代码拷贝到本地并建⽴路径能够执⾏编译3、编译打包项⽬(package)⾄项⽬下,项⽬跑起来后进⾏本地测试4、版本稳定后,上测试环境上测试环境1、将远程仓库的jar包拷贝到测试环境2、将本地的项⽬代码上传到测试环境 pom能建⽴路径执⾏mvn脚本进⾏编译打包3、编译打包项⽬(package)⾄项⽬下,项⽬跑起来后进⾏测试4、版本在测试环境稳定后,install⾄本地仓库,在上传⾄远程仓库5、不推荐嫌⿇烦直接上传本地jar包的⽅式,因为这样⽆法发现由于环境造成的错误⽽且传输速度没有直接编译的快客户端联调1、将远程仓库的jar包(包括刚刚上传的服务端jar) 拷贝到本地仓库2、将项⽬代码拷贝到本地并建⽴路径能够执⾏编译3、编译打包项⽬(package)⾄项⽬下,项⽬跑起来后进⾏本地测试4、项⽬注册⾄RPC服务中来访问跑在测试环境的服务端项⽬5、版本稳定后,上测试环境联调。
团队的技术栈,基于这个背景再展开后⾯将提到的⼏个问题,将会有更深刻的体会。
控制层基于SpringMvc,数据持久层基于JdbcTemplate⾃⼰封装了⼀套类MyBatis的Dao框架,视图层基于Velocity模板技术,其余组件基于SpringCloud全家桶。
问题1某应⽤发布以后开始报数据库连接池不够⽤异常,⽇志如下:1com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 500, maxActive 500, creating 0 很明显这是数据库连接池满了,当时处于业务低峰期,所以很显然并不是由于流量突发造成的,另⼀种可能性是长事务导致,⼀般是事务中掺杂了外部⽹络调⽤,最终跟业务负责⼈⼀起排除了长事务的可能性。
java threadpoolexecutor用法 -回复
java threadpoolexecutor用法-回复Java ThreadPoolExecutor是Java中的一个线程池管理器,它提供了一种有效的方式来管理和复用线程资源,以提高多线程应用程序的性能和效率。
本文将一步一步回答有关ThreadPoolExecutor的用法和常见问题。
第一步:导入ThreadPoolExecutor类要使用ThreadPoolExecutor类,首先要在代码中导入它。
在Java中,可以使用以下代码导入ThreadPoolExecutor类:import java.util.concurrent.ThreadPoolExecutor;第二步:创建ThreadPoolExecutor实例创建一个ThreadPoolExecutor实例是使用该类的第一步。
可以使用以下代码创建一个线程池管理器:ThreadPoolExecutor executor = newThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);在这里,需要指定以下参数:- corePoolSize:核心线程池的大小。
核心线程池是一直存在的线程,不会被销毁,即使它们处于空闲状态。
- maximumPoolSize:线程池的最大大小。
线程池的大小可以动态地增加到这个值,以处理更多的任务。
- keepAliveTime:当线程池中的线程数超过核心线程池大小,并且处于空闲状态时,线程池会等待一定的时间,然后销毁这些空闲的线程。
- unit:keepAliveTime的时间单位。
- workQueue:用于存储待执行的任务的阻塞队列。
第三步:提交任务给ThreadPoolExecutor一旦创建了ThreadPoolExecutor对象,就可以使用以下代码将任务提交给线程池:executor.execute(task);在这里,task是一个实现了Runnable接口的任务对象。
java threadpoolexecutor用法 -回复
java threadpoolexecutor用法-回复Java ThreadPoolExecutor用法介绍:Java ThreadPoolExecutor是Java语言提供的用于管理和调度线程池的类。
它提供了一种方便的方式来处理异步任务,实现线程的复用以及控制线程池中的线程数量。
本文将详细介绍ThreadPoolExecutor的用法,包括创建线程池、添加任务、设置参数以及监控线程池的状态等。
一、创建线程池:在使用ThreadPoolExecutor之前,需要先创建一个线程池。
创建线程池的方式有两种:一种是直接创建ThreadPoolExecutor实例,另一种是使用Executors工具类提供的静态方法来创建不同类型的线程池。
1. 直接创建ThreadPoolExecutor实例:ThreadPoolExecutor线程池构造函数的参数包括:- corePoolSize:核心线程池大小,即线程池中保持的最小线程数。
- maximumPoolSize:线程池允许创建的最大线程数。
- keepAliveTime:线程池中超过核心线程数的空闲线程的存活时间。
- unit:keepAliveTime的时间单位。
- workQueue:用于保存等待执行的任务的阻塞队列。
- threadFactory:线程工厂,用于创建新线程。
- handler:当线程池的任务队列已满且线程池中的线程数达到最大线程数时的拒绝策略。
以下是一个创建线程池的示例代码:ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);2. 使用Executors工具类创建线程池:Executors类提供了一些静态方法,用于创建不同类型的线程池。
java线程池的工作原理
java线程池的工作原理
Java线程池的工作原理如下:
1. 线程池的初始化:在使用线程池之前,需要首先创建一个线程池对象,并设定线程池的核心线程数、最大线程数、线程空闲时间等参数。
2. 任务提交:当有任务需要执行时,可以使用线程池的
submit()或execute()方法将任务提交给线程池。
3. 任务队列:线程池会维护一个任务队列,用于存储提交的任务。
如果线程池中的线程数没有达到核心线程数,线程池就会创建新的线程来执行任务。
如果线程池中的线程数已经达到核心线程数,但任务队列仍然可以存储新任务,线程池会将新任务存储在任务队列中。
4. 线程池的工作方式:线程池会不断地从任务队列中取出任务,并通过线程池中的线程来执行任务。
若线程池中的线程处于空闲状态,则会被重新利用,否则任务会等待直到有线程可用。
5. 线程池的扩容:当任务队列已满且线程池中的线程数未达到最大线程数时,线程池会创建新的线程来执行任务。
一旦线程数达到最大线程数,线程池将不再接受新的任务。
6. 线程池的关闭:当不再需要线程池时,可以调用线程池的shutdown()方法来关闭线程池。
关闭线程池后,线程池将不再
接受新的任务,同时会等待已提交的任务执行完毕。
可以使用
awaitTermination()方法来等待所有任务执行完毕。
线程池的好处是提高了线程的利用率,避免了频繁创建和销毁线程的开销。
同时可以控制线程的并发数,防止系统资源过度消耗。
Java中的大规模数据处理如何应对海量数据
Java中的大规模数据处理如何应对海量数据随着互联网的迅速发展和智能设备的普及,我们所面对的数据量越来越大,特别是在大数据领域。
在这种背景下,如何高效地处理海量数据成为了一个重要的问题。
Java作为一种强大的编程语言,具备很好的数据处理能力,本文将介绍Java中应对海量数据的一些常用方法和工具。
一、分布式计算框架在海量数据处理中,分布式计算框架是一种常见的解决方案。
它将数据分割成多个小块,并通过多台服务器进行并行处理,从而提高整体的计算效率。
在Java领域中,Apache Hadoop和Apache Spark是两个常用的分布式计算框架。
1. Apache HadoopApache Hadoop是一个基于Java的开源分布式计算框架,它采用了MapReduce思想。
MapReduce将数据分割成多个小块,并通过多个计算节点进行并行计算。
Hadoop提供了HDFS(分布式文件系统)用于存储海量数据,还提供了MapReduce编程模型用于实现分布式计算。
使用Hadoop可以有效地处理海量数据,提高计算效率。
2. Apache SparkApache Spark是一个快速、通用的分布式计算系统,它同样基于Java语言。
与Hadoop不同的是,Spark提供了一种内存计算方式,可以更快地处理海量数据。
Spark还提供了丰富的API,支持多种数据处理场景,包括批处理、流处理和机器学习等。
通过合理使用Spark的API,我们可以灵活地处理海量数据。
二、数据分片和分区在大规模数据处理中,数据分片和分区是一种常见的方式。
通过将数据划分成多个小块,可以提高数据处理的效率。
1. 数据分片数据分片是将大数据集划分成多个小块,每个小块可以在不同的计算节点上进行并行处理。
在Java中,我们可以使用Hadoop的MapReduce编程模型来实现数据分片。
2. 数据分区数据分区是将数据集分成多个逻辑分区,每个分区可以在不同的计算节点上进行并行计算。
hutool线程池用法
hutool线程池用法一、引言在Java编程中,线程池是一种常用的技术,用于管理线程的创建和销毁,以提高程序的性能和效率。
Hutool是一个Java工具包,提供了丰富的实用工具和功能,其中包括线程池的实现。
本文将详细介绍Hutool中的线程池用法。
Hutool中的线程池实现基于Java的Executor框架,提供了固定大小线程池和可调整大小线程池两种类型。
通过线程池,可以避免频繁创建和销毁线程带来的性能开销,同时也可以充分利用系统资源,减少系统资源的浪费。
三、创建线程池使用Hutool创建线程池非常简单。
可以通过调用相应的方法来创建一个固定大小或可调整大小线程池。
例如,创建一个固定大小为3的线程池可以使用以下代码:ThreadPoolExecutorthreadPool=newThreadPoolExecutor(3);对于可调整大小线程池,可以使用以下代码创建一个初始大小为2,最大大小为5的线程池:ThreadPoolExecutorthreadPool=newThreadPoolExecutor(2,5);在创建线程池时,还可以设置一些参数,如核心线程数、工作队列、拒绝策略等。
Hutool提供了默认的配置,可以根据需要进行调整。
四、提交任务创建完线程池后,可以使用其相关方法提交任务。
可以通过调用execute()或submit()方法来提交任务。
execute()方法用于执行无需返回结果的任务,而submit()方法用于提交需要返回结果的任务。
提交任务后,线程池会自动分配一个空闲线程来执行任务。
如果所有核心线程都在执行任务,则会等待新的任务到来或等待现有任务完成。
五、关闭线程池在完成任务提交后,应该及时关闭线程池,释放系统资源。
可以使用shutdown()方法关闭线程池,释放其占用的资源。
此外,还可以调用shutdownNow()方法来停止所有正在执行的任务,并返回尚未执行的任务列表。
六、总结Hutool中的线程池实现提供了方便快捷的方法来管理线程,避免了频繁创建和销毁线程带来的性能开销。
java threadpoolexecutor 参数
Java中的ThreadPoolExecutor类允许您创建并管理一个线程池。
您可以通过以下参数配置ThreadPoolExecutor:1. corePoolSize:核心线程池大小,即线程池中保持活动的最小线程数。
当提交的任务数量小于corePoolSize时,线程池中的线程数不会超过这个值。
2. maximumPoolSize:线程池能够容纳的最大线程数。
如果队列满了,并且当前线程数小于maximumPoolSize,则创建新线程执行任务。
3. keepAliveTime:当线程数大于corePoolSize时,多余的空闲线程在终止之前等待新任务的最长时间。
4. TimeUnit:keepAliveTime的时间单位,可以是秒、分钟、小时等。
5. workQueue:用于存储等待执行任务的队列。
它可以是以下类型之一:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue或PriorityBlockingQueue。
6. ThreadFactory:用于创建新线程的工厂。
您可以使用默认的ThreadFactory,也可以提供自定义的ThreadFactory来自定义线程的创建方式。
7. RejectedExecutionHandler:当任务被拒绝时触发的拒绝策略。
可以选择以下拒绝策略之一:AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy或DiscardPolicy。
这些参数允许您根据应用程序的需求配置ThreadPoolExecutor。
例如,如果您希望线程池中的线程数始终保持在一定范围内,可以将corePoolSize和maximumPoolSize设置为相同的值。
如果您希望在任务等待时缓存空闲线程,可以将keepAliveTime设置为一个非零值。
java 线程池 参数
java 线程池参数在Java 中,线程池是一种用于管理和重用线程的机制,它可以在执行大量任务时提供更好的性能和资源管理。
Java 提供了`java.util.concurrent` 包,其中包括`Executor` 框架,用于创建和管理线程池。
在创建线程池时,你可以使用不同的参数来配置线程池的行为。
以下是一些常见的线程池参数:1. corePoolSize(核心线程数):-定义了线程池中保持活动状态的最小线程数。
即使线程处于空闲状态,核心线程也会一直保持活动。
线程池在没有任务执行时也不会销毁这些核心线程。
2. maximumPoolSize(最大线程数):-定义了线程池中允许存在的最大线程数。
当工作队列已满并且有新任务提交时,线程池会创建新的线程,直到达到最大线程数。
3. keepAliveTime(线程空闲时间):-当线程池中的线程数超过核心线程数时,多余的空闲线程在被终止之前等待新任务的时间。
如果在这段时间内没有新任务到达,则这些空闲线程将被终止,直到线程数等于核心线程数。
4. TimeUnit(时间单位):-与`keepAliveTime` 一起使用,指定了时间的单位,可以是秒、毫秒、微秒等。
5. workQueue(工作队列):-用于保存等待执行的任务的队列。
线程池会从这个队列中取出任务来执行。
Java 提供了不同种类的队列,如`LinkedBlockingQueue`、`ArrayBlockingQueue` 等。
6. ThreadFactory(线程工厂):-用于创建新线程的工厂。
可以通过实现`ThreadFactory` 接口来自定义线程的创建过程。
7. RejectedExecutionHandler(拒绝策略):-定义了当工作队列和线程池的最大线程数都达到上限,无法处理新任务时的处理策略。
常见的策略包括抛出异常、丢弃任务、直接执行等。
这些参数可以在使用`ThreadPoolExecutor` 类或`Executors` 工厂类创建线程池时进行配置。
线程池使用场景
线程池使用场景
线程池使用场景
一、什么是线程池?
线程池是指创建一个可复用的线程集,这些线程可以被重复利用,而不需要每次都创建新线程。
它可以帮助我们避免在系统中创建大量线程,从而减少内存消耗、节省时间开销和CPU开销。
二、线程池使用场景
1. 后台服务
后台服务通常会执行长时间的任务,这些任务可能需要运行几小时或几天,线程池可以很好地处理这类任务,因为它可以缓存线程,减少对系统资源的占用。
2. 并发任务
使用线程池可以有效地处理大量并发请求,如在Web 应用程序中处理HTTP请求,线程池可以更快地处理这些请求,并且可以更好地控制系统资源的分配。
3. 批量任务
当需要处理大量相似的任务时,可以使用线程池来提高效率,比如在数据库中批量处理数据,使用线程池可以加快处理速度。
4.实时任务
如果有一系列的实时任务,可以使用线程池来处理,比如在游戏中,可以使用线程池来处理实时的玩家操作,这样可以更好地分配系统资源,提高性能。
5. 长时间任务
线程池可以用来处理长时间运行的任务,例如爬虫任务,它可以帮助我们更好地控制系统资源的分配,减少系统负载。
6. IO密集型任务
IO密集型任务可以使用线程池来加速处理,比如从数据库或文件系统中读取数据,使用线程池可以加快处理速度,减少等待时间。
7. 网络通信
网络通信中的任务也可以使用线程池来处理,线程池可以帮助我们更好地分配系统资源,提高网络通信的性能。
总之,线程池可以帮助我们更好地管理和分配系统资源,提高程序的性能,有效地处理大量请求和长时间任务,因此它在多种场景下都很有用。
JAVA海量数据处理方法大全
JAVA海量数据处理方法大全在Java中处理海量数据是一项挑战,因为Java的内存限制可能会限制我们一次性加载和处理大量数据。
但是,有许多方法可以帮助我们有效地处理海量数据。
下面是一些使用Java处理海量数据的常用方法。
1. 数据分块处理:将大数据分成较小的块,然后逐个块进行处理。
这样可以减少内存的压力,同时提高处理效率。
Java中可以使用文件分割和分页查询等方法来实现。
2.多线程处理:使用多线程可以将处理任务并行化,提高处理效率。
可以通过使用线程池来管理线程,以避免创建太多线程导致的性能问题。
3. 数据压缩:对于大规模的数据,可以使用压缩算法来减少数据的占用空间。
Java提供了一些压缩库,如GZIP和Snappy,可以用来压缩和解压缩数据。
4. 分布式处理:如果处理海量数据超出了单个计算机的能力范围,可以考虑使用分布式计算框架,如Hadoop和Spark。
这些框架可以将数据和计算任务分布到多台计算机上处理。
5.数据库存储和查询:如果数据量太大无法完全加载到内存中,可以将数据存储在数据库中,并使用数据库的查询功能进行处理。
数据库可以在磁盘上存储大量数据,并提供高效的索引和查询功能。
6.内存映射文件:内存映射文件是一种将文件映射到内存的方法,使得文件可以像访问内存一样进行读写操作。
使用内存映射文件可以避免将整个文件加载到内存中,而是将文件的一部分映射到内存中进行处理。
7.外部排序:外部排序是一种将大规模数据分成小块进行排序,然后合并排序的结果的方法。
可以使用归并排序和堆排序等算法来实现外部排序。
8.基于索引的查询:对于大规模数据,使用索引可以提高查询效率。
可以使用B树、哈希表等数据结构来建立和查询索引。
9. Stream API:Java 8引入了Stream API,可以在集合上进行批处理操作。
Stream API具有延迟计算和并行处理的特性,非常适合处理大规模数据。
10. 分布式缓存:使用分布式缓存系统,如Redis和Memcached,可以将一部分数据缓存在内存中,加快访问速度。
Java线程池的配置
Java线程池的配置1、ThreadPoolExecutor的重要参数1、corePoolSize:核⼼线程数* 核⼼线程会⼀直存活,及时没有任务需要执⾏* 当线程数⼩于核⼼线程数时,即使有线程空闲,线程池也会优先创建新线程处理* 设置allowCoreThreadTimeout=true(默认false)时,核⼼线程会超时关闭2、queueCapacity:任务队列容量(阻塞队列)* 当核⼼线程数达到最⼤时,新任务会放在队列中排队等待执⾏3、maxPoolSize:最⼤线程数* 当线程数>=corePoolSize,且任务队列已满时。
线程池会创建新线程来处理任务* 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务⽽抛出异常4、 keepAliveTime:线程空闲时间* 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize* 如果allowCoreThreadTimeout=true,则会直到线程数量=05、allowCoreThreadTimeout:允许核⼼线程超时6、rejectedExecutionHandler:任务拒绝处理器* 两种情况会拒绝处理任务:- 当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务- 当线程池被调⽤shutdown()后,会等待线程池⾥的任务执⾏完毕,再shutdown。
如果在调⽤shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务* 线程池会调⽤rejectedExecutionHandler来处理这个任务。
如果没有设置默认是AbortPolicy,会抛出异常* ThreadPoolExecutor类有⼏个内部实现类来处理这类情况:- AbortPolicy 丢弃任务,抛运⾏时异常- CallerRunsPolicy 执⾏任务- DiscardPolicy 忽视,什么都不会发⽣- DiscardOldestPolicy 从队列中踢出最先进⼊队列(最后⼀个执⾏)的任务* 实现RejectedExecutionHandler接⼝,可⾃定义处理器2、线程池队列的选择wordQueue任务队列,⽤于转移和阻塞提交了的任务,即任务队列是运⾏线程的,任务队列根据corePoolSize和maximumPoolSize⼯作:1.当正在运⾏的线程⼩于corePoolSize,线程池会创建新的线程2.当⼤于corePoolSize⽽任务队列未满时,就会将整个任务塞⼊队列3.当⼤于corePoolSize⽽且任务队列满时,并且⼩于maximumPoolSize时,就会创建新额线程执⾏任务4、当⼤于maximumPoolSize时,会根据handler策略处理线程任务队列有以下三种模式:1. 直接提交。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
大数据处理之Java线程池使用前言:最近在做分布式海量数据处理项目,使用到了java的线程池,所以搜集了一些资料对它的使用做了一下总结和探究,前面介绍的东西大多都是从网上搜集整理而来。
文中最核心的东西在于后面两节无界队列线程池和有界队列线程池的实例使用以及线上问题处理方案。
1. 为什么要用线程池?在Java中,如果每当一个请求到达就创建一个新线程,开销是相当大的。
在实际使用中,每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源,甚至可能要比花在实际处理实际的用户请求的时间和资源要多的多。
除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。
如果在一个JVM中创建太多的线程,可能会导致系统由于过度消耗内存或者“切换过度”而导致系统资源不足。
为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务,这就是“池化资源”技术产生的原因。
线程池主要用来解决线程生命周期开销问题和资源不足问题,通过对多个任务重用线程,线程创建的开销被分摊到多个任务上了,而且由于在请求到达时线程已经存在,所以消除了创建所带来的延迟。
这样,就可以立即请求服务,使应用程序响应更快。
另外,通过适当的调整线程池中的线程数据可以防止出现资源不足的情况。
网上找来的这段话,清晰的描述了为什么要使用线程池,使用线程池有哪些好处。
工程项目中使用线程池的场景比比皆是。
本文关注的重点是如何在实战中来使用好线程池这一技术,来满足海量数据大并发用户请求的场景。
2. ThreadPoolExecutor类Java中的线程池技术主要用的是ThreadPoolExecutor 这个类。
先来看这个类的构造函数,ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)corePoolSize 线程池维护线程的最少数量maximumPoolSize 线程池维护线程的最大数量keepAliveTime 线程池维护线程所允许的空闲时间workQueue 任务队列,用来存放我们所定义的任务处理线程threadFactory 线程创建工厂handler 线程池对拒绝任务的处理策略ThreadPoolExecutor 将根据 corePoolSize和 maximumPoolSize 设置的边界自动调整池大小。
当新任务在方法execute(Runnable) 中提交时,如果运行的线程少于 corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。
如果运行的线程多于 corePoolSize 而少于 maximumPoolSize,则仅当队列满时才创建新线程。
如果设置的corePoolSize 和 maximumPoolSize 相同,则创建了固定大小的线程池。
ThreadPoolExecutor是Executors类的实现,Executors类里面提供了一些静态工厂,生成一些常用的线程池,主要有以下几个:newSingleThreadExecutor:创建一个单线程的线程池。
这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。
如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。
此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
newFixedThreadPool:创建固定大小的线程池。
每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。
线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
newCachedThreadPool:创建一个可缓存的线程池。
如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。
此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
在实际的项目中,我们会使用得到比较多的是newFixedThreadPool,创建固定大小的线程池,但是这个方法在真实的线上环境中还是会有很多问题,这个将会在下面一节中详细讲到。
当任务源源不断的过来,而我们的系统又处理不过来的时候,我们要采取的策略是拒绝服务。
RejectedExecutionHandler接口提供了拒绝任务处理的自定义方法的机会。
在ThreadPoolExecutor中已经包含四种处理策略。
1)CallerRunsPolicy:线程调用运行该任务的 execute 本身。
此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) {r.run();}}这个策略显然不想放弃执行任务。
但是由于池中已经没有任何资源了,那么就直接使用调用该execute的线程本身来执行。
2)AbortPolicy:处理程序遭到拒绝将抛出运行时RejectedExecutionExceptionpublic void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException();}这种策略直接抛出异常,丢弃任务。
3)DiscardPolicy:不能执行的任务将被删除public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {} 这种策略和AbortPolicy几乎一样,也是丢弃任务,只不过他不抛出异常。
4)DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) {e.getQueue().poll();e.execute(r);}该策略就稍微复杂一些,在pool没有关闭的前提下首先丢掉缓存在队列中的最早的任务,然后重新尝试运行该任务。
这个策略需要适当小心。
3. ThreadPoolExecutor无界队列使用public class ThreadPool {private final static String poolName = "mypool";static private ThreadPool threadFixedPool = new ThreadPool(2);private ExecutorService executor;static public ThreadPool getFixedInstance() {return threadFixedPool;}private ThreadPool(int num) {executor = Executors.newFixedThreadPool(num, new DaemonThreadFactory(poolName));}public void execute(Runnable r) {executor.execute(r);}public static void main(String[] params) {class MyRunnable implements Runnable {public void run() {System.out.println("OK!");try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}for (int i = 0; i <10; i ) {ThreadPool.getFixedInstance().execute(new MyRunnable());}try {Thread.sleep(2000);System.out.println("Process end.");} catch (InterruptedException e) {e.printStackTrace();}}}在这段代码中,我们发现我们用到了Executors.newFixedThreadPool()函数,这个函数的实现是这样子的:return new ThreadPoolExecutor(nThreads, nThreads, 0L,LISECONDS,new LinkedBlockingQueue());它实际上是创建了一个无界队列的固定大小的线程池。
执行这段代码,我们发现所有的任务都正常处理了。
但是在真实的线上环境中会存在这样的一个问题,前端的用户请求源源不断的过来,后端的处理线程如果处理时间变长,无法快速的将用户请求处理完返回结果给前端,那么任务队列中将堵塞大量的请求。
这些请求在前端都是有超时时间设置的,假设请求是通过套接字过来,当我们的后端处理进程处理完一个请求后,从队列中拿下一个任务,发现这个任务的套接字已经无效了,这是因为在用户端已经超时,将套接字建立的连接关闭了。
这样一来我们这边的处理程序再去读取套接字时,就会发生I/0 Exception. 恶性循环,导致我们所有的处理服务线程读的都是超时的套接字,所有的请求过来都抛I/O异常,这样等于我们整个系统都挂掉了,已经无法对外提供正常的服务了。
对于海量数据的处理,现在业界都是采用集群系统来进行处理,当请求的数量不断加大的时候,我们可以通过增加处理节点,反正现在硬件设备相对便宜。
但是要保证系统的可靠性和稳定性,在程序方面我们还是可以进一步的优化的,我们下一节要讲述的就是针对线上出现的这类问题的一种处理策略。
4. ThreadPoolExecutor有界队列使用public class ThreadPool {private final static String poolName = "mypool";static private ThreadPool threadFixedPool = null;public ArrayBlockingQueue queue = new ArrayBlockingQueue(2);private ExecutorService executor;static public ThreadPool getFixedInstance() {return threadFixedPool;}private ThreadPool(int num) {executor = new ThreadPoolExecutor(2, 4,60,TimeUnit.SECONDS, queue,new DaemonThreadFactory(poolName), new ThreadPoolExecutor.AbortPolicy());}public void execute(Runnable r) {executor.execute(r);}public static void main(String[] params) {class MyRunnable implements Runnable {public void run() {System.out.println("OK!");try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}int count = 0;for (int i = 0; i <10; i ) {try {ThreadPool.getFixedInstance().execute(new MyRunnable());} catch (RejectedExecutionException e) {e.printStackTrace();count ;}}try {("queue size:"ThreadPool.getFixedInstance().queue.size());Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Reject task: " count);}}首先我们来看下这段代码几个重要的参数,corePoolSize 为2,maximumPoolSize为4,任务队列大小为2,每个任务平均处理时间为10ms,一共有10个并发任务。