线程池的参数说明

关于线程池的说明

理论基础:

由于CPU的速度远远快于内存的速度,为了充分的利用CPU的资源,操作系统会将CPU的计算进行分时复用

目前普遍采用的是时间片轮询的方式来调度cpu资源

在我们的java语言中,创建线程就像是new一个对象那么简单,但是实际上由于java的多线程是创建的内核级线程,和系统操作的线程一一对应,资源的分配调度都是由操作系统来进行管理,开销很大 (对应的是golang的协程,程序会有自己的资源调度器,对比的话JAVA中第三方会有纤程 quasar);

如何创建一个线程池

ThreadPoolExecutor的创建方式,这么多构造参数是不是有点慌,往下接着看!

public class MyThreadFactory implements ThreadFactory {
	@Override
	public Thread newThread(Runnable r) {
		Thread thread=new Thread(r,"custom"+r.hashCode());  //自定义线程的名字前缀,增加可追溯性
		return thread;
	}
}
public class RejectHandler implements RejectedExecutionHandler {
	@Override
	public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
		System.out.println(executor.getPoolSize());
		System.out.println("拒绝执行任务了");
	}
}
public class MyThreadPool {
	public static void main(String[] args) {
		ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(5,
				10,
				60,
				TimeUnit.SECONDS,
				new LinkedBlockingQueue<>(10),
				new MyThreadFactory(),
				new RejectHandler()
				);
		for (int i = 0; i < 17; i++) {
			threadPoolExecutor.execute(()->{
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName());
			});
		}
		System.out.println(threadPoolExecutor.getPoolSize());
		//threadPoolExecutor.shutdown();
		threadPoolExecutor.shutdownNow();   两种关闭线程池塘的方式对比
	}
}

线程池的各个参数说明

  ThreadPoolExecutor pool = new ThreadPoolExecutor(5,   //核心线程数
				10,   //最大线程数
				60,   //最大闲置时间()
				TimeUnit.SECONDS, //时间单位
				new LinkedBlockingQueue<>(10), //任务队列
				r -> new Thread(r, "ray" + r.hashCode()),   //lamda方式自定义线程工厂
				(r, q) -> {
					System.out.println("拒绝策略");   //lamda方式自定义拒绝策略,那么执行这个策略的线程是哪个线程呢?(主线程!!!)
				});
上面的参数我们会挨个来解释:

线程池的本质就是生产者消费者模型,举一个生活中的例子来进行描述,假设我们整个过程模拟成工厂里的一道工序,整个过程类比一下:

核心线程数: 该部门的正式员工

最大线程数: 该团队的最大人数限制,就相当于工位吧, 大于核心线程数的那部分理解为允许的临时工数量

最大闲置时间: 临时工的闲置时间(木有工作任务的时间,超出后会被释放)

时间单位: 对应上一个参数的单位

线程工厂: 部门boss用来招员工的入口(自定义就可以给每一个线程加上名字,便于追溯)

拒绝策略: 部门老大说 俺们这儿扛不住了,不要再来任务了!!!(主线程来执行这部分的代码)

任务队列就相当于我们的待加工产品

调用线程池的这个线程就是对应的主线程

既然是生产者消费者模型,那么我们就可以通过任务队列的从零到满再到零的过程来描述线程池的各个对应状态:

  1. 假如线程池的工作不饱和,那么此时的线程数量就是线程池塘的核心线程数,队列未满
  2. 假如工作饱和,当前任务队列满了,那么就会扩招零时员工,根据当前的任务来增加线程数
  3. 当前的线程数量已经达到最大线程数目了,但是增加任务的时候,就会触发线程池的拒绝策略
  4. 当前的紧急任务完成之后,临时员工的闲置时间达到闲置时间之后就会被线程池释放掉
注意事项:
  • 线程池的队列最好配置为有界队列,任务对象本身也是会占据虚拟机的内存

  • 线程工厂最好添加固定前缀名称,便于问题定位

  • 线程的数量并不是越多越好,并不是哈(取决于CPU设备和IO设备的利用率对比,IO设备的利用只需要线程去调度,但是不消耗CPU资源,计算机是通过协处理器来处理IO任务)

    参考公式: CPU核数目*[1+IO耗时/CPU耗时] 仅供参考,实际应用根据实际情况调整,程序的性能优化本身就是多方面,不用强行局部最优解,边际收益太低

Executors.? 这种创建的方式不推荐

线程池的分配,最好不要将线程池共用,不要把鸡蛋放在了一个篮子里面,线程加上名字便于追溯

接口聚合数据

下面这段代码的问题在哪呢
public class UsePool {
	public static void main(String[] args) {
		//创建线程池
		ThreadPoolExecutor pool = new ThreadPoolExecutor(5,
				10,
				60,
				TimeUnit.SECONDS,
				new LinkedBlockingQueue<>(10),
				r -> new Thread(r, "ray" + r.hashCode()),   //lamda方式自定义线程工厂
				(r, q) -> {
					System.out.println("拒绝策略");   //lamda方式自定义拒绝策略,那么执行这个策略的线程是哪个线程呢?(主线程!!!)
				});

		Demo demo=new Demo();
		pool.submit(()-> {
			demo.setName("ray");
		});

		pool.submit(()-> {
			demo.setNickName("tyrion");
		});
		System.out.println(demo.toString());
		//我们需要保证线程的执行顺序以确保执行结果符合预期,控制线程的协作方式有哪些呢?(juc工具包下的各个工具类或者  java1.8之后推出的CommpleteFuture(强烈推荐))
	}

	static class Demo {
		private String name;
		private String nickName;

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public String getNickName() {
			return nickName;
		}

		public void setNickName(String nickName) {
			this.nickName = nickName;
		}

		@Override
		public String toString() {
			return "Demo{" +
					"name='" + name + '\'' +
					", nickName='" + nickName + '\'' +
					'}';
		}
	}
}

主线程不知道线程的任务是否执行完,没有做好线程之间的协作,那么怎么解决呢?继续往下吧

怎么去批量执行异步任务

说明:

ExecutorCompletionService 构造函数 一个线程池,加一个队列

每次执行的结果会返回一个future对象,将对象添加到阻塞队列中

分为take poll take会阻塞 poll可能会返回null值,支持超时时间设置,我们可以控制获取到一个结果后就直接返回,也可以获取到所有结果后返回

CompletionService
public class CompletionServiceDemo {
	public static void main(String[] args) throws ExecutionException, InterruptedException {
		Tyrion tyrion = new Tyrion();
		System.out.println(tyrion);
		ExecutorService threadPool = Executors.newFixedThreadPool(5);
		CompletionService<Integer> service = new ExecutorCompletionService<>(threadPool);

		Random random = new Random();
		service.submit(() ->
		{
			int j = random.nextInt(10);
			Thread.sleep(j * 100);
			return j;
		});
		service.submit(() ->
		{
			int j = random.nextInt(10);
			Thread.sleep(j * 100);
			return j;
		});
		service.submit(() ->
		{
			int j = random.nextInt(10);
			Thread.sleep(j * 100);
			return j;
		});
		service.submit(() ->
		{
			int j = random.nextInt(10);
			Thread.sleep(j * 100);
			return j;
		});
		service.submit(() ->
		{ int j = random.nextInt(10);
			Thread.sleep(j * 100);
			return j; });

		for (int i = 0; i < 5; i++) {
			//System.out.println(service.take().get());
			Future<Integer> future = service.poll(150, TimeUnit.MILLISECONDS);
			if (future != null) {
				System.out.println(future.get());
			}
		}
		threadPool.shutdown();
	}
	static class Tyrion {
		private String nickName;
		private Integer age;
		private String familyName;
		public String getNickName() {
			return nickName;
		}
		public void setNickName(String nickName) {
			this.nickName = nickName;
		}
		public Integer getAge() {
			return age;
		}
		public void setAge(Integer age) {
			this.age = age;
		}
		public String getFamilyName() {
			return familyName;
		}
		public void setFamilyName(String familyName) {
			this.familyName = familyName;
		}
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_42061605/article/details/106885130