Java线程池深入分析

由于最近在开发一个嵌入式设备时,用户会进行频繁的取消功能、手机频繁的按返回键等,而且客户的手机配置又低,导致程序在极端测试时出现了屏幕卡顿或应用崩溃的问题。而且我本身的应用框架线程间的通讯主要用的还是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();
打印日志如下:


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










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值