ThreadPoolUtil线程池使用

一、为什么要使用多线程:

         现在的电脑都是多核,即可以同时处理多个任务,开启多进程,所以使用多线程,可以有效的利用cpu资源,提升工作效率。

二、线程的生命周期:

三、线程池与多线程区别:

多线程:通过new Thread 可以创建多个线程,但是每次new对象性能较差,线程缺乏统一的管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom;也无法执行特殊的功能,如定时,周期执行,线程的中断等操作。

线程池:可重用线程,减少对象创建和销毁等开销,性能较好,同时控制有效的最大线程并发线程数,避免线程阻塞等,实现复杂任务。

四、常用多线程使用:

java.util.concurrent的Executors中有常用的创建线程池的方法。常用四种方法总结如下:

newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

源码如图:

参数:

/**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters and default thread factory and rejected execution handler.
     * It may be more convenient to use one of the {@link Executors} factory
     * methods instead of this general purpose constructor.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

构造方法的参数说明:
corePoolSize : 线程池的核心线程数。该参数并不是初始化时候写死了,线程池对象构造完成以后也能通过它提供的一些方法动态修改,ThreadPoolExecutor.setCorePoolSize。

maximumPoolSize : 线程池的最大线程数,只有当任务队列满了才会创建新的线程。该参数并不是初始化时候写死了,线程池对象构造完成以后也能通过它提供的一些方法动态修改,ThreadPoolExecutor.setMaximumPoolSize。

keepAliveTime : 线程池所维护的线程的活动时间。如果超过了该时间范围,而线程还是空闲的,那么该线程将会被回收。不再由线程池所维护。以下是官方的一些说明:
如果当前线程池数大于核心线程池数,且这些线程是空闲的超过所设定的存活时间keepAliveTime,那么这些多出来的线程则会被终止,可以降低线程资源消耗。通过设置尽可能大的过期时间来阻止空闲线程被销毁,可手动调用setKeepAliveTime来设置。默认情况下,该策略只能适用于大于核心线程数的线程,但是可以通过设置ThreadPoolExecutor.allowCoreThreadTimeOut(boolean),则该策略也适用于核心线程数。

unit : 和上面keepAliveTime所对应,表示活动时间的时间单位。如 毫秒、秒、分..

workQueue : 初始化一组任务,但是该组任务并不会马上执行,需要手动调用prestartCoreThread或者prestartAllCoreThreads来预先启动线程才会执行这些初始化的任务。否则只有当你提交第一个任务时候他才会执行,且可能是单线程执行(取决于你提交几次任务)..

threadFactory : 传入一个线程工厂。通过该线程工厂可以创建线程。它创建的所有线程都是通过同样的ThreadGroup和同样的NORM_PRIORITY和non-daemon状态。通过提供的不同ThreadFactory,可以掌握修改线程名字,线程组,优先级,守护状态等。如果线程池调用线程工厂创建一个线程失败时,则返回一个null。且executor会继续,但是可能不会执行任何任务。
如果我们自己重写封装了一遍线程工厂,还有个好处就是可以通过该线程工厂实例维护所有由它创建的线程。
 

开发者使用钩子方法如:ThreadPoolExecutor.beforeExecute and ThreadPoolExecutor.afterExecute。这些方法可在任务执行前后被执行,或者线程终止时执行。场景:重新初始化ThreadLocal。收集统计信息,添加日志记录,源码如下:

protected void beforeExecute(Thread t, Runnable r) { }
/*  <pre> {@code
 * class PausableThreadPoolExecutor extends ThreadPoolExecutor {
 *   private boolean isPaused;
 *   private ReentrantLock pauseLock = new ReentrantLock();
 *   private Condition unpaused = pauseLock.newCondition();
 *
 *   public PausableThreadPoolExecutor(...) { super(...); }
 *
 *   protected void beforeExecute(Thread t, Runnable r) {
 *     super.beforeExecute(t, r);
 *     pauseLock.lock();
 *     try {
 *       while (isPaused) unpaused.await();
 *     } catch (InterruptedException ie) {
 *       t.interrupt();
 *     } finally {
 *       pauseLock.unlock();
 *     }
 *   }
 *
 *   public void pause() {
 *     pauseLock.lock();
 *     try {
 *       isPaused = true;
 *     } finally {
 *       pauseLock.unlock();
 *     }
 *   }
 *
 *   public void resume() {
 *     pauseLock.lock();
 *     try {
 *       isPaused = false;
 *       unpaused.signalAll();
 *     } finally {
 *       pauseLock.unlock();
 *     }
 *   }
 * }}</pre>
 */

 protected void afterExecute(Runnable r, Throwable t) { }
    /* <pre> {@code
     * class ExtendedExecutor extends ThreadPoolExecutor {
     *   // ...
     *   protected void afterExecute(Runnable r, Throwable t) {
     *     super.afterExecute(r, t);
     *     if (t == null && r instanceof Future<?>) {
     *       try {
     *         Object result = ((Future<?>) r).get();
     *       } catch (CancellationException ce) {
     *           t = ce;
     *       } catch (ExecutionException ee) {
     *           t = ee.getCause();
     *       } catch (InterruptedException ie) {
     *           Thread.currentThread().interrupt(); // ignore/reset
     *       }
     *     }
     *     if (t != null)
     *       System.out.println(t);
     *   }
     * }}</pre>
     */
 protected void terminated() { }

工具类如下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolUtil
{
  private static volatile ThreadPoolUtil instance;
  private static ExecutorService threadPool;
  
  public static ThreadPoolUtil getInstance()
  {
    if (instance == null) {
      synchronized (ThreadPoolUtil.class)
      {
        instance = new ThreadPoolUtil();
        threadPool = Executors.newCachedThreadPool();
      }
    }
    return instance;
  }
  
  public void excute(Runnable runnable)
  {
    threadPool.execute(runnable);
  }
  
  public void shutdown()
  {
    threadPool.shutdown();
  }
  
  public boolean isActive()
  {
    if (threadPool.isTerminated()) {
      return false;
    }
    return true;
  }

    /*//方法调用
     ThreadPoolUtil.getInstance().excute(new Runnable()
        {
          public void run()
          {
            //调用执行具体业务
          }
       });
   */
}

newFixedThreadPool  :创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool : 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor : 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

newSingleThreadScheduledExecutor: 创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。

创建方式: 
(1)Executors.newSingleThreadScheduledExecutor() ; 
(2)Executors.newSingleThreadScheduledExecutor(ThreadFactory threadFactory) ;//threadFactory创建线程的工厂

其余方法类似。

五、线程池执行过程

            我们通过submit或execute提交到线程池中,线程池内部会调用内部类Worker,其实现了Runnable,封装了线程,它们是通过线程工厂创建线程时候把this,即Worker传入到Thread的构造参数,从代码上是线程池维护了一组Worker,通过Worker.thread.start()后,那么Worker所封装的线程就会运行Worker自身的run实现。最后在run实现里持续的从workQueue中获取任务来执行,由于workQueue是阻塞队列,所以如果队列中没有任务,那么该线程则会被挂起等待唤醒(有任务过来),Worker类里封装了Thread和任务Runnable,还有completedTasks。可以注意到创建一个Thread时候把this传入,这样的话如果我调用Worker.thread.start()就相当于该线程会执行Worker里的run方法了。completedTasks是用来记录该线程完成了多少个任务(非整个线程池)。
           注意该Worker继承了AQS同步基础器,它主要实现了互斥锁的功能,但是这个互斥锁和ReentrantLock有点不同,该实现是不允许线程重入获取锁的。下面说说为什么要实现锁功能和非重入:
1.lock方法主要用在标明当前线程正在执行任务中,而private interruptIdleWorkers 方法需要使用tryLock来判断当前线程是否正在执行任务,如果非执行任务状态则表明可能是正在获取任务,那么该线程属于空闲状态,可以被中断。
2.看回答1可以知道这通过ReentrantLock也能实现,但是如果我们在提交一个任务给线程池(实现一个Runnable),如果该
任务里面调用了和interruptIdleWorkers相关的,需要中断当前可获取锁(代表空闲)的线程。如果我们不使用非重入锁,这个任务线程就会给中断,从而导致一些奇怪的问题。

源码如下:

 private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        /**
         * This class will never be serialized, but we provide a
         * serialVersionUID to suppress a javac warning.
         */
        private static final long serialVersionUID = 6138294804551838833L;

        /** Thread this worker is running in.  Null if factory fails. */
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;
        /** Per-thread task counter */
        volatile long completedTasks;

        /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        /** Delegates main run loop to outer runWorker  */
        public void run() {
            runWorker(this);
        }

        // Lock methods
        //
        // The value 0 represents the unlocked state.
        // The value 1 represents the locked state.

        protected boolean isHeldExclusively() {
            return getState() != 0;
        }
        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) {
              setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
        protected boolean tryRelease(int unused) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
        public void lock()        { acquire(1); }
        public boolean tryLock()  { return tryAcquire(1); }
        public void unlock()      { release(1); }
        public boolean isLocked() { return isHeldExclusively(); }
        void interruptIfStarted() {
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }
    }

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值