Java线程池

文中技术点,是在JDK1.8环境下运行测试的,其它环境未必能完美运行

Java线程池

在这里插入图片描述

核心原理

在Java线程池中最核心的类就是ThreadPoolExecutor,而在ThreadPoolExecutor类中最核心的构造方法就是带有7个参数的构造方法,如下所示(此处借鉴互联网图片):

在这里插入图片描述

其中:

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

并且Java的线程池是通过 生产者-消费者模式 实现的,线程池的使用方是生产者,而线程池本身就是消费者。其核心工作流程如下图所示:

在这里插入图片描述

直接手撸

手动实现的线程池要比Java自身的线程池简单的多,可以去掉了各种复杂的处理方式,保留了最核心的原理:线程池的使用者向任务队列中添加任务,而线程池本身从任务队列中消费任务并执行任务。

在这里插入图片描述

理解了这个核心原理,接下来的代码就简单多了。在实现这个简单的线程池时,我们可以将整个实现过程进行拆解。拆解后的实现流程为:定义核心、创建内部类RunThread、创建ThreadPool类的构造方法和创建执行任务的方法。

以下代码中,有些变量都是我自定义命名的,请按照个人习惯即可

定义核心ThreadPool类

创建一个名称为ThreadPool的Java类,并在这个类中定义几个常用的核心变量:

  • QUEUE_SIZE:静态常量,表示默认的阻塞队列大小。
  • runQueue:模拟实际的线程池使用阻塞队列来实现生产者-消费者模式。
  • runThreads:模拟实际的线程池使用List集合保存线程池内部的工作线程。

代码片段如下:

//默认阻塞队列大小
private static final int QUEUE_SIZE = 5;

//模拟实际的线程池使用阻塞队列来实现生产者-消费者模式
private BlockingQueue<Runnable> runQueue;

//模拟实际的线程池使用List集合保存线程池内部的工作线程
private List<RunThread> runThreads = new ArrayList<RunThread>();
创建内部类RunThread

在上面那个ThreadPool中创建一个内部类RunThread,模拟线程池中的工作线程。主要的作用就是消费runQueue中的任务,并执行任务。由于工作线程需要不断从runQueue中获取任务,所以,这里使用了while(true)循环不断尝试消费队列中的任务。

代码片段如下:

//内部类RunThread,模拟线程池中的工作线程
//主要的作用就是消费runkQueue中的任务,并执行
//由于工作线程需要不断从runkQueue中获取任务,使用了while(true)循环不断尝试消费队列中的任务
class RunThread extends Thread{
    @Override
    public void run() {
        //不断循环获取队列中的任务
        while (true){
            //当没有任务时,会阻塞
            try {
                Runnable runTask = runQueue.take();
                runTask.run();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
创建ThreadPool类的构造方法

ThreadPool类创建两个构造方法,一个构造方法中传入线程池的容量大小和阻塞队列,另一个构造方法中只传入线程池的容量大小。

代码片段如下:

//在ThreadPool的构造方法中传入线程池的大小和阻塞队列
public ThreadPool(int poolSize, BlockingQueue<Runnable> runQueue){
    this.runQueue = runQueue;
    //创建poolSize个工作线程并将其加入到runThreads集合中
    IntStream.range(0, poolSize).forEach((i) -> {
        RunThread runThread = new RunThread();
        runThread.start();
        runThreads.add(runThread);
    });
}

//在ThreadPool的构造方法中传入线程池的大小
public ThreadPool(int poolSize){
    this(poolSize, new LinkedBlockingQueue<>(QUEUE_SIZE));
}
创建执行任务的方法

在ThreadPool类中创建执行任务的方法execute(),execute()方法的实现比较简单,就是将方法接收到的Runnable任务加入到runQueue队列中。

代码片段如下:

//通过线程池执行任务
public void execute(Runnable task){
    try {
        runQueue.put(task);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
最后放出完整源码ThreadPool类
package com.wlee.test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.IntStream;

public class ThreadPool {

	//默认阻塞队列大小
	private static final int QUEUE_SIZE = 5;

	//模拟实际的线程池使用阻塞队列来实现生产者-消费者模式
	private BlockingQueue<Runnable> runQueue;

	//模拟实际的线程池使用List集合保存线程池内部的工作线程
	private List<RunThread> runThreads = new ArrayList<RunThread>();
	
	//在ThreadPool的构造方法中传入线程池的大小和阻塞队列
	public ThreadPool(int poolSize, BlockingQueue<Runnable> runQueue){
	    this.runQueue = runQueue;
	    //创建poolSize个工作线程并将其加入到runThreads集合中
	    IntStream.range(0, poolSize).forEach((i) -> {
	        RunThread runThread = new RunThread();
	        runThread.start();
	        runThreads.add(runThread);
	    });
	}

	//在ThreadPool的构造方法中传入线程池的大小
	public ThreadPool(int poolSize){
	    this(poolSize, new LinkedBlockingQueue<>(QUEUE_SIZE));
	}
	
	//内部类RunThread,模拟线程池中的工作线程
	//主要的作用就是消费runkQueue中的任务,并执行
	//由于工作线程需要不断从runkQueue中获取任务,使用了while(true)循环不断尝试消费队列中的任务
	class RunThread extends Thread{
	    @Override
	    public void run() {
	        //不断循环获取队列中的任务
	        while (true){
	            //当没有任务时,会阻塞
	            try {
	                Runnable runTask = runQueue.take();
	                runTask.run();
	            } catch (InterruptedException e) {
	                e.printStackTrace();
	            }
	        }
	    }
	}
	
	//通过线程池执行任务
	public void execute(Runnable task){
	    try {
	        runQueue.put(task);
	    } catch (InterruptedException e) {
	        e.printStackTrace();
	    }
	}
}

编写测试程序

这里就直接用过main方法中调用ThreadPool类的构造方法,传入线程池的大小,创建一个ThreadPool类的实例,然后循环10次调用ThreadPool类的execute()方法,向线程池中提交的任务为:打印当前线程的名称--->> 你好 ThreadPool

package com.wlee.test;

import java.util.stream.IntStream;

public class RunPool {

	public static void main(String[] args) {
		
		ThreadPool threadPool = new ThreadPool(10);
		
        IntStream.range(0, 10).forEach((i) -> {
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() + "--->> 你好 ThreadPool");
            });
        });
	}
}

执行main方法之后,将输出(其中,当前线程的名称是随机的,也就是说你测试输出的不一定和我的一样):

Thread-1--->> 你好 ThreadPool
Thread-1--->> 你好 ThreadPool
Thread-1--->> 你好 ThreadPool
Thread-1--->> 你好 ThreadPool
Thread-1--->> 你好 ThreadPool
Thread-0--->> 你好 ThreadPool
Thread-2--->> 你好 ThreadPool
Thread-2--->> 你好 ThreadPool
Thread-2--->> 你好 ThreadPool
Thread-4--->> 你好 ThreadPool

至此,我们自定义的Java线程池就开发完成了。

总结

线程池的核心原理其实并不复杂,示例只是示例,还需要结合项目要求,优化使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WorkLee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值