【视频笔记】80行代码手写线程池

定义一个threadpool类

public class MyThreadPool {
    void execute(Runnable runnable) {}
}

定义一个Main类

public class Main {
    public static void main(String[] args) {
        MyThreadPool myThreadPool = new MyThreadPool();
        myThreadPool.execute(() -> {
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"执行任务");
        });
        System.out.println("主线程执行结束,未阻塞");
    }
}

执行Main类main方法,此时runnable中内容并未执行。

思考2个问题:

1、线程什么时候创建

2、线程的runnable是什么,是我们提交的command吗

实现execute方法,创建线程传入runnable

public class MyThreadPool {
    void execute(Runnable runnable) {
        new Thread(runnable).start();
    }
}

console打印:
主线程执行结束,未阻塞
Thread-0执行任务

 使用创建线程(new Thread(runnable).start();)的方式有2个问题:

1、创建线程是非常消耗资源的;

2、我们没有办法管理我们创建的线程

我们把我们创建的线程提出来,放到集合中保存起来,进行管理。

思考一个问题:集合里这些线程可以复用吗?可以用集合中已有线程,来执行我们的command吗?

答案是不可以。 ### thread类中没有塞入runnable的方法,只能通过构造函数传入

一个线程的生命周期,从创建开始,到完成它要执行的command之后,它就会被销毁,所以不存在让它再执行另一个command的说话。

 

用创建的线程,一直不停运行,然后处理多次传入的command,是可以的。

public class MyThreadPool {
    List<Runnable> tasks = new ArrayList<Runnable>();
    Thread thread  ;
    public MyThreadPool() {
        thread  = new Thread(() -> {
            while (true) {
                if(!tasks.isEmpty()) {
                    Runnable task = tasks.remove(0);
                    task.run();
                }
            }
        });
        thread.start();
    }

    void execute(Runnable runnable) {
        tasks.add(runnable);
    }
}
public class Main {
    public static void main(String[] args) {
        MyThreadPool myThreadPool = new MyThreadPool();
        myThreadPool.execute(() -> {
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"执行任务");
        });
        myThreadPool.execute(() -> {
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"执行任务2");
        });
        System.out.println("主线程执行结束,未阻塞");
    }
}

--- console ---
主线程执行结束,未阻塞
Thread-0执行任务
Thread-0执行任务2

程序不会终止

上述代码问题,如果tasks为空,线程空转,耗费cpu一个核。

有没有一种容器,让tasks为空的时候,阻塞在这里,让它不需要消耗cpu资源;有元素的时候,拿到元素去执行。

阻塞队列,就是完美实现这种需求的容器。

mport java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class MyThreadPool {
    BlockingQueue<Runnable> tasks = new ArrayBlockingQueue<>(1024);
    Thread thread  ;
    public MyThreadPool() {
        thread  = new Thread(() -> {
            while (true) {
                
                    Runnable task = null;
                    try {
                        // 线程在等待或者阻塞的时候,如果线程被中断了,我们就需要手动的去处理这个异常
                        // 这里是在等待阻塞队列填充元素的过程中,如果thread被中断了,就不会继续在这里等待,而去处理这个异常
                        // Thread.sleep也有这个异常;CountDownLatch的await方法也有这个InterruptException;在JUC包里需要等待的函数都有这个异常
                        // 有一个例外,LockSupport他有一个park方法,这个park方法只会记录中断位,而不会抛出interrupted exception这个异常
                        task = tasks.take();
                        task.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                
            }
        }, "唯一线程");
        thread.start();
    }

    void execute(Runnable runnable) {
        //offer和add他们两个的功能是一样的,都是给阻塞队列添加一个元素,语义不同。
        // add在blocking queue满了的时候,是会抛出异常;offer是有一个返回值,通过这个返回值去判断是否成功的给这个阻塞队列添加了元素,
        tasks.offer(runnable);
    }
}
public class Main {
    public static void main(String[] args) {
        MyThreadPool myThreadPool = new MyThreadPool();
        for (int i = 0; i < 5; i++) {
            myThreadPool.execute(() -> {
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName() + "执行任务");
            });
        }
        System.out.println("主线程执行结束,未阻塞");
    }
}

 console打印了5次“唯一线程执行任务”,程序没有终止,是因为线程没有终止,它阻塞在tasks.take(),他在等待阻塞队列来新的任务

改造,我们的线程池应该有多少个线程? 

定义一个变量corePoolSize表示核心线程数量,当创建的线程数量未达到核心线程的数量时,每次来任务创建一个;

当达到了,往阻塞队列放;当阻塞队列满了,再创建一些辅助线程,未达到最大线程数量maxSize时,每次来任务创建一个。

public class MyThreadPool {
    BlockingQueue<Runnable> tasks = new ArrayBlockingQueue<>(1024);
    Runnable task = () -> {
        while (true) {
            
                Runnable task = null;
                try {
                    task = tasks.take();
                    task.run();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            
        }
    };

    private int coolPoolSize = 10;
    private int maxPoolSize = 16;
    List<Thread> coreList = new ArrayList<>();
    // maxPoolSize - coolPoolSize
    List<Thread> supportList = new ArrayList<>();

    public MyThreadPool() {

    }

    void execute(Runnable runnable) {
        if (coreList.size() < coolPoolSize) {
            Thread thread = new Thread(task);
            coreList.add(thread);
            thread.start();
        }

        // 如果放入阻塞队列返回false,说明我们的队列已经满了
        // 当阻塞队列满了的时候,我们需要用一些辅助线程,来帮我们的核心线程处理这些任务
        boolean offer = tasks.offer(runnable);
        if (offer) {
            return;
        }
        if (coreList.size() + supportList.size() < maxPoolSize) {
            Thread thread = new Thread(task);
            supportList.add(thread);
            thread.start();
        }

    }
}

此时 execute方法不是线程安全的,解决这个问题,可以用原子操作,或者是cas的原子变量,或者是加锁来解决。

思考:一个线程,如何在空闲的时候,让自己结束呢?

线程从阻塞队列中拿任务时,设置一个超时时间,超过这个时间仍没有拿到任务,说明线程池当前是一个不忙碌的状态,支持线程应该要自己结束掉。

使用poll方法取代take方法,E poll(long timeout, TimeUnit unit) throws InterruptedException;把抛异常这个事抽象为一个行为,拒绝策略

package com.rocky.threadpool;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class MyThreadPool {

    private final int coolPoolSize;
    private final int maxPoolSize;
    private final int timeout;
    private final TimeUnit timeUnit;
    private final BlockingQueue<Runnable> tasks;
    private final RejectHandle rejectHandle;

    public MyThreadPool(int coolPoolSize, int maxPoolSize, int timeout, TimeUnit timeUnit, BlockingQueue<Runnable> tasks, RejectHandle rejectHandle) {
        this.coolPoolSize = coolPoolSize;
        this.maxPoolSize = maxPoolSize;
        this.timeout = timeout;
        this.timeUnit = timeUnit;
        this.tasks = tasks;
        this.rejectHandle = rejectHandle;
    }

    public BlockingQueue<Runnable> getTasks() {
        return tasks;
    }

    List<Thread> coreList = new ArrayList<>();
    // maxPoolSize - coolPoolSize
    List<Thread> supportList = new ArrayList<>();

    void execute(Runnable runnable) {
        if (coreList.size() < coolPoolSize) {
            Thread thread = new CoreThread();
            coreList.add(thread);
            thread.start();
        }

        // 如果放入阻塞队列返回false,说明我们的队列已经满了
        // 当阻塞队列满了的时候,我们需要用一些辅助线程,来帮我们的核心线程处理这些任务
        if (tasks.offer(runnable)) {
            return;
        }

        if (coreList.size() + supportList.size() < maxPoolSize) {
            Thread thread = new SupportThread();
            supportList.add(thread);
            thread.start();
        }

        if (!tasks.offer(runnable)) {
            rejectHandle.reject(runnable, this);
        }
    }

    class CoreThread extends Thread {
        public void run() {
            while (true) {
                Runnable command = null;
                try {
                    command = tasks.take();
                    command.run();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    class SupportThread extends Thread {
        public void run() {
            while (true) {
                Runnable command = null;
                try {
                    command = tasks.poll(timeout, timeUnit);
                    if (command == null) {
                        break;
                    }
                    command.run();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName() + "线程结束了");
        }
    }
}
package com.rocky.threadpool;

public interface RejectHandle {
    void reject(Runnable rejectCommand, MyThreadPool myThreadPool);
}
package com.rocky.threadpool;

public class ThrowRejectHandle implements RejectHandle {
    @Override
    public void reject(Runnable runnableCommand, MyThreadPool myThreadPool) {
        throw new RuntimeException("阻塞队列满了");
    }
}
package com.rocky.threadpool;

public class DiscardRejectHandle implements RejectHandle{
    @Override
    public void reject(Runnable rejectCommand, MyThreadPool myThreadPool) {
        myThreadPool.getTasks().poll();
        myThreadPool.getTasks().offer(rejectCommand);
    }
}
package com.rocky.threadpool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args) {
        MyThreadPool myThreadPool = new MyThreadPool(2, 4, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2), new DiscardRejectHandle());
        for (int i = 0; i < 8; i++) {
            final int taskIndex = i;
            myThreadPool.execute(() -> {
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName() + "执行任务" + taskIndex);
            });
        }
        System.out.println("主线程执行结束,未阻塞");
    }
}

 

参考:【不背八股】80行代码手写线程池_哔哩哔哩_bilibili

posted @ 2025-06-04 22:00  fanblog  阅读(14)  评论(0)    收藏  举报