Java线程池详解

前言:我们知道,在并发场景下,我们需要创建线程来让任务调度执行。但是在高并发场景下,频繁的创建和销毁线程显然用之前的方法是低效的,频繁的创建线程会带来资源的损耗,所以像数据库连接池一样,我们在线程中也引入了线程池的概念。线程池的主要目的主要就是达到线程的复用,减少资源的损耗,对线程进行一个统一的管理。

一、创建线程的两种方式

ExecutorService es= Executors.newCachedThreadPool();(关于这个类的详情说明在下面)
ExecutorService ese =new ThreadPoolExecutor(0, 0, 0, null, null);

这两种方式都可以创建一个线程池,但是现在推荐使用第二种。第一种中的newCachedThreadPool()也是继承ThreadPoolExecutor但对其队列,核心线程数,最大线程数等参数提前做了规定。但是在工作中,我们需要根据实际业务场景来对这些参数进行修改,如果使用第一种则不灵活,第二种更为方便。

二、ExecutorService,Executors,ThreadPoolExecutor的关系

三、三大常用线程池

1)newFixedThreadPool(5);//一池五个线程,底层用阻塞队列
2)newSingleThreadExecutor();//一池一个线程底层用阻塞队列
3)newCachedThreadPool();//一池N个线程,底层用同步队列
底层均继承ThreadPoolExecutor,例子如下:

 public static ExecutorService newFixedThreadPool(int var0, ThreadFactory var1) {
      return new ThreadPoolExecutor(var0, var0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), var1);
   }

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

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

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

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

newFixedThreadPool(5)

public static void main(String[] args) {
		ExecutorService threadpool = Executors.newFixedThreadPool(5);//一池五个线程
		//ExecutorService threadpool = Executors.newSingleThreadExecutor();//一池一个线程
		//ExecutorService threadpool = Executors.newCachedThreadPool();//一池N个线程
		//模拟10个用户来办理业务,每个用户是一个请求线程
		try {
			for(int i=1;i<=10;i++){
				threadpool.execute(()->{
					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
				});
			}
			TimeUnit.HOURS.sleep(100000);
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			threadpool.shutdown();
		}
	}

结果:
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-2 办理业务
pool-1-thread-3 办理业务
pool-1-thread-4 办理业务
pool-1-thread-5 办理业务
可以看到,只有5个线程进行

newSingleThreadExecutor()

public static void main(String[] args) {
		//ExecutorService threadpool = Executors.newFixedThreadPool(5);//一池五个线程
		ExecutorService threadpool = Executors.newSingleThreadExecutor();//一池一个线程
		//ExecutorService threadpool = Executors.newCachedThreadPool();//一池N个线程
		//模拟10个用户来办理业务,每个用户是一个请求线程
		try {
			for(int i=1;i<=10;i++){
				threadpool.execute(()->{
					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
				});
			}
			TimeUnit.HOURS.sleep(100000);
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			threadpool.shutdown();
		}
	}

结果:
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
可看到只有1个线程进行处理

newCachedThreadPool()

public static void main(String[] args) {
		//ExecutorService threadpool = Executors.newFixedThreadPool(5);//一池五个线程
		//ExecutorService threadpool = Executors.newSingleThreadExecutor();//一池一个线程
		ExecutorService threadpool = Executors.newCachedThreadPool();//一池N个线程
		//模拟10个用户来办理业务,每个用户是一个请求线程
		try {
			for(int i=1;i<=10;i++){
				threadpool.execute(()->{
					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
				});
			}
			TimeUnit.HOURS.sleep(100000);
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			threadpool.shutdown();
		}
	}

结果
pool-1-thread-1 办理业务
pool-1-thread-2 办理业务
pool-1-thread-3 办理业务
pool-1-thread-4 办理业务
pool-1-thread-5 办理业务
pool-1-thread-6 办理业务
pool-1-thread-7 办理业务
pool-1-thread-7 办理业务
pool-1-thread-8 办理业务
pool-1-thread-7 办理业务
可看到根据实际线程调用多个线程进行处理

四、线程池的几个重要参数:

public ThreadPoolExecutor(int var1, int var2, long var3, TimeUnit var5,
			BlockingQueue<Runnable> var6, ThreadFactory var7,
			RejectedExecutionHandler var8) {
		this.ctl = new AtomicInteger(ctlOf(-536870912, 0));
		this.mainLock = new ReentrantLock();
		this.workers = new HashSet();
		this.termination = this.mainLock.newCondition();
		if (var1 >= 0 && var2 > 0 && var2 >= var1 && var3 >= 0L) {
			if (var6 != null && var7 != null && var8 != null) {
				this.acc = System.getSecurityManager() == null
						? null
						: AccessController.getContext();
				this.corePoolSize = var1;
				this.maximumPoolSize = var2;
				this.workQueue = var6;
				this.keepAliveTime = var5.toNanos(var3);
				this.threadFactory = var7;
				this.handler = var8;
			} else {
				throw new NullPointerException();
			}
		} else {
			throw new IllegalArgumentException();
		}

1.corePoolSize:线程池中的常驻核心线程数
2.maximumPoolSize:线程池能够容纳同时执行的最大线程数
3.keepAliveTime:多余的空闲线程的存活时间(当前线程池数量超过corePoolSize时,当空闲
时间达到keepAliveTime值时,多余的空闲线程会被销毁直到只剩下corePoolSize个线程为止)
4.unit:keepAliveTime的单位
5.workQueue:任务队列,被提交但尚未被执行的任务
6.threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程一般用默认的即可
7.handler:拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数时如何来拒绝

而在线程池中,当一个任务来临时,需要经过以下判断:
(1)如果当前线程数没有超过核心线程数,则创建一个线程来执行
(2)如果当前线程数超过了核心线程数,但是队列还没有满,则将任务放入消息队列中
(3)如果当前线程数超过了核心线程数,并且队列已满,但是没有超过最大线程数,则将创建一个线程来调度执行任务。
(4)如果超过了最大线程数,并且队列已满,则执行拒绝策略

五、拒绝策略:

AbortPolicy:直接抛出RejectedExecutionException异常,阻止系统正常运行
CallerRunsPolicy:"调用者运行"一种调节机制,该策略既不会抛弃任务,也不会抛出异常而是将某些任务回退到调用者
DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交
DiscardPolicy:直接丢弃任务,不予任何处理也不抛异常。
例子:

public static void main(String[] args) {
		//System.out.println(Runtime.getRuntime().availableProcessors());//CPU核数
		ExecutorService threadpool = new ThreadPoolExecutor(2, 3,1L, TimeUnit.SECONDS, 
				new LinkedBlockingQueue<Runnable>(3),Executors.defaultThreadFactory(),
				new ThreadPoolExecutor.AbortPolicy());
		try {
			for(int i=1;i<=15;i++){
				threadpool.execute(()->{
					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
				});
			}
			//TimeUnit.MILLISECONDS.sleep(10);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}finally{
			threadpool.shutdown();
		}
	}

结果:
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-2 办理业务
pool-1-thread-3 办理业务
java.util.concurrent.RejectedExecutionException: Task MyThreadPoolDemo$$Lambda 1 / 135721597 @ 65 a b 7765 r e j e c t e d f r o m j a v a . u t i l . c o n c u r r e n t . T h r e a d P o o l E x e c u t o r @ 1 b 28 c d f a [ R u n n i n g , p o o l s i z e = 3 , a c t i v e t h r e a d s = 2 , q u e u e d t a s k s = 0 , c o m p l e t e d t a s k s = 4 ] a t j a v a . u t i l . c o n c u r r e n t . T h r e a d P o o l E x e c u t o r 1/135721597@65ab7765 rejected from java.util.concurrent.ThreadPoolExecutor@1b28cdfa[Running, pool size = 3, active threads = 2, queued tasks = 0, completed tasks = 4] at java.util.concurrent.ThreadPoolExecutor 1/135721597@65ab7765rejectedfromjava.util.concurrent.ThreadPoolExecutor@1b28cdfa[Running,poolsize=3,activethreads=2,queuedtasks=0,completedtasks=4]atjava.util.concurrent.ThreadPoolExecutorAbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at MyThreadPoolDemo.main(MyThreadPoolDemo.java:55)

可以看到抛出了异常,但在实际使用中,抛出异常阻止运行是不合理的,那么根据实际可调用其他拒绝策略,在这便不一一演示,可自行演示测试。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java线程池是一种用于管理和复用线程的机制,它可以提高多线程程序的性能和效率。在Java中,线程池ThreadPoolExecutor类实现,通过设置不同的参数可以对线程池的行为进行调整。 以下是Java线程池的一些常用参数及其解释: 1. corePoolSize(核心线程数):线程池中始终保持的活动线程数,即使它们处于空闲状态。当有新任务提交时,如果活动线程数小于corePoolSize,则会创建新线程来处理任务。 2. maximumPoolSize(最大线程数):线程池中允许存在的最大线程数。当活动线程数达到maximumPoolSize并且工作队列已满时,新任务将会被拒绝。 3. keepAliveTime(线程空闲时间):当线程池中的线程数量超过corePoolSize时,多余的空闲线程在等待新任务到来时的最长等待时间。超过这个时间,空闲线程将被终止。 4. unit(时间单位):keepAliveTime的时间单位,可以是秒、毫秒、微秒等。 5. workQueue(工作队列):用于存储等待执行的任务的阻塞队列。常见的工作队列有ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue等。 6. threadFactory(线程工厂):用于创建新线程的工厂类。可以自定义线程的名称、优先级等属性。 7. handler(拒绝策略):当线程池无法接受新任务时的处理策略。常见的拒绝策略有AbortPolicy(默认,抛出RejectedExecutionException异常)、CallerRunsPolicy(由调用线程执行任务)、DiscardPolicy(直接丢弃任务)和DiscardOldestPolicy(丢弃最旧的任务)。 这些参数可以根据实际需求进行调整,以达到最佳的线程池性能和资源利用率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值