Java线程池深入分析

来源:互联网 发布:炫浪网络在线阅读手机 编辑:程序博客网 时间:2024/05/21 10:43

由于最近在开发一个嵌入式设备时,用户会进行频繁的取消功能、手机频繁的按返回键等,而且客户的手机配置又低,导致程序在极端测试时出现了屏幕卡顿或应用崩溃的问题。而且我本身的应用框架线程间的通讯主要用的还是AsynchTask与Handler。但是由于android本身提供的这些框架不能让子线程实现立刻停止运行,没法让应用达到最后的体验。所以我决定深研线程,首先从java本身提供的多线程框架与android提供的线程间的通信入手,将其彻底研究透,这样为我自身的框架做一个知识铺垫。没有办法,谁让我是一个喜欢将应用做到极致美的程序猿。接下来就开始我的学习。

java提供的线程池框架

java1.5后引入了Executor框架与Lock锁等。这样我们可以很方便的处理多线程的问题。使用他们很容易,按照api去操作就可以,但是具体是怎么实现的,这个需要深入的学习一下。那么从调用与源码开始。

1、通过Executors入口创建线程池,这是java提供的初始化接口

ExecutorService mCacheThreadExecutor = Executors.newCachedThreadPool();// 一个没有限制最大线程数的线程池        ExecutorService mFixedThreadExecutor = Executors.newFixedThreadPool(count);// 限制线程池大小为count的线程池        ExecutorService mScheduledThreadExecutor = Executors.newScheduledThreadPool(count);// 一个可以按指定时间可周期性的执行的线程池        ExecutorService mSingleThreadExecutor = Executors.newSingleThreadExecutor();// 每次只执行一个线程任务的线程池

该四个基本线程池的基本用法与区别,请看这片文章:点击打开链接

2、自定义线程池

ThreadPoolExecutor pool = new ThreadPoolExecutor(1,                2,                60,                TimeUnit.SECONDS,                new ArrayBlockingQueue<Runnable>(3),                Executors.defaultThreadFactory(),                new ThreadPoolExecutor.DiscardPolicy()        );        pool.prestartAllCoreThreads();

3、参数分析

进入Executors内部发现除了newScheduledThreadPool比较特殊,其他三个线程池都使用了ThreadPoolExecutor线程池工厂类,通过它可以快速初始化一个符合业务需求的线程池。例如:

    public static ExecutorService newFixedThreadPool(int nThreads) {        return new ThreadPoolExecutor(nThreads, nThreads,                                      0L, TimeUnit.MILLISECONDS,                                      new LinkedBlockingQueue<Runnable>());    }

其本质是通过不同的参数初始化一个ThreadPoolExecutor对象,具体参数描述如下:

corePoolSize:线程池的核心线程数

当提交一个任务时(1)若线程池数小于该核心数,则创建新线程执行任务

                                 (2)若线程数大于该核心数,则任务被保存到阻塞队列中

另外,ThreadPoolExecutor提供了parestartAllCoreThreads()方法,该方法只能在自定义的线程池中使用。

maximumPoolSize:线程池中允许的最大线程数。

如果当前阻塞队列满了。且继续提交任务,则创建新的线程执行任务,前提是当前的线程数小于maximumPoolSize.

keepAliveTime:线程空闲的存活时间。

当线程没有任务执行时,继续存活的时间,默认情况下,该参数只有线程数大于corePoolSize时才有用。

unit:keepAliveTime的单位。

WorkQueue:用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口。提供了如下阻塞队列:

1、ArrayBlockingQueue:基于数组结构的有界阻塞队列,大小固定,按FIFO排序任务;
2、LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene。可以指定大小,也可以不指定大小,不指定的话,大小就是Integer.MAX_VALUE。
3、SynchronousQuene:这个是同步Queue,属于线程安全的BlockingQueue的一种,在SynchronousQueue中,生产者线程的插入操作必须要等待消费者线程的移除操作,Synchronous内部没有数据缓存空间,因此我们无法对SynchronousQueue进行读取或者遍历其中的数据,元素只有在你试图取走的时候才有可能存在。我们可以理解为生产者和消费者互相等待,等到对方之后然后再一起离开。吞吐量通常要高于LinkedBlockingQuene;
4、priorityBlockingQuene:具有优先级的无界阻塞队列。大小就为a和LinkedBlockingQueue类似,不同的是PriorityBlockingQueue中的元素不是按照FIFO来排序的,而是按照元素的Comparator来决定存取顺序的(这个功能也反映了存入PriorityBlockingQueue中的数据必须实现了Comparator接口)。

threadFactory;创建线程的工厂

static class DefaultThreadFactory implements ThreadFactory {        private static final AtomicInteger poolNumber = new AtomicInteger(1);        private final ThreadGroup group;        private final AtomicInteger threadNumber = new AtomicInteger(1);        private final String namePrefix;        DefaultThreadFactory() {            SecurityManager s = System.getSecurityManager();            group = (s != null) ? s.getThreadGroup() :                                  Thread.currentThread().getThreadGroup();            namePrefix = "pool-" +                          poolNumber.getAndIncrement() +                         "-thread-";//给每个新建的线程设置一个具有识别度的线程名。        }        public Thread newThread(Runnable r) {            Thread t = new Thread(group, r,                                  namePrefix + threadNumber.getAndIncrement(),                                  0);            if (t.isDaemon())                t.setDaemon(false);//标记不是守护线程            if (t.getPriority() != Thread.NORM_PRIORITY)                t.setPriority(Thread.NORM_PRIORITY);//设置线程优先级为普通            return t;        }    }

handler;线程池的饱和策略

当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
1、AbortPolicy:直接抛出异常,默认策略;
2、CallerRunsPolicy:用调用者所在的线程来执行任务;
3、DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
4、DiscardPolicy:直接丢弃任务;
5、也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。

4、线程池内部:ThreadPoolExecutor类分析

1、线程池内部状态,通过该类的字段参数控制
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));    private static final int COUNT_BITS = Integer.SIZE - 3;//32-3=29    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;    // runState is stored in the high-order bits    private static final int RUNNING    = -1 << COUNT_BITS;    private static final int SHUTDOWN   =  0 << COUNT_BITS;    private static final int STOP       =  1 << COUNT_BITS;    private static final int TIDYING    =  2 << COUNT_BITS;    private static final int TERMINATED =  3 << COUNT_BITS;    // Packing and unpacking ctl    private static int runStateOf(int c)     { return c & ~CAPACITY; }//线程池运行状态    private static int workerCountOf(int c)  { return c & CAPACITY; }//工作线程数    private static int ctlOf(int rs, int wc) { return rs | wc; }

参数ct1的功能很强大,由于java中int类型是32位的,所以利用低29位表示线程池中线程池数,通过高3位表示线程的运行状态:
1、RUNNING:-1 << COUNT_BITS,即高3位为111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;
2、SHUTDOWN: 0 << COUNT_BITS,即高3位为000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
3、STOP : 1 << COUNT_BITS,即高3位为001,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
4、TIDYING : 2 << COUNT_BITS,即高3位为010;
5、TERMINATED: 3 << COUNT_BITS,即高3位为011;

2、execute方法解析

public void execute(Runnable command) {        if (command == null)//如果任务为空,则抛出异常            throw new NullPointerException();        int c = ctl.get();//获取线程池状态        if (workerCountOf(c) < corePoolSize) {//工作线程数小于核心线程数            if (addWorker(command, true))//添加核心线程数来执行任务<                return;            c = ctl.get();//若新添线程失败,则从新检测线程池状态        }        if (isRunning(c) && workQueue.offer(command)) {//往队列中添加任务,并且成功            int recheck = ctl.get();//再次获取线程池状态            if (! isRunning(recheck) && remove(command))//线程池已经停止工作,并且任务还在队列中                reject(command);//拒绝执行任务            else if (workerCountOf(recheck) == 0)//如果工作线程数为0                addWorker(null, false);        }        else if (!addWorker(command, false))//添加扩展线程            reject(command);    }



3、addWorker实现
从方法execute的实现可以看出:addWorker主要负责创建新的线程并执行任务
private boolean addWorker(Runnable firstTask, boolean core) {        retry:         //尝试增加线程数,增加成功则跳出该循环,否则返回false        for (;;) {//循环1            int c = ctl.get();            int rs = runStateOf(c);//线程池运行状态            // Check if queue empty only if necessary.            if (rs >= SHUTDOWN &&                ! (rs == SHUTDOWN &&                   firstTask == null &&                   ! workQueue.isEmpty()))//如果线程池已经停止工作并且线程池状态不为SHUTDOWN或者firstTask不为null或者workQueue为空其中之一                return false;            for (;;) {                int wc = workerCountOf(c);//或取核心线程数                if (wc >= CAPACITY ||                    wc >= (core ? corePoolSize : maximumPoolSize))//判断线程数是否已经饱和                    return false;                if (compareAndIncrementWorkerCount(c))//增加线程数                    break retry;//增加线程数成功,跳出循环1                c = ctl.get();  // Re-read ctl                if (runStateOf(c) != rs)//线程池状态有所变化                    continue retry;//继续循环1                // else CAS failed due to workerCount change; retry inner loop            }        }        //添加线程数成功,开始执行下面的代码        boolean workerStarted = false;        boolean workerAdded = false;        Worker w = null;        try {            w = new Worker(firstTask);//创建worker,新线程也是在这里创建的,通过ThreadFactory            final Thread t = w.thread;            if (t != null) {                final ReentrantLock mainLock = this.mainLock;                mainLock.lock();                try {                    // Recheck while holding lock.                    // Back out on ThreadFactory failure or if                    // shut down before lock acquired.                    int rs = runStateOf(ctl.get());                    if (rs < SHUTDOWN ||                        (rs == SHUTDOWN && firstTask == null)) {                        if (t.isAlive()) // precheck that t is startable                            throw new IllegalThreadStateException();                        workers.add(w);                        int s = workers.size();                        if (s > largestPoolSize)                            largestPoolSize = s;                        workerAdded = true;                    }                } finally {                    mainLock.unlock();                }                if (workerAdded) {                    t.start();                    workerStarted = true;                }            }        } finally {            if (! workerStarted)                addWorkerFailed(w);        }        return workerStarted;    }
从上述代码中可以看出addWoker方法实现:
1、判断线程池的状态,如果线程池的状态值大于或等SHUTDOWN,则不处理提交的任务,直接返回;
2、通过参数core判断当前需要创建的线程是否为核心线程,如果core为true,且当前线程数小于corePoolSize,则跳出循环,开始创建新的线程。

线程池的工作线程通过Woker类实现,在ReentrantLock锁的保证下,把Woker实例插入到HashSet后,并启动Woker中的线程,其中Worker类设计如下:
1、继承了AQS类,可以方便的控制工作线程的状态操作,然后通过线程池的状态与worker的状态来控制是否中止线程。
2、实现了Runnable接口,可以将自身作为一个任务在工作线程中执行;
3、当前提交的任务firstTask作为参数传入Worker的构造方法;
代码如下:
private final class Worker        extends AbstractQueuedSynchronizer        implements Runnable{    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;    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) {            }        }    }}
从Woker类的构造方法实现可以发现:线程工厂在创建线程thread时,将Woker实例本身this作为参数传入,当执行start方法启动线程thread时,本质是执行了ThreadPoolExecutor的runWorker方法,因为worker是ThreadPoolExecutor的内部类,所以可以调用ThreadPoolExecutor中的方法。代码如下:
runWorker(Worker w) {        Thread wt = Thread.currentThread();        Runnable task = w.firstTask;        w.firstTask = null;        w.unlock(); // allow interrupts        boolean completedAbruptly = true;        try {            while (task != null || (task = getTask()) != null) {                w.lock();                // If pool is stopping, ensure thread is interrupted;                // if not, ensure thread is not interrupted.  This                // requires a recheck in second case to deal with                // shutdownNow race while clearing interrupt                if ((runStateAtLeast(ctl.get(), STOP) ||                        (Thread.interrupted() &&                                runStateAtLeast(ctl.get(), STOP))) &&                        !wt.isInterrupted())                    wt.interrupt();                try {                    beforeExecute(wt, task);                    Throwable thrown = null;                    try {                        task.run();                    } catch (RuntimeException x) {                        thrown = x; throw x;                    } catch (Error x) {                        thrown = x; throw x;                    } catch (Throwable x) {                        thrown = x; throw new Error(x);                    } finally {                        afterExecute(task, thrown);                    }                } finally {                    task = null;                    w.completedTasks++;                    w.unlock();                }            }            completedAbruptly = false;        } finally {            processWorkerExit(w, completedAbruptly);        }    }
runWorker方法是线程池中每个线程运行、不断执行任务的核心:
1、线程启动之后,通过unlock方法释放锁,设置AQS的state为0,表示当前线程为正常状态,可以工作。
2、获取第一个任务firstTask,这个任务是创建该线程时就传递进来的任务。执行任务的run方法,不过在执行任务之前,会进行加锁操作,任务执行完会释放锁;
3、在执行任务的前后,可以根据业务场景自定义beforeExecute和afterExecute方法,这个需要从新定义ThreadPoolExecutor,实现里面的方法。我会在最后将这个功能用代码实现。
4、firstTask执行完成之后,通过getTask方法从阻塞队列中获取等待的任务(上述代码的第八行),如果队列中没有任务,getTask方法会被阻塞并挂起,不会占用cpu资源;

getTask实现代码如下:
private Runnable getTask() {        boolean timedOut = false; // Did the last poll() time out?        for (;;) {            int c = ctl.get();            int rs = runStateOf(c);            // Check if queue empty only if necessary.            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {                decrementWorkerCount();                return null;            }            int wc = workerCountOf(c);            // Are workers subject to culling?            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;            if ((wc > maximumPoolSize || (timed && timedOut))                && (wc > 1 || workQueue.isEmpty())) {                if (compareAndDecrementWorkerCount(c))                    return null;                continue;            }            try {                Runnable r = timed ?                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :                    workQueue.take();                if (r != null)                    return r;                timedOut = true;            } catch (InterruptedException retry) {                timedOut = false;            }        }    }
整个getTask操作在自旋下完成:
1、workQueue.take:如果阻塞队列为空,当前线程会被挂起等待;当队列中有任务加入时,线程被唤醒,take方法返回任务,并执行;(在倒数第10行可以看到)
2、workQueue.poll:如果在keepAliveTime时间内,阻塞队列还是没有任务,则返回null;
所以,线程池中实现的线程可以一直执行由用户提交的任务。
线程的挂起与唤醒,是在队列中实现的,实质还是Lock锁的休眠与唤醒代码如下:
public E take() throws InterruptedException {        E x;        int c = -1;        final AtomicInteger count = this.count;        final ReentrantLock takeLock = this.takeLock;        takeLock.lockInterruptibly();        try {            while (count.get() == 0) {                notEmpty.await();            }            x = dequeue();            c = count.getAndDecrement();            if (c > 1)                notEmpty.signal();        } finally {            takeLock.unlock();        }        if (c == capacity)            signalNotFull();        return x;    }
 /**     * Signals a waiting take. Called only from put/offer (which do not     * otherwise ordinarily lock takeLock.)     */    private void signalNotEmpty() {        final ReentrantLock takeLock = this.takeLock;        takeLock.lock();        try {            notEmpty.signal();        } finally {            takeLock.unlock();        }    }

线程池实现beforeExecute和afterExecute方法,示例代码如下:

import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.Executors;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;/** * Created by Xi on 2017 */public class DefineThreadPoolExecutor {    public void toDefine(){        final Runnable ru=new Runnable() {            @Override            public void run() {                System.out.println("run1执行了。。。。");            }        };        final Runnable ru2=new Runnable() {            @Override            public void run() {                System.out.println("run2执行了。。。。");            }        };        //自定义线程池,实现beforeExecute、afterExecute方法        ThreadPoolExecutor pool = new ThreadPoolExecutor(3,                5,                60,                TimeUnit.SECONDS,                new ArrayBlockingQueue<Runnable>(3),                Executors.defaultThreadFactory(),                new ThreadPoolExecutor.DiscardPolicy()        ){            @Override            protected void beforeExecute(Thread t, Runnable r) {                super.beforeExecute(t, r);                if(r.equals(ru)){//若当前Runnable为任务1才输出日志                    System.out.println("之前执行了。。。。");                }            }            @Override            protected void afterExecute(Runnable r, Throwable t) {                super.afterExecute(r, t);                System.out.println("之后执行了。。。。");            }        };        pool.execute(ru);        pool.execute(ru2);    }}
执行下面代码:
new DefineThreadPoolExecutor().toDefine();
打印日志如下:


下面来一个重磅,线程池内部整体流程图: