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类分析
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位表示线程的运行状态:
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); }
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,则跳出循环,开始创建新的线程。
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;
所以,线程池中实现的线程可以一直执行由用户提交的任务。
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(); } }
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();打印日志如下:
- Java线程池深入分析
- Java多线程之线程池深入分析
- Java多线程之线程池深入分析 (下)
- Java多线程之线程池深入分析(上)
- Java多线程之线程池深入分析(下)
- Java多线程之线程池深入分析(下)
- Java多线程之线程池深入分析(上)
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- javascript表格操作
- <Unity>鼠标拖动物体、按下鼠标左键旋转观察物体、鼠标滚轮缩放视野
- 人工智能攻陷电竞,和AI对战你能赢吗?欢迎留下你的评论
- android自定义view 右侧字母导航
- 动态规划-背包问题(状态转换)
- Java线程池深入分析
- 利用Plupload来进行前端文件上传
- arch/x86/mm/pgtable.c
- ShareSDK tweet分享
- jvm垃圾回收器
- spring scheduled-task集群问题
- 面捕与三角剖分
- 黑科技:纯CSS定制文本省略
- codeforces 459C