动手实现简单的线程池

本文详细介绍了线程池的设计与实现过程,包括线程池的属性变量、构造方法及核心的操作方法如execute、getTask等。此外还阐述了工作线程类的运作机制。

    废话不多说,直接开始。

一、属性变量

    线程的属性变量如下

//线程池是否关闭
private boolean isClosed = false;
//任务队列
private LinkedList<Runnable> workQueue;
//线程池id
private static int threadPoolId;
//线程id
private int threadId;

二、构造方法

public ThreadPool(int size) {
    super("ThreadPool-"+(threadPoolId++));
    workQueue = new LinkedList<>();
    for (int i=0;i<size;i++){
        new WorkerThread().start();
    }
}

    构造时传入线程数,初始化任务队列和线程

三、操作方法

    1、execute方法

public synchronized void execute(Runnable task) throws Exception {
    if (isClosed)
        throw new Exception("ThreadPool is down");
    if (task == null)
        throw new NullPointerException();
    workQueue.add(task);
    //唤醒线程去执行任务
    notify();
}

    execute方法首先判断判断线程池是否在运行,接着判断任务是否为空,如果两个条件都不满足则将任务加入到任务队列,然后唤醒线程去执行任务

    2、getTask方法

public synchronized Runnable getTask() throws InterruptedException {
    while (workQueue.size() == 0){
        if (isClosed)
            return null;
        wait();
    }
    return workQueue.removeFirst();
}

    如果任务队列为空则阻塞当前线程,如果线程池已关闭则返回null,不然将队头返回。

    3、join方法

public void join() {
    synchronized (this) {
        isClosed = true;
        notifyAll();  //唤醒还在getTask()方法中等待任务的工作线程
    }

    Thread[] threads = new Thread[activeCount()];
    //enumerate()方法继承自ThreadGroup类,获得线程组中当前所有活着的工作线程
    int count = enumerate(threads);
    for (int i=0; i<count; i++) { //等待所有工作线程运行结束
        try {
            threads[i].join();  //等待工作线程运行结束
        }catch(InterruptedException ex) { }
    }
}

    join方法是等待线程将工作任务中的任务执行完

    4、shutdown方法

public synchronized void shutdown(){
    if (!isClosed){
        isClosed = true;
        workQueue.clear();
        interrupt();
    }
}

    清空任务队列,中断所有线程

三、工作线程类

private class WorkerThread extends Thread{
    public WorkerThread() {
        //加入到当前ThreadPool线程组中
        super(ThreadPool.this,"WorkThread-" + (threadId++));
    }

    @Override
    public void run() {
        while (!interrupted()){
            Runnable task = null;
            try {
                task = getTask();
            }catch (Exception e){
            }
            if (task ==null)
                return;
            task.run();
        }
    }
}

    可以看到,当工作线程没有没中断,就去任务队列取一个任务执行。

源代码如下:继承线程组的好处是线程池便于管理线程和使用线程组的方法

public class ThreadPool extends ThreadGroup{
    //线程池是否关闭
    private boolean isClosed = false;
    //任务队列
    private LinkedList<Runnable> workQueue;
    //线程池id
    private static int threadPoolId;
    //线程id
    private int threadId;

    public ThreadPool(int size) {
        super("ThreadPool-"+(threadPoolId++));
        workQueue = new LinkedList<>();
        for (int i=0;i<size;i++){
            new WorkerThread().start();
        }
    }

    public synchronized void execute(Runnable task) throws Exception {
        if (isClosed)
            throw new Exception("ThreadPool is down");
        if (task == null)
            throw new NullPointerException();
        workQueue.add(task);
        //唤醒线程去执行任务
        notify();
    }

    public synchronized Runnable getTask() throws InterruptedException {
        while (workQueue.size() == 0){
            if (isClosed)
                return null;
            wait();
        }
        return workQueue.removeFirst();
    }

    public void join() {
        synchronized (this) {
            isClosed = true;
            notifyAll();  //唤醒还在getTask()方法中等待任务的工作线程
        }

        Thread[] threads = new Thread[activeCount()];
        //enumerate()方法继承自ThreadGroup类,获得线程组中当前所有活着的工作线程
        int count = enumerate(threads);
        for (int i=0; i<count; i++) { //等待所有工作线程运行结束
            try {
                threads[i].join();  //等待工作线程运行结束
            }catch(InterruptedException ex) { }
        }
    }

    public synchronized void shutdown(){
        if (!isClosed){
            isClosed = true;
            workQueue.clear();
            interrupt();
        }
    }

    private class WorkerThread extends Thread{
        public WorkerThread() {
            //加入到当前ThreadPool线程组中
            super(ThreadPool.this,"WorkThread-" + (++threadId));
        }

        @Override
        public void run() {
            while (!interrupted()){
                Runnable task = null;
                try {
                    task = getTask();
                }catch (Exception e){
                }
                if (task ==null)
                    return;
                task.run();
            }
        }
    }
}


「LLM那些事」系列第 4 篇《上下文窗口的边界》,文章连接:https://blog.csdn.net/houwenjin/article/details/163999753。 演示什么:在「预测」Sheet 的黄色格子里输入一句话(默认「来泡一杯」),四个「模型」——分别只统计最后 1 / 2 / 3 / 4 个字的 n-gram 查表——同时预测下一个字。同一个输入,看的上下文越长,候选越少、预测越确定: ┌────────────────┬──────────┬───────────────┬──────┐ │ 只看最后几个字 │ 用的前缀 │ 候选下一字数 │ 预测 │ ├────────────────┼──────────┼───────────────┼──────┤ │ 1 个 │ 杯 │ 3(茶/子/水) │ 模糊 │ ├────────────────┼──────────┼───────────────┼──────┤ │ 2 个 │ 一杯 │ 2(茶/水) │ 收窄 │ ├────────────────┼──────────┼───────────────┼──────┤ │ 3 个 │ 泡一杯 │ 1(茶) │ 确定 │ ├────────────────┼──────────┼───────────────┼──────┤ │ 4 个 │ 来泡一杯 │ 1(茶) │ 确定 │ └────────────────┴──────────┴───────────────┴──────┘
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值