首页 > 其他分享 >自己简单实现一个线程池

自己简单实现一个线程池

时间:2024-03-12 09:56:27浏览次数:27  
标签:jobs 实现 void int num 线程 简单 workerNums

线程池
Java中的线程池是运用最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。

线程池的好处/作用:
1.不需要频繁的创建和销毁线程,提高相应速度
2.管理线程,避免无休止的创建线程导致资源枯竭。(不会每来一个任务就创建一个线程,线程不断的取任务执行)

线程池的方法有哪些
五个方法:
void execute(Job job): 执行一个Job,这个Job需要实现Runnable接口
void shutdown():关闭线程池
void addWorkers(int num):添加工作线程
void removeWorkers(int num):减少工作者线程
int getJobsSize():获得正在等待执行的任务数量

线程池的提交方式有哪几种
两种。第一种是execute不带返回值的提交,第二种是submit带返回值的提交。

线程池的属性有哪些
最大线程数 核心线程数 任务队列 超时时间 拒绝策略
核心线程数: 提交一个任务到线程池的时候,线程池会创建工作线程执行任务,即使其他核心线程空闲也会创建,直到达到核心线程数。
任务队列: 用来保存正在等待执行的任务的阻塞队列。
最大线程数: 线程池允许创建最大的线程数量。如果任务队列满了,并且已创建的线程数小于最大线程数,就会创建新的线程加速消耗。
超时时间: 如果在指定时间内没有任务那么线程就会撤销,不再占用CPU资源。
拒绝策略: 线程和队列都满了的情况下采取一种策略来应对到来的任务。

拒绝策略有哪些(可以说出来两三个)
直接抛异常/只用调用者所在线程来运行任务/丢弃队列中最近的一个任务,并执行当前任务/不处理把任务丢掉。

线程池的工作流程
1.判断核心线程是否已经满了。如果满了,下一步,没满就创建线程执行任务。
2.任务队列是不是满了。如果没满,把任务加入到队列中,如果满了,下一步。
3.线程池是不是满了。如果满了,采取拒绝策略,如果没满就创建新的线程加速消耗。
总结: 有任务过来先给到核心线程,核心线程处理不过来,就交给队列,队列满了,就开启最大线程数来加速消耗。

线程池初始阶段注意事项
即使一个任务提交时线程是空闲状态,只要是没达到核心线程数,就会创建新线程。

代码模拟
线程池接口

public interface ThreadPool<Job extends Runnable>{
    void execute(Job job);
    void shutdown();
    void addWorkers(int num);
    void removeWorkers(int num);
    int getJobSize();
}

工作线程实现
因为jobs是多线程共享的资源,所以在使用的时候需要先上锁。
有任务就取出来任务执行,没任务就会阻塞在jobs上面。

    class Worker implements Runnable {
        //是否在工作
        private volatile boolean running = true;
        @Override
        public void run() {
            while (running) {//取任务
                Job job = null;
                synchronized (jobs) {
                    while (jobs.isEmpty()) {
                        try {
                            System.out.println("没任务了" + Thread.currentThread().getName() + "进入阻塞");
                            jobs.wait();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            return;
                        }
                    }
                    job = jobs.removeFirst();//取任务
                }
                if (job != null) {
                    job.run();//执行任务
                }
            }
        }
        public void shutdown() {
            this.running = false;
        }
    }

线程池实现

public class DefaultThreadPool<Job extends Runnable> implements ThreadPool<Job> {
    //最大的工作线程数量
    private static final int MAX_WORKER_NUMBERS = 10;
    //默认的线程数量
    private static final int DEFAULT_WORKER_NUMBERS = 5;
    //最小的工作线程数量
    private static final int MIN_WORKER_NUMBERS = 1;
    //任务集合
    private final LinkedList<Job> jobs = new LinkedList<>();//任务从队尾插入 从队头取出来
    //工作线程
    private final List<Worker> workers = Collections.synchronizedList(new ArrayList<Worker>());
    //线程的编号
    private AtomicLong threadNum = new AtomicLong(0);
    //工作线程数
    private int workerNums = DEFAULT_WORKER_NUMBERS;

    public DefaultThreadPool() {//初始化线程池
        initializeWorkers(DEFAULT_WORKER_NUMBERS);
    }

    public DefaultThreadPool(int workerNums) {//指定线程
        this.workerNums = workerNums > MAX_WORKER_NUMBERS ? MAX_WORKER_NUMBERS : workerNums < MIN_WORKER_NUMBERS ? MIN_WORKER_NUMBERS : workerNums;
        initializeWorkers(this.workerNums);
    }

    private void initializeWorkers(int workerNums) {
        for (int i = 0; i < workerNums; i++) {
            Worker worker = new Worker();
            workers.add(worker);//加入到集合当中
            Thread thread = new Thread(worker, "worker thread" + threadNum.incrementAndGet());
            System.out.println("输出了" + threadNum);
            thread.start();//进入就绪态 开始工作
        }
    }

    @Override
    public void execute(Job job) {
        if (job != null) {
            synchronized (jobs) {
                jobs.addLast(job);
                jobs.notify();//唤醒因jobs阻塞的工作线程
            }
        }
    }

    @Override
    public void shutdown() {//将线程池关闭
        for (Worker worker: workers){
            worker.shutdown();//停止工作
        }
    }

    @Override
    public void addWorkers(int num) {//添加num个工作线程
        synchronized (jobs) {
            if (num + workerNums > MAX_WORKER_NUMBERS) num = MAX_WORKER_NUMBERS - workerNums;
            initializeWorkers(num);//添加num个线程
            workerNums += num;
        }
    }

    @Override
    public void removeWorkers(int num) {
        synchronized (jobs) {
            if (num >= workerNums) {
                throw new IllegalArgumentException("删除失败");
            }
            for (int i = 0; i < num; i++) {
                Worker worker = workers.get(i);
                if (workers.remove(worker)) {
                    worker.shutdown();//线程不再工作
                }
            }
            workerNums -= num;//减去数量
        }
    }

    @Override
    public int getJobSize() {
        return this.jobs.size();
    }
}

创建测试类

public class Demo2 {
    public static void main(String[] args) throws InterruptedException {
        ThreadPool threadPool = new DefaultThreadPool();
        for(int i=0;i<10;i++){
            Task task = new Task();
            threadPool.execute(task);
        }
        Thread.sleep(1000);
        threadPool.shutdown();
    }
    static class Task implements Runnable{
        @Override
        public void run() {
            for(int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName() + " task is going...");
            }
        }
    }
}

在这个过程中,可能会有一个误区。run和start是不同的,start会开启一个新线程,但是run就等同于调用了普通方法,并不会开启新线程。
所以在Thread.currentThread().getName()打印的实际上就是工作线程的名称。

所有内容参考自《java并发编程的艺术》

标签:jobs,实现,void,int,num,线程,简单,workerNums
From: https://www.cnblogs.com/chengyiyuki/p/18067659

相关文章

  • 前端如何实现token的无感刷新
    通常,对于一些需要记录用户行为的系统,在进行网络请求的时候都会要求传递一下登录的token。不过,为了接口数据的安全,服务器的token一般不会设置太长,根据需要一般是1-7天的样子,token过期后就需要重新登录。不过,频繁的登录会造成体验不好的问题,因此,需要体验好的话,就需要定时去刷新toke......
  • 前端大屏尺寸实现自适应屏幕大小
    说在前面目前很多业主在使用系统的时候都会有大屏的需求,很多屏幕并不会像我们开发的屏幕一样标准,比如1920*1080,这样我们就需要根据业主的屏幕尺寸进行适配,避免一些图表或文字在大屏中出现偏移,影响视觉观感。方案比选方案一:如果希望在大屏中实现最佳的展示效果,应该由设计人......
  • .NET Framework 4.8 Web API+Entity Framework(实现增删改查)
    1、首先,创建一个新的.NETFramework4.8WebAPI项目。2、定义Student类:publicclassStudent{publicintId{get;set;}publicstringName{get;set;}publicintAge{get;set;}}3、创建DbContext类:usingSystem.Data.Entity;publicclassS......
  • C#3种常见的定时器(多线程)
    总结以下三种方法,实现c#每隔一段时间执行代码:方法一:调用线程执行方法,在方法中实现死循环,每个循环Sleep设定时间;调用线程执行方法,在方法中实现死循环,每个循环Sleep设定时间Threadthread=newThread(newThreadStart(obj.Method1));thread.Start();方法二:使用System.Timers......
  • 安全高效、易管控的汽车制造业文件协同共享,到底该如何实现?
    汽车制造业是一个不断发展和创新的行业,包括汽车零部件制造、汽车整车制造、销售与服务等多个产业链,汽车制造业的上下游产业链包括多个环节:上游产业链主要包括汽车零部件供应商、原材料供应商以及相关的设备制造业。中游产业链主要是汽车整车制造。这一环节包括汽车设计、生产制造......
  • 基于RocketMQ实现分布式事务
    背景在一个微服务架构的项目中,一个业务操作可能涉及到多个服务,这些服务往往是独立部署,构成一个个独立的系统。这种分布式的系统架构往往面临着分布式事务的问题。为了保证系统数据的一致性,我们需要确保这些服务中的操作要么全部成功,要么全部失败。通过使用RocketMQ实现分布式事......
  • c++从零实现reactor高并发服务器!!!
    环境准备linux虚拟机安装升级c/c++编译器gcc/g++选项源代码文件1源代码文件2...源代码文件n-o指定输出的文件名(不能和源文件同名默认是a.out)-g调试-On链接时优化减小体积(n=1-3)-c只编译用于生成库-std=c++11支持c++11标准安装man功能man级别接口......
  • 多线程
    多线程的实现java.lang.Thread类代表多线程注意事项启动线程必须是start方法,不是调用run方法不要把主线任务放在启动子线程之前继承Thread/***@authorPickle*@versionV1.0*@date2024/3/1116:43*/publicclassMyThreadextendsThread{@Override......
  • 用c++实现净现值的计算
    我是用c++实现的,我是把贴现率保留了四位小数。下面是我写的代码:#include<iostream>#include<cmath>usingnamespacestd;floatjst(intj,floatm,floatlv){while(j!=0){m*=(1+lv);j--;}return1.0/m;}i......
  • 实验1 C语言输入输出和简单程序编写
    task1_1.c`#include<stdio.h>include<stdlib.h>intmain(){printf("O\n");printf("\n");printf("II\n");printf("O\n");printf("\n");printf("II\n");system("pause&......