java线程池executor怎么编写?该怎么使用?

TheDisguiser 2020-07-31 16:53:16 java常见问答 6055

对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工具网了解具体。

推荐阅读:

java线程池拒绝策略有哪些?

java线程池参数设置原则,如何设置线程池参数比较合理?

java线程池的作用是什么?线程池介绍