JAVA线程池粗解

本咸鱼不会看原码,只能和大家一起看看线程池的大致使用方法,相信有很多和我一样的咸鱼急于上手,不求甚解,看我的blog就对了

一、线程池的作用

最近接到个任务,开发一个小工具,用来获取现网设备的性能,现网3000多台设备,我一开始也没多想,直接一个主线程开跑,等到测试的时候发现有的设备连接超时,有的设备响应超时,从数据库中获取一台设备的ip也不只一个,需要对每个ip进行尝试。这就非常尴尬了,不知道要执行多久。后来SE说给我30个核用多线程跑,我就想到了线程池。
线程的创建和销毁的开销是巨大的,而通过线程池的重用大大减少了这些不必要的开销,当然既然少了这么多消费内存的开销,其线程执行速度也是突飞猛进的提升。

二、线程池

线程池,就是ThreadPoolExecutor,它有几个构造方法(其实都是调用的同一个构造方法),现在我们就看看最常用的构造方法

ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue)
  1. corePoolSize ,核心线程数;
  2. maximumPoolSize,最大线程数;
  3. keepAliveTime,线程执行结束后生存时间(线程可重用,可不用销毁线程,线程池自动分配任务);
  4. unit,keepAliveTime的单位,有7种取值:
TimeUnit.DAYS;              //天
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒
  1. workQueue,工作队列,有以下几种类型:
ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;

线程池初始化后调用ThreadPoolExecutor.execute(Runable)即可(Runable为实现了Runable接口的实例)

三、快速入手

Task.java:

package com.zr;

public class Task implements Runnable{
	int workNum;
	public Task(int workNum) {                      //传入要处理的数据
		// TODO Auto-generated constructor stub
		this.workNum=workNum;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("正在执行任务"+workNum);
	}
}

ThreadPoolTest.java:

package com.zr;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolTest {
	public static void main(String[] args) {
		ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 5, 2, TimeUnit.MINUTES, new LinkedBlockingQueue<>());
		for (int i = 0; i < 10; i++) {
			executor.execute(new Task(i));
		}
	}
}

执行结果:
在这里插入图片描述
可以看到所有任务都执行完了,但是程序并没有结束,这时我们需要调用ThreadPoolExecutor.shutdown()方法来结束线程池即在for循环后加上executor.shutdown(),实际开发的时候我们肯能需要让主线程挂起,当线程池任务执行完后再接着执行后面的代码,这时就需要在主进程中添加判断当ThreadPoolExecutor.isTerminated()为true时再执行后面的代码。修改过的代码如下:

	public static void main(String[] args) {
		ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 5, 2, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
		for (int i = 0; i < 10; i++) {
			executor.execute(new Task(i));
		}
		executor.shutdown();
		while(true) {
			if(executor.isTerminated()) {
				System.out.println("main执行完成");
				break;
			}
		}
		System.out.println("main真的执行完成");
	}

执行结果:
在这里插入图片描述
也可以将判断逻辑改成这样:

		while(!executor.isTerminated()) {
		}
		System.out.println("main执行完成");
		System.out.println("main真的执行完成");

什么是workQueue

按字面意思就是工作队列,当我们调用ThreadPoolExecutor.execute(Runable)时就是向线程池添加了一个Runable任务,LinkedBlockingQueue中可以设置workQueue的容量,默认为Integer.MAX_VALUE,在上面的例子中for循环一次添加了10个任务,我们看看给LinkedBlockingQueue设置容量执行会出现什么结果
当workQueue容量设置为10时:
在这里插入图片描述
可以发现并没有什么变化
但我们把workQueue容量设置为4时就会出现异常:
在这里插入图片描述
可以发现一共执行了9个任务,最后一个并没有执行。
原理: 在workQueue没有满时,线程池只会创建corePoolSize个线程来跑任务,当workQueue满了,每加一个任务就会新创建一个线程来跑任务,但是不会超过maximumPoolSize,当线程数达到maximumPoolSize,还在往workQueue中添加任务时就会添加失败,抛出java.util.concurrent.RejectedExecutionException异常。上面的例子中我在Task执行的时候添加了Thread.sleep(500),可以保证在添加最后一个任务时workQueue已满(因为主线程for循环添加10个任务就是一瞬间的事,而不加sleep线程执行也是一瞬间的事,这样可能不会出现workQueue满了还在添加任务的情况)。workQueue的大小为4,核心线程数(corePoolSize)为3,前三个任务一进workQueue就被拿出来执行了,所以当添加第三个任务时workQueue还是空的,然后继续添加四个任务将workQueue填满,当添加第八个和第九个任务时创建额外的线程执行,这时线程数量达到最大值(maximumPoolSize),workQueue也满了,还有最后一个任务就会添加失败。
现在我们在添加最后一个任务之前让队列或线程空出来,这样就不会出现异常了

	public static void main(String[] args) {
		ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 5, 2, TimeUnit.SECONDS, new LinkedBlockingQueue<>(4));
		for (int i = 0; i < 10; i++) {
			if (i == 9) {
				try {
					Thread.sleep(600);					//Task里sleep(500),600ms后肯定有执行完的任务
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			executor.execute(new Task(i));
		}
		executor.shutdown();
		while (!executor.isTerminated()) {
		}
		System.out.println("main执行完成");
		System.out.println("main真的执行完成");
	}

接下来同学们自己执行下就行了,结果就是全部执行成功。在我们工作时可以按照实际设备性能分配workQueue和线程数

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值