对java中线程池executor小伙伴们了解多少呢?这个有趣的家伙在提升线程管理性上非常有效,下面来看看它是如何编写及使用的吧。
创建线程池首先我们需要有一定数量的线程,所以当然要先来一个线程数组,当然集合也可以的。
来了解一下线程数组,这是专门用来存放线程实例的,想使用这些线程就需要有任务提交过来。当一些任务量过大时,我们是不可能在同一时刻给所有的任务分配一个线程的,所以我们还需要一个用于存放任务的容器。
这里的预先初始化线程实例的数量也是需要我们根据业务来确定。
同时,线程实例的数量也是不能随意定义的,所以我们还需要设置一个最大线程数。
//线程池中允许的最大线程数 private static int MAXTHREDNUM = Integer.MAX_VALUE; //当用户没有指定时默认的线程数 private int threadNum = 6; //线程队列,存放线程任务 private List < Runnable > queue; private WorkerThread[] workerThreads;
线程池工作
线程池的线程一般需要预先进行实例化,我们通过构造函数来试着了解下
public MyThreadPool(int threadNum) { this.threadNum = threadNum; if (threadNum > MAXTHREDNUM) threadNum = MAXTHREDNUM; this.queue = new LinkedList < > (); this.workerThreads = new WorkerThread[threadNum]; init(); } //初始化线程池中的线程 private void init() { for (int i = 0; i < threadNum; i++) { workerThreads[i] = new WorkerThread(); workerThreads[i].start(); } }
线程池准备好后,我们就要往线程池中提交工作任务,任务统一提交到队列中,当它有任务时,自动分发线程。
//提交任务 public void execute(Runnable task) { synchronized(queue) { queue.add(task); //提交任务后唤醒等待在队列的线程 queue.notifyAll(); } }
工作线程想要获取任务,就必须一直监听任务队列,当队列中有任务时就由一个线程去执行,这里我们就用到了前面提到的安全中断。
private class WorkerThread extends Thread { private volatile boolean on = true; @Override public void run() { Runnable task = null; //判断是否可以取任务 try { while (on && !isInterrupted()) { synchronized(queue) { while (on && !isInterrupted() && queue.isEmpty()) { //这里如果使用阻塞队列来获取在执行时就不会报错 //报错是因为退出时销毁了所有的线程资源,不影响使用 queue.wait(1000); } if (on && !isInterrupted() && !queue.isEmpty()) { task = queue.remove(0); } if (task != null) { //取到任务后执行 task.run(); } } } } catch (InterruptedException e) { e.printStackTrace(); } task = null; //任务结束后手动置空,加速回收 } public void cancel() { on = false; interrupt(); } }
记得退出时需要对线程池中线程进行销毁处理。
//销毁线程池 public void shutdown() { for (int i = 0; i < threadNum; i++) { workerThreads[i].cancel(); workerThreads[i] = null; } queue.clear(); }
到这里我们的简易线程池就完成了,线程池运行的基本原理基本全实现了,实际还是非常简单的。我们可以写个程序测试下:
public class ThreadPoolTest { public static void main(String[] args) throws InterruptedException { // 创建3个线程的线程池 MyThreadPool t = new MyThreadPool(3); CountDownLatch countDownLatch = new CountDownLatch(5); t.execute(new MyTask(countDownLatch, "testA")); t.execute(new MyTask(countDownLatch, "testB")); t.execute(new MyTask(countDownLatch, "testC")); t.execute(new MyTask(countDownLatch, "testD")); t.execute(new MyTask(countDownLatch, "testE")); countDownLatch.await(); Thread.sleep(500); t.shutdown(); // 所有线程都执行完成才destory System.out.println("finished..."); } // 任务类 static class MyTask implements Runnable { private CountDownLatch countDownLatch; private String name; private Random r = new Random(); public MyTask(CountDownLatch countDownLatch, String name) { this.countDownLatch = countDownLatch; this.name = name; } public String getName() { return name; } @Override public void run() { // 执行任务 try { countDownLatch.countDown(); Thread.sleep(r.nextInt(1000)); System.out.println("任务 " + name + " 完成"); } catch (InterruptedException e) { System.out.println(Thread.currentThread() .getId() + " sleep InterruptedException:" + Thread.currentThread() .isInterrupted()); } } } } result: 任务 testA 完成 任务 testB 完成 任务 testC 完成 任务 testD 完成 任务 testE 完成 finished... java.lang.InterruptedException at java.lang.Object.wait(Native Method) at com.learn.threadpool.MyThreadPool$WorkerThread.run(MyThreadPool.java: 75) ...
从结果看到,我们提交的所有任务都被执行了,且执行完成后强制销毁了所有线程,所以会抛出异常。
以上就是本篇文章的所有内容,如若对一些java常见问题及解决方法还有没清楚的疑问的话,欢迎来到奇Q工具网了解具体。
推荐阅读: