【视频笔记】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("主线程执行结束,未阻塞"); } }

浙公网安备 33010602011771号