简单线程池教程

一、线程池概念

在多线程的程序里面,经常要创建线程,但是创建线程的过程是十分耗费资源的,而且线程是一种可以重复利用的资源,如果一个线程可以重复反复利用,处理完一个任务后用来处理另外一个任务,那么可以节省很多的资源。因此产生了线程池,一个用来统一管理线程一遍提高程序的效率的功能。

二、线程池的原理

线程池里面含有两个存储元素的数据结构,一个是存储任务的阻塞队列(也可以用简单的队列来代替),另外一个是用来存储线程的数组,当队列里面有任务时,任务队列里面的任务会被放到线程里面执行,线程解决完一个任务在取出另外的任务,并处理,当任务队列里面没有任务时所有线程进入等待状态。达到了线程重复利用的效果。
在这里插入图片描述

三、封装任务线程

在存储任务线程的数组里面,我们存储的不是Thread,而是把任务封装成WorkThread,并继承Runnable,在通过这样的封装后我们可以对任务线程进行更加精细的操作,并提供更加多的拓展。该类的重要的方法是在任务的队列里面获取任务,并使任务执行起来,同时还要判断任务线程是否要进入等待状态。

public void run() {
        while (true){
            //Lock写法
            Runnable work = null;
            try {
                lock.lock();
                //判断当前
                while (workList.isEmpty()){
                    System.out.println("任务队列为空"+Thread.currentThread().getName()+"进入等待");
                    notEmpty.await();
                }
                work = workList.remove(0);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
            work.run();
        }
    }

代码判断任务队列是否为空,如果是,使线程进入等待状态。特别注意的是代码里面的work里面的run方法不可以放在锁的代码块里面,否则会有严重的线程闲置问题,只有在取出任务队列的元素才要保证线程安全。work的run方法英因为WorkThread已经放入了Thread里面,所以没有必要在新建Thread。直接run就好了。

四、简单线程池的编写

线程池里面的重要方法是添加任务,并在初始化时创建对应的线程数。

public class MyThreadPool {
    //管理任务线程
    public ArrayList<WorkThread> threadList = new ArrayList<>();
    //管理任务
    public ArrayList<Runnable> workList = new ArrayList<>();
    public ReentrantLock lock = new ReentrantLock();
    public Condition notEmpty = lock.newCondition();
    //添加任务线程
    public MyThreadPool(int core){
        for(int i = 0; i < core; i++){
            WorkThread workThread = new WorkThread(workList, lock, notEmpty);
            new Thread(workThread).start();
            threadList.add(workThread);
        }
    }

    /**
     * 提交任务方法
     * @param runnable 提交的任务
     */
    public void addWork(Runnable runnable){
        try {
            lock.lock();
            workList.add(runnable);
            notEmpty.signal();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
}

要注意的是WorkThread和线程池里面的Lock和Condition都要保持一致。

五、idea内置线程池的最大线程数

idea里面的内置线程池是一定的当任务队列被装满时,而且此时的核心线程都被占用,此时的会唤醒额外的线程数 。如果此时还向线程池里面添加任务,那么线程池会拒绝并抛弃任务。线程池里面还有存活时间,当任务队列为空,超过存活时间,那么线程重新进入等待状态。

六、实现最大线程数

我们把上面的存储任务的数组换成自己写的阻塞队列,使用时把线程池里面的Lock换成阻塞队列里面的Lock就好了,在改改参数名和方法名。阻塞队列源码如下:

public class MyQueue2 <T>{
    //队列大小
    int length;
    //当前队列的数据
    int size = 0;
    int head = 0;
    int tail = 0;
    T[] queue;
    public ReentrantLock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();
    public MyQueue2(int length){
        this.length = length;
        queue = (T[]) new Object[length];
    }
    //存入数据
    public void put(T t) throws Exception{
        lock.lock();
        try {
            //线程睡眠 阻塞
            while (length == size){
                //System.out.println("队列已满,进入阻塞状态");
                notFull.await();
            }
            queue[head] = t;
            head = (head + 1) % length;
            size++;
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }
    //取出数据
    public T take() throws Exception{
        lock.lock();
        try {
            while (size == 0){
                //System.out.println("队列为空,进入阻塞状态");
                //使队列进入等待状态
                notEmpty.await();
            }
            T data = queue[tail];
            queue[tail] = null;
            tail = (tail + 1) % length;
            size--;
            notFull.signal();
            return data;
        } finally {
            lock.unlock();
        }
    }
    //判断队列是否为空
    public boolean isEmpty(){
        if(size == 0){
            return true;
        }
        return false;
    }
    //是否满了
    public boolean isFull(){
        if(size == length){
            return true;
        }
        return false;
    }
}

想要实现上面的最大线程数,我们可以额外创建一个ArrayList存储他们,里面的线程在判断阻塞队列已满,所以核心线程处于工作状态时进入等待状态。其他方法和原本的WorkThread完全一样。我们可以直接继承WorkThread,重写里面的run()方法,添加一个核心线程队列。就可以实现了。

public class ExtraWorkThread extends WorkThread{
    public ArrayList<WorkThread> threads;
    public ExtraWorkThread(ArrayList<WorkThread> threads, MyQueue2<Runnable> workList, ReentrantLock lock, Condition notEmpty) {
        super(workList, lock, notEmpty);
        this.threads = threads;
    }

    @Override
    public void run() {
        while (true){
            //Lock写法
            Runnable work = null;
            try {
                lock.lock();
                while (!(isWork() && workList.isFull())){
                    //System.out.println("任务不繁忙," + Thread.currentThread().getName()+ "进入等待状态");
                    notEmpty.await();
                }
                //work = workList.remove(0);
                work = workList.take();
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
            work.run();
        }
    }

    public boolean isWork(){
        for(int i = 0; i < threads.size(); i++){
            WorkThread thread = threads.get(i);
            if(thread.sleep){
                return false;
            }
        }
        return true;
    }

}

在修改一下WorkThread,设置一个sleep标签,方便判断是否处于工作状态。在线程进入等待状态时设置成false,取出元素使设置为true。修改后的代码如下

public class WorkThread implements Runnable{
    MyQueue2<Runnable> workList;
    //ArrayList<Runnable> workList;
    public ReentrantLock lock;
    public Condition notEmpty;
    public boolean sleep;

    public WorkThread(MyQueue2<Runnable> workList, ReentrantLock lock, Condition notEmpty){
        this.workList = workList;
        this.lock = lock;
        this.notEmpty = notEmpty;
    }

    @Override
    public void run() {
        while (true){
            //Lock写法
            Runnable work = null;
            try {
                lock.lock();
                //判断当前
                while (workList.isEmpty()){
                    System.out.println("任务队列为空"+Thread.currentThread().getName()+"进入等待");
                    sleep = true;
                    notEmpty.await();
                }
                sleep = false;
                //work = workList.remove(0);
                work = workList.take();
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
            work.run();
        }

    }
}

这样就可以了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值