java多线程之线程池

1).线程池简介    

    假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。
    如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。
                一个线程池包括以下四个基本组成部分:
                1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
                2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
                3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
                4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
                
    线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
    线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目,看一个例子:
    假设一个服务器一天要处理50000个请求,并且每个请求需要一个单独的线程完成。在线程池中,线程数一般是固定的,所以产生线程总数不会超过线程池中线程的数目,而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池大小是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间,从而提高效率。     

合理运用线程池,可以带来几点好处,降低资源消耗,提高响应速度,提高线程的可管理性,防止服务过载。

2).Executors线程池工具类

     我们可以通过 Executors.new......来创建线程池,比如 Executors.newCachedThreadPool()来创建一个缓存线程池,查看Executors源码可以发现有5种不同的线程池,我们依次来看一下

 I.线程池中包含指定个数的线程(定长线程池)

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
    }

II.创建一个单线程的线程池

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
    }

III.创建一个可缓存的线程池

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
    }

其中有三个线程池是通过ThreadPoolExecutor()创建的,所以我们有必要看下ThreadPoolExecutor这个类

ThreadPoolExecutor类

ThreadPoolExecutor有四个构造方法,前面三个构造都是调用最后一个构造实现的,所以我们看下最后一个构造方法

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime,TimeUnit unit,BockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);

依次介绍下各个参数的意思,

corePoolSize 是核心线程个数

maximumPoolSize 线程池最大线程个数,核心线程个数<=线程池最大个数

keepAliveTime 是线程空闲时存活时间,就是说线程没事做的时候,过多久会被销毁

unit 是线程存活时间的单位

workQueue 是阻塞队列

threadFactory 是线程工厂,也就是线程池生产线程的工厂类

handler 是当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略还处理新提交的任务,handler有四种可以选择的参数,当然也可以自己实现RejectedExecutionHandler来自定义饱和策略

AbortPolicy:直接抛出异常,默认情况下采用这种策略
CallerRunsPolicy:只用调用者所在线程来运行任务
DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务

DiscardPolicy:不处理,丢弃掉

下面看一个自定义线程池的例子

package com.zuokai.thread0427;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;
import java.util.concurrent.TimeUnit;

/**
 * 自定义线程池
 * @author lijh
 *
 */
public class MyThreadPool{
	
	public static ExecutorService newMyThreadPool(int corePoolSize,int maximumPoolSize,int keepAliveTime){
		//corePoolSize 核心线程数量  maximumPoolSize 线程最大数量  keepAliveTime 线程空闲时,保持存活时间  unit 保持存活时间的单位  workQueue 队列
		//用给定的初始参数和默认的线程工厂及被拒绝的执行处理程序创建新的 ThreadPoolExecutor。
		//ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
		
		//handler 饱和策略
		//用给定的初始参数和默认的线程工厂创建新的 ThreadPoolExecutor。
		//ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
		
		//threadFactory 线程工厂
		//用给定的初始参数和默认被拒绝的执行处理程序创建新的 ThreadPoolExecutor
		//ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
		
		//用给定的初始参数创建新的 ThreadPoolExecutor
		//ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) 
		
		
		//handler 当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略还处理新提交的任务
		//AbortPolicy:直接抛出异常,默认情况下采用这种策略
		//CallerRunsPolicy:只用调用者所在线程来运行任务
		//DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务
		//DiscardPolicy:不处理,丢弃掉
		
		
		//workQueue 用于保存等待执行的任务的阻塞队列
		//ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,按FIFO原则进行排序
		//LinkedBlockingQueue:一个基于链表结构的阻塞队列,吞吐量高于ArrayBlockingQueue。静态工厂方法Excutors.newFixedThreadPool()使用了这个队列
		//SynchronousQueue: 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,
		//吞吐量高于LinkedBlockingQueue,静态工厂方法Excutors.newCachedThreadPool()使用了这个队列
		//PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
		
		ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5),new MyRejectedExecutionHandler());
		return threadPoolExecutor;
		
	}
	
}

class MyRejectedExecutionHandler implements RejectedExecutionHandler{

	@Override
	public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
		DiscardPolicy dp = new DiscardPolicy();
		executor.setRejectedExecutionHandler(dp);
		System.out.println("线程池和任务队列已经饱和,此线程被抛弃");
	}
	
}

IV.创建一个线程池,它可以安排在给定的延迟之后运行的命令,或者定期执行

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

ScheduledThreadPoolExecutor调用了父类的方法

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

最后还是使用了ThreadPoolExecutor来创建线程池,队列是使用延时队列

V.创建一个带并行级别的线程池,并行级别决定了同一时刻最多有多少个线程在执行,如不传入并行级别参数,将默认为当前系统的CPU个数。

public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java多线程编程中,配置线程池的参数是非常重要的。根据不同的业务需求,可以设置线程池的参数来控制线程的数量和行为。根据引用中的例子,可以使用`Executors.newFixedThreadPool(int nThreads)`来创建一个固定线程数量的线程池。在这个方法中,`nThreads`参数表示线程池中的线程数量,只有这个数量的线程会被创建。然后,可以使用`pool.execute(Runnable command)`方法来提交任务给线程池执行。 在配置线程池时,需要考虑业务的性质。如果是CPU密集型的任务,比如加密、计算hash等,最佳线程数一般为CPU核心数的1-2倍。而如果是IO密集型的任务,比如读写数据库、文件、网络读写等,最佳线程数一般会大于CPU核心数很多倍。这样可以充分利用IO等待时间来执行其他任务,提高程序的性能。引用中给出了一些常见的线程池特点和构造方法参数。 总之,根据业务需求和特点,合理配置线程池的参数可以提高程序的性能和效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Java多线程线程池的参数和配置](https://blog.csdn.net/MRZHQ/article/details/129107342)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Java多线程线程池(合理分配资源)](https://blog.csdn.net/m0_52861000/article/details/126869155)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Java多线程线程池](https://blog.csdn.net/weixin_53611788/article/details/129602719)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值