Android多线程
来源:互联网 发布:淘宝什么数据库 编辑:程序博客网 时间:2024/06/14 18:23
主要内容
Java多线程
Android多线程的实现
Android线程池
Android异步任务
一、多线程的优点
❖ 耗时的操作使用线程,提高应用程序响应
❖ 多CPU系统中,使用线程提高CPU利用率
❖ 改善程序结构
二、多线程的缺点
❖ 对线程进型管理需要额外的CPU开销
❖ 对公有变量的同时读或写
❖ 等候使用共享资源时造成程序的运行速度变慢
❖ 一个线程的崩溃可能影响到整个程序的稳定性
JAVA中的多线程
编写多线程程序是为了实现多任务的并发执行,从而能够更好地与用户交互。一般有三种方法:
Thread:
线程是一个程序的多个执行路径,执行调度的单元,依托于进程的存在。线程不仅可以共享进程的内在,而且还拥有一个属于自己的内存空间,这段内存空间就是线程栈,是在建立线程时系统分配的,主要用来保存线内部所使用的数据,如线程执行函数中所定义的变量,线程的状态等信息。
Java中的多线程是一个抢占机制,抢占机制指的是多个线程处于可运行状态,但只是允许一个线程运行,他们通过竞争方式抢占CPU
定义一个线程(Defining a Thread)有两种方法:
- [1] 继承java.lang.Thread类,重写run()方法
/** * 使用继承java.lang.Thread类的方式创建一个线程 * * @author DreamSea 2011-12-29 20:17:06 */public class ThreadTest extends Thread { /** * 重写(Override)run()方法 JVM会自动调用该方法 */ public void run() { System.out.println("I'm running!"); }}
重写(override)run()方法在该线程的start()方法被调用后,JVM会自动调用run方法来执行任务。
- [2] 实现java.lang.Runnable接口,并将其传入到Thread类对象中
[java] view plain copy/** * 通过实现Runnable接口创建一个线程 * @author DreamSea */ public class ThreadTest implements Runnable { public void run() { System.out.println("I'm running!"); } }
3.线程的启动
任何一个线程的执行的前提都是必须有Thread class的实例存在,并且通过调用run()方法启动线程。
- [1] 如果线程是继承Thread类,则创建方式如下:
ThreadTest1 tt = new ThreadTest1(); tt.start();
- [2] 如果是实现Runnable接口,则创建方式如下:
ThreadTest2 tt = new ThreadTest2(); Thread t = new Thread(tt); t.start();
4.线程的状态
[1] 新生状态(New):当一个线程的实例被创建即使用new关键字和Thread类或其子类创建一个线程对象后,此时该线程处于新生(new)状态,处于新生状态的线程有自己的内存空间,但该线程并没有运行,此时线程还不是活着的(not alive)。
[2] 就绪状态(Runnable):通过调用线程实例的start()方法来启动线程使线程进入就绪状态(runnable);处于就绪状态的线程已经具备了运行条件,但还没有被分配到CPU即不一定会被立即执行,此时处于线程就绪队列,等待系统为其分配CPCU,等待状态并不是执行状态; 此时线程是活着的(alive)。
[3] 运行状态(Running):一旦获取CPU(被JVM选中),线程就进入运行(running)状态,线程的run()方法才开始被执行;在运行状态的线程执行自己的run()方法中的操作,直到调用其他的方法而终止、或者等待某种资源而阻塞、或者完成任务而死亡;如果在给定的时间片内没有执行结束,就会被系统给换下来回到线程的等待状态;此时线程是活着的(alive)。
[4] 阻塞状态(Blocked):通过调用join()、sleep()、wait()或者资源被暂用使线程处于阻塞(blocked)状态;处于Blocking状态的线程仍然是活着的(alive)。
[5] 死亡状态(Dead):当一个线程的run()方法运行完毕或被中断或被异常退出,该线程到达死亡(dead)状态。此时可能仍然存在一个该Thread的实例对象,当该Thread已经不可能在被作为一个可被独立执行的线程对待了,线程的独立的call stack已经被dissolved。一旦某一线程进入Dead状态,他就再也不能进入一个独立线程的生命周期了。对于一个处于Dead状态的线程调用start()方法,会出现一个运行期(runtime exception)的异常;处于Dead状态的线程不是活着的(not alive)。
线程的状态转换图如下所示:
5.线程的方法、属性
[1] 优先级(priority)
每个类都有自己的优先级,一般property用1-10的整数表示,默认优先级是5,优先级最高是10;优先级高的线程并不一定比优先级低的线程执行的机会高,只是执行的机率高;默认一个线程的优先级和创建他的线程优先级相同。
[2] Thread.sleep()/sleep(long millis)
当前线程睡眠/millis的时间(millis指定睡眠时间是其最小的不执行时间,因为sleep(millis)休眠到达后,无法保证会被JVM立即调度);sleep()是一个静态方法(static method) ,所以他不会停止其他的线程也处于休眠状态;线程sleep()时不会失去拥有的对象锁。 作用:保持对象锁,让出CPU,调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留一定的时间给其他线程执行的机会。
[3] Thread.yield()
让出CPU的使用权,给其他线程执行机会、让同等优先权的线程运行(但并不保证当前线程会被JVM再次调度、使该线程重新进入Running状态),如果没有同等优先权的线程,那么yield()方法将不会起作用。
[4] Thread.join()
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
[5] object.wait()
当一个线程执行到wait()方法时,他就进入到一个和该对象相关的等待池(Waiting Pool)中,同时失去了对象的机锁—暂时的,wait后还要返还对象锁。当前线程必须拥有当前对象的锁,如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常,所以wait()必须在synchronized block中调用。
[6] Object.notify()/notifyAll()
唤醒在当前对象等待池中等待的第一个线程/所有线程。notify()/notifyAll()也必须拥有相同对象锁,否则也会抛出IllegalMonitorStateException异常。
[7] Synchronizing Block
Synchronized Block/方法控制对类成员变量的访问;Java中的每一个对象都有唯一的一个内置的锁,每个Synchronized Block/方法只有持有调用该方法被锁定对象的锁才可以访问,否则所属线程阻塞;机锁具有独占性、一旦被一个Thread持有,其他的Thread就不能再拥有(不能访问其他同步方法),方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态
6.Object.wait()和Thread.sleep(long)的区别
[1] sleep()方法
sleep()使当前线程进入停滞状态(阻塞当前线程),让出CUP的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会。
sleep()是Thread类的Static(静态)的方法;因此他不能改变对象的机锁,所以当在一个Synchronized块中调用Sleep()方法是,线程虽然休眠了,但是对象的机锁并没有被释放,其他线程无法访问这个对象(即使睡着也持有对象锁)。
在sleep()休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。
[2] wait()方法
wait()方法是Object类里的方法;当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超时时间到后还需要返还对象锁),其他线程可以访问。
wait()使用notify或者notifyAlll或者指定睡眠时间来唤醒当前等待池中的线程。
wiat()必须放在synchronized block中,否则会在program runtime时扔出”java.lang.IllegalMonitorStateException”异常。
Runnable和Callable的区别
1.Callable需要实现的方法是call();Runnable需要实现的方法是run()。
2.call方法可以抛出异常,run方法不可以。
3.Callable的任务执行后可返回值; Runnable的任务是不能返回值的。
4.运行Callable任务可以拿到一个Future对象,表示异步计算的结果,它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
/** * Callable的使用 * * @throws Exception */ @Test public void testCallable() throws Exception { final ExecutorService exec = Executors.newFixedThreadPool(5); Callable<String> call = new Callable<String>() { public String call() throws Exception { Thread.sleep(1000 * 10);//休眠指定的时间,此处表示该操作比较耗时 return "----Other less important but longtime things."; } }; Future<String> future = exec.submit(call); //重要的事情 System.out.println("----Let's do important things. start"); Thread.sleep(1000 * 3); System.out.println("----Let's do important things. end"); //不重要的事情 while (!future.isDone()) { System.out.println("----still waiting...."); Thread.sleep(1000 * 1); } System.out.println("----get sth...."); String obj = future.get(); System.out.println(obj); //关闭线程池 exec.shutdown(); }
Android多线程
Android 程序的大多数代码操作都必须执行在主线程,例如系统事件(例如设备屏幕发生旋转),输入事件(例如用户点击滑动等),程序回调服务,UI 绘制以及闹钟事件等等。那么我们在上述事件或者方法中插入的代码也将执行在主线程。
阻碍主线程去响应点击/滑动事件,阻碍主线程的 UI 绘制等等。我们知道,为了让屏幕的刷新帧率达到 60fps,我们需要确保 16ms 内完成单次刷新的操作。一旦我们在主线程里面执行的任务过于繁重就可能导致接收到刷新信号的时候因为资源被占用而无法完成这次刷新操作,这样就会产生掉帧的现象,刷新帧率自然也就跟着下降了(一旦刷新帧率降到 20fps 左右,用户就可以明显感知到卡顿不流畅了)。
In Android, application responsiveness is monitored by the Activity Manager and Window Manager system services. Android will display the ANR dialog for a particular application when it detects one of the following conditions:
- No response to an input event (such as key press or screen touch events) within 5 seconds.
- A Broadcast Receiver hasn’t finished executing within 10 seconds.
为了避免上面提到的掉帧问题,我们需要使用多线程的技术方案,把那些操作复杂的任务移动到其他线程当中执行,这样就不容易阻塞主线程的操作,也就减小了出现掉帧的可能性
解决方案
- AsyncTask: 为 UI 线程与工作线程之间进行快速的切换提供一种简单便捷的机制。适用于当下立即需要启动,但是异步执行的生命周期短暂的使用场景。
- HandlerThread: 为某些回调方法或者等待某些任务的执行设置一个专属的线程,并提供线程任务的调度机制。
- ThreadPool: 把任务分解成不同的单元,分发到各个不同的线程上,进行同时并发处理。
- IntentService: 适合于执行由 UI 触发的后台 Service 任务,并可以把后台任务执行的情况通过一定的机制反馈给 UI。
在很多时候,线程不仅仅是线性执行一系列的任务就结束那么简单的,我们会需要增加一个任务队列,让线程不断的从任务队列中获取任务去进行执行,另外我们还可能在线程执行的任务过程中与其他的线程进行协作。如果这些细节都交给我们自己来处理,这将会是件极其繁琐又容易出错的事情。
所幸的是,Android 系统为我们提供了 Looper,Handler,MessageQueue 来帮助实现上面的线程任务模型。
Looper: 能够确保线程持续存活并且可以不断的从任务队列中获取任务并进行执行。
角色分析:
Handler进程间通讯主要关联了Thread,ThreadLocal,Handler,Message,MessageQueue,Looper,,这几个类在内。我们先看看这几个类各自在这个实现工程中扮演者什么样的角色!
- Thread:整个机制产生的意义,所有这个机制就是为Thread服务!Looper的创建是为Thread而创建,其实是Thread持有。Handler使用的Looper是由Thread提供!
- ThreadLocal:Thread属性的存储者,内部维护一个ThreadLocalMap,其中存放了Thread的所有属性,Looper就是存放在这儿。
- Handler:消息的发起者,发出一个消息。消息的处理者,消息处理的时间到了对该消息进行处理。必须有一个Looper,通过操作Looper持有的MessageQueue实现消息的发送,当Looper通知处理Message的时候,对Message进行处理。
- Message:数据和信号携带者,携带数据进行传递。处理时机决定者,通过when属性决定消息的处理时间。消息队列的产生者,将一个个的message对象串联起来,形成消息队列。
- MessageQueue:消息存放者,存放了一个消息队列,有消息进来的时候安插在指定的位置,需要处理的时候将消息取出来。消息排队者,将消息按处理的时间进行存放,让消息排队进行处理。
- Looper:运行机制全局掌控者,让整个机制活动起来的关键人员,将所有角色联系起来,根据具体实现进行调度,完成整个机制的任务。内部持有一个MessageQueue,
- Handler通过关联Looper,联系上MessageQueue。
大体的运转结构
了解了这些类在整个任务中充当的角色何彼此之间的联系之后,我们来看看他们大体都做了些什么工作,让整个机制运转起来。
Handler通过sendMessage方法,将Message发送出去,MessageQueue通过queueMessage方法将消息存插入到消息队列中等待被处理。Looper通过loop方法在特定的时间到MessageQueue中将消息取出来,回调Handler的dispatchMessage方法将消息交给Handler处理。
具体为什么就将数据在线程之间传递成功了呢?
通过第2点,我们大体了解了整个机制的原理,但是这样做为什么就能实现线程间数据的传递呢?这个问题很关键,在我们源代码分析完了之后再来进行讲解,这儿先不说。
Looper源代码分析:
public final class Looper { private static final String TAG = "Looper"; /* ThreadLocal保存了线程所有的变量,可以通过get方法去获取对应的变量,取得的变量是什么取决于泛型, 这儿传的是Looper,所以获取的是线程的Looper变量,在prepare方法中会创建一个looper存在sThreadLocal中, 如果没有先prepare,sThreadLocal.get取得的就是null*/ static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static Looper sMainLooper; // guarded by Looper.class final MessageQueue mQueue; final Thread mThread; private Printer mLogging; private long mTraceTag; /** * 初始化一个looper给当前线程 */ public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { //先查询sThreadLocal,如果已经存在looper会抛出异常 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } //创建一个looper对象存放到sThreadLocal中 sThreadLocal.set(new Looper(quitAllowed)); } /** * 为UI线程准备的,系统初始化UI线程的时候调用。我们一般不需要调用这个方法 */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } /** * 获取UI线程中的looper变量 */ public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } } /** * 从MessageQueue中取出Message,调用Handler中的dispatchMessage,对消息进行处理 */ public static void loop() { //获取到looper,如果没有之前prepare,取到的会是null,抛出异常 final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } //取出消息中的消息队里MessageQueue final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. /** * 确认当前线程的身份是属于当前本地进程,确保track是属于这个线程 */ Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); //一个死循环,一直处理消息队里中的消息,直到所有的消息处理完成才会退出 for (; ; ) { //获取下一个消息,有可能阻塞 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { //开始记录trace,在ANR分析中会用到trace.txt文件 Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { //调用handler的方法处理消息 msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { //结束trace记录 Trace.traceEnd(traceTag); } } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } //回收该消息 msg.recycleUnchecked(); } } /** * R获取当前线程的looper变量 */ public static @Nullable Looper myLooper() { return sThreadLocal.get(); } /** * Return the {@link MessageQueue} object associated with the current * thread. This must be called from a thread running a Looper, or a * NullPointerException will be thrown. */ public static @NonNull MessageQueue myQueue() { return myLooper().mQueue; } /** * 构造方法私有化,在prepare中调用,初始化。 * * @param quitAllowed */ private Looper(boolean quitAllowed) { //创建一个消息队列,用于存放消息 mQueue = new MessageQueue(quitAllowed); //取得当前线程,让该looper跟当前线程关联 mThread = Thread.currentThread(); } /** * 判断looper是否为当前线程的looper */ public boolean isCurrentThread() { return Thread.currentThread() == mThread; } /** * 记录被该looper对象处理的所有消息的控制标志,如果enabled, * 将会通过Trace.traceBegin和Trace.traceEnd(traceTag)控制记录每一条消息的处理状况; * * @param printer A Printer object that will receive log messages, or * null to disable message logging. */ public void setMessageLogging(@Nullable Printer printer) { mLogging = printer; } /** * {@hide} */ public void setTraceTag(long traceTag) { mTraceTag = traceTag; } /** * 退出当前looper操作 * 在当前looper的消息队列没有消息需要处理的时候才可以调用该方法, * 该方法调用的时候会将所有消息队列中的消息回收, * 在这个方法之后所有的消息将不会再被处理,包括当前正在处理的消息 * 一般来说用quitSafely代替该方法,因为该方法不安全,他不能保证已经被delivered的消息处理完成 */ public void quit() { mQueue.quit(false); } /** * 安全终止looper * 会先将消息的处理时间在当前时间点之前的消息全部处理完之后才会终止looper, * 但是不能保证执行时间是在当前时间之后的消息被处理 */ public void quitSafely() { mQueue.quit(true); } /** * 获取当前looper关联的线程 */ public @NonNull Thread getThread() { return mThread; } /** * 获取当前looper持有的消息队列 */ public @NonNull MessageQueue getQueue() { return mQueue; } public void dump(@NonNull Printer pw, @NonNull String prefix) { pw.println(prefix + toString()); mQueue.dump(pw, prefix + " "); } @Override public String toString() { return "Looper (" + mThread.getName() + ", tid " + mThread.getId() + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}"; }}
MessageQueue源代码分析:
/* 内部维护消息队列,这个消息队列中的消息是按照处理的时间先后顺序排队的 * 跟looper协作进行消息的分发,指派。 * 消息是通过Handler关联上Looper之后,放到消息队列中,然 * 后通过looper取出,调用handler的dispatchMessage方法进行处理 */public final class MessageQueue { private static final String TAG = "MessageQueue"; private static final boolean DEBUG = false; // 代表当前消息队列能否exit private final boolean mQuitAllowed; @SuppressWarnings("unused") private long mPtr; // used by native code Message mMessages; private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); private SparseArray<FileDescriptorRecord> mFileDescriptorRecords; private IdleHandler[] mPendingIdleHandlers; // private boolean enqueueMessage; // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout. private boolean mBlocked; // The next barrier token. // Barriers are indicated by messages with a null target whose arg1 field carries the token. private int mNextBarrierToken; private native static long nativeInit(); private native static void nativeDestroy(long ptr); private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/ private native static void nativeWake(long ptr); private native static boolean nativeIsPolling(long ptr); private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events); //构造方法,初始化 MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit(); } @Override protected void finalize() throws Throwable { try { dispose(); } finally { super.finalize(); } } // Disposes of the underlying message queue. // Must only be called on the looper thread or the finalizer. private void dispose() { if (mPtr != 0) { nativeDestroy(mPtr); mPtr = 0; } } /** * 判断跟这个消息队列关联的looper是不是为空闲状态,true表示空闲,false表示非空闲 */ public boolean isIdle() { synchronized (this) { //获取系统时间 final long now = SystemClock.uptimeMillis(); //如果消息队列中没有消息,或者需要处理的消息里面时间最早的一个都是在当前系统时间的后面,那就代表着空闲 return mMessages == null || now < mMessages.when; } } public void addIdleHandler(@NonNull IdleHandler handler) { if (handler == null) { throw new NullPointerException("Can't add a null IdleHandler"); } synchronized (this) { mIdleHandlers.add(handler); } } public void removeIdleHandler(@NonNull IdleHandler handler) { synchronized (this) { mIdleHandlers.remove(handler); } } public boolean isPolling() { synchronized (this) { return isPollingLocked(); } } private boolean isPollingLocked() { // If the loop is quitting then it must not be idling. // We can assume mPtr != 0 when mQuitting is false. return !mQuitting && nativeIsPolling(mPtr); } public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd, @OnFileDescriptorEventListener.Events int events, @NonNull OnFileDescriptorEventListener listener) { if (fd == null) { throw new IllegalArgumentException("fd must not be null"); } if (listener == null) { throw new IllegalArgumentException("listener must not be null"); } synchronized (this) { updateOnFileDescriptorEventListenerLocked(fd, events, listener); } } public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) { if (fd == null) { throw new IllegalArgumentException("fd must not be null"); } synchronized (this) { updateOnFileDescriptorEventListenerLocked(fd, 0, null); } } private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events, OnFileDescriptorEventListener listener) { final int fdNum = fd.getInt$(); int index = -1; FileDescriptorRecord record = null; if (mFileDescriptorRecords != null) { index = mFileDescriptorRecords.indexOfKey(fdNum); if (index >= 0) { record = mFileDescriptorRecords.valueAt(index); if (record != null && record.mEvents == events) { return; } } } if (events != 0) { events |= OnFileDescriptorEventListener.EVENT_ERROR; if (record == null) { if (mFileDescriptorRecords == null) { mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>(); } record = new FileDescriptorRecord(fd, events, listener); mFileDescriptorRecords.put(fdNum, record); } else { record.mListener = listener; record.mEvents = events; record.mSeq += 1; } nativeSetFileDescriptorEvents(mPtr, fdNum, events); } else if (record != null) { record.mEvents = 0; mFileDescriptorRecords.removeAt(index); } } // Called from native code. private int dispatchEvents(int fd, int events) { // Get the file descriptor record and any state that might change. final FileDescriptorRecord record; final int oldWatchedEvents; final OnFileDescriptorEventListener listener; final int seq; synchronized (this) { record = mFileDescriptorRecords.get(fd); if (record == null) { return 0; // spurious, no listener registered } oldWatchedEvents = record.mEvents; events &= oldWatchedEvents; // filter events based on current watched set if (events == 0) { return oldWatchedEvents; // spurious, watched events changed } listener = record.mListener; seq = record.mSeq; } // Invoke the listener outside of the lock. int newWatchedEvents = listener.onFileDescriptorEvents( record.mDescriptor, events); if (newWatchedEvents != 0) { newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR; } // Update the file descriptor record if the listener changed the set of // events to watch and the listener itself hasn't been updated since. if (newWatchedEvents != oldWatchedEvents) { synchronized (this) { int index = mFileDescriptorRecords.indexOfKey(fd); if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record && record.mSeq == seq) { record.mEvents = newWatchedEvents; if (newWatchedEvents == 0) { mFileDescriptorRecords.removeAt(index); } } } } // Return the new set of events to watch for native code to take care of. return newWatchedEvents; } /** * 如果消息队列中还有下一个消息,取出这个消息 * * @return */ Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration //下一次循环的时间,相当于一个闹钟 int nextPollTimeoutMillis = 0; for (; ; ) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // 跳过消息队列中的异步消息 do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // 如果第一个消息的处理时间还没到,就设定一个闹钟,等到闹钟响起,再开始处理 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { //获取一个消息对象 mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // 消息对立中没有消息 nextPollTimeoutMillis = -1; } // 所有的消息都处理完之后,退出message if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } } /** * 退出消息队列,在looper退出的时候调用 * * @param safe true表示安全退出,false强行退出 */ void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { //安全退出 removeAllFutureMessagesLocked(); } else { //强行退出 removeAllMessagesLocked(); } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); } } public int postSyncBarrier() { return postSyncBarrier(SystemClock.uptimeMillis()); } private int postSyncBarrier(long when) { // Enqueue a new sync barrier token. // We don't need to wake the queue because the purpose of a barrier is to stall it. synchronized (this) { final int token = mNextBarrierToken++; final Message msg = Message.obtain(); msg.markInUse(); msg.when = when; msg.arg1 = token; Message prev = null; Message p = mMessages; if (when != 0) { while (p != null && p.when <= when) { prev = p; p = p.next; } } if (prev != null) { // invariant: p == prev.next msg.next = p; prev.next = msg; } else { msg.next = p; mMessages = msg; } return token; } } public void removeSyncBarrier(int token) { // Remove a sync barrier token from the queue. // If the queue is no longer stalled by a barrier then wake it. synchronized (this) { Message prev = null; Message p = mMessages; while (p != null && (p.target != null || p.arg1 != token)) { prev = p; p = p.next; } if (p == null) { throw new IllegalStateException("The specified message queue synchronization " + " barrier token has not been posted or has already been removed."); } final boolean needWake; if (prev != null) { prev.next = p.next; needWake = false; } else { mMessages = p.next; needWake = mMessages == null || mMessages.target != null; } p.recycleUnchecked(); // If the loop is quitting then it is already awake. // We can assume mPtr != 0 when mQuitting is false. if (needWake && !mQuitting) { nativeWake(mPtr); } } } boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { //消息队列中还没有消息存在或者当前插入消息的处理时间比队列中存在 // 的所有消息的处理时间都要早,就将该消息放在队首 msg.next = p; mMessages = msg; needWake = mBlocked; } else { /** * 将消息存放到队列的中间位置,根据处理的时机,跟队列中所有消息的处理时间进行比较,按从早到迟的顺序安插 */ needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; //遍历队列,根据该消息的处理时间,将该消息插入到合适的位置 for (; ; ) { prev = p; p = p.next; if (p == null || when < p.when) { //如果已经是队尾或者当前消息的处理时间比下一个消息的处理时间早,跳出循环 break; } if (needWake && p.isAsynchronous()) { needWake = false; } } //将消息入队 msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; } /** * 查询handler相关联的消息队列中,是否有指定what和指定obj的消息 * * @param h 想要查询的关联handler * @param what 消息的what值 * @param object 消息的obj值 * @return */ boolean hasMessages(Handler h, int what, Object object) { if (h == null) { return false; } synchronized (this) { Message p = mMessages; while (p != null) { if (p.target == h && p.what == what && (object == null || p.obj == object)) { return true; } p = p.next; } return false; } } /** * 查询handler相关联的消息队列中,是否有指定obj的任务 * * @param h 想要查询的关联handler * @param object 任务的obj值 * @return */ boolean hasMessages(Handler h, Runnable r, Object object) { if (h == null) { return false; } synchronized (this) { Message p = mMessages; while (p != null) { if (p.target == h && p.callback == r && (object == null || p.obj == object)) { return true; } p = p.next; } return false; } } /** * 移除跟h相关联的消息队列中,指定what值和obj值的消息 * * @param h * @param what * @param object */ void removeMessages(Handler h, int what, Object object) { if (h == null) { return; } synchronized (this) { Message p = mMessages; // Remove all messages at front. while (p != null && p.target == h && p.what == what && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; p.recycleUnchecked(); p = n; } // Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && n.what == what && (object == null || n.obj == object)) { Message nn = n.next; n.recycleUnchecked(); p.next = nn; continue; } } p = n; } } } /** * 移除跟h相关联的消息队列中,指定obj值的任务 * * @param h * @param r * @param object */ void removeMessages(Handler h, Runnable r, Object object) { if (h == null || r == null) { return; } synchronized (this) { Message p = mMessages; // Remove all messages at front. while (p != null && p.target == h && p.callback == r && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; p.recycleUnchecked(); p = n; } // Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && n.callback == r && (object == null || n.obj == object)) { Message nn = n.next; n.recycleUnchecked(); p.next = nn; continue; } } p = n; } } } /** * 移除跟指定handler相关联的消息队列中,指定obj值的所有消息 * * @param h * @param object */ void removeCallbacksAndMessages(Handler h, Object object) { if (h == null) { return; } synchronized (this) { Message p = mMessages; // Remove all messages at front. while (p != null && p.target == h && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; p.recycleUnchecked(); p = n; } // Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && (object == null || n.obj == object)) { Message nn = n.next; n.recycleUnchecked(); p.next = nn; continue; } } p = n; } } } //回收消息队列中所有的消息 private void removeAllMessagesLocked() { Message p = mMessages; while (p != null) { Message n = p.next; p.recycleUnchecked(); p = n; } mMessages = null; } /** * 安全退出消息队列 */ private void removeAllFutureMessagesLocked() { final long now = SystemClock.uptimeMillis(); Message p = mMessages; if (p != null) { if (p.when > now) { //如果在消息设定的处理时间还没到,直接回收掉,将不会处理 removeAllMessagesLocked(); } else { Message n; for (; ; ) { n = p.next; if (n == null) { return; } if (n.when > now) { break; } p = n; } p.next = null; do { p = n; n = p.next; p.recycleUnchecked(); } while (n != null); } } } void dump(Printer pw, String prefix) { synchronized (this) { long now = SystemClock.uptimeMillis(); int n = 0; for (Message msg = mMessages; msg != null; msg = msg.next) { pw.println(prefix + "Message " + n + ": " + msg.toString(now)); n++; } pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked() + ", quitting=" + mQuitting + ")"); } } ......}
Message源码分析
public final class Message implements Parcelable { /** * 用于指定当前消息的身份 */ public int what; /** * 该消息所携带的简单数据 */ public int arg1; /** * 消息所携带的简单数据 */ public int arg2; /** * 一个Object类型的数据,这个数据可以用于进程间数据传递,这儿不深究 */ public Object obj; /** * Optional Messenger where replies to this message can be sent. The * semantics of exactly how this is used are up to the sender and * receiver. */ public Messenger replyTo; /** * 通过 {@link Messenger}进行进程间数据传递的时候才会有效,代表发送消息的进程的UID,一般情况下为-1,这儿同样不进行深究; */ public int sendingUid = -1; /** * 用于标志当前消息是否正在使用 */ /*package*/ static final int FLAG_IN_USE = 1 << 0; /** * 用于标志当前消息的同步和异步 */ /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1; /** * Flags to clear in the copyFrom method */ /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE; /*package*/ int flags; /*package*/ long when; /*package*/ Bundle data; /** * 处理该消息的handler */ /*package*/ Handler target; /** * 处理消息的回调,如果没有这个回调将会调用Handler的handleMessage方法进行处理 */ /*package*/ Runnable callback; /** * 保存了下一个message对象的地址 */ /*package*/ Message next; private static final Object sPoolSync = new Object(); private static Message sPool; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; private static boolean gCheckRecycle = true; /** * 从消息池中获取一个message对象返回,避免过多创建对象 */ public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); } /** * 从消息池中获取一个message对象返回,避免过多创建对象 * 并且将传进来的消息中的数据复制给message,并返回 */ public static Message obtain(Message orig) { Message m = obtain(); m.what = orig.what; m.arg1 = orig.arg1; m.arg2 = orig.arg2; m.obj = orig.obj; m.replyTo = orig.replyTo; m.sendingUid = orig.sendingUid; if (orig.data != null) { m.data = new Bundle(orig.data); } m.target = orig.target; m.callback = orig.callback; return m; } /** * 从消息池中获取一个message对象返回,避免过多创建对象 * 指定一个handler去处理 */ public static Message obtain(Handler h) { Message m = obtain(); m.target = h; return m; } /** * 从消息池中获取一个message对象返回,避免过多创建对象 * 指定一个handler去处理 * 并指定处理的回调接口 */ public static Message obtain(Handler h, Runnable callback) { Message m = obtain(); m.target = h; m.callback = callback; return m; } /** * Same as {@link #obtain()}, but sets the values for both <em>target</em> and * <em>what</em> members on the Message. * * @param h Value to assign to the <em>target</em> member. * @param what Value to assign to the <em>what</em> member. * @return A Message object from the global pool. */ public static Message obtain(Handler h, int what) { Message m = obtain(); m.target = h; m.what = what; return m; } /** * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, and <em>obj</em> * members. * * @param h The <em>target</em> value to set. * @param what The <em>what</em> value to set. * @param obj The <em>object</em> method to set. * @return A Message object from the global pool. */ public static Message obtain(Handler h, int what, Object obj) { Message m = obtain(); m.target = h; m.what = what; m.obj = obj; return m; } /** * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, * <em>arg1</em>, and <em>arg2</em> members. * * @param h The <em>target</em> value to set. * @param what The <em>what</em> value to set. * @param arg1 The <em>arg1</em> value to set. * @param arg2 The <em>arg2</em> value to set. * @return A Message object from the global pool. */ public static Message obtain(Handler h, int what, int arg1, int arg2) { Message m = obtain(); m.target = h; m.what = what; m.arg1 = arg1; m.arg2 = arg2; return m; } /** * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, * <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members. * * @param h The <em>target</em> value to set. * @param what The <em>what</em> value to set. * @param arg1 The <em>arg1</em> value to set. * @param arg2 The <em>arg2</em> value to set. * @param obj The <em>obj</em> value to set. * @return A Message object from the global pool. */ public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) { Message m = obtain(); m.target = h; m.what = what; m.arg1 = arg1; m.arg2 = arg2; m.obj = obj; return m; } /** * @hide */ public static void updateCheckRecycle(int targetSdkVersion) { if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) { gCheckRecycle = false; } } /** * 回收message,先检查当前消息是否正在使用,如果正在使用抛出异常 * 如果没有使用就会回收掉,回收之后将不能再使用这个消息 */ public void recycle() { if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked(); } /** * 内部调用回收message,不会检查当前消息是否正在使用,直接回收,回收之后将不能再使用这个消息 */ void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } } /** * 复制o的数据(不包括队列相关的数据)给当前消息 */ public void copyFrom(Message o) { this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM; this.what = o.what; this.arg1 = o.arg1; this.arg2 = o.arg2; this.obj = o.obj; this.replyTo = o.replyTo; this.sendingUid = o.sendingUid; if (o.data != null) { this.data = (Bundle) o.data.clone(); } else { this.data = null; } } /** * 返回消息的处理时间 */ public long getWhen() { return when; } /* *指定处理该消息的handler */ public void setTarget(Handler target) { this.target = target; } /** * 获取将处理该消息的handler */ public Handler getTarget() { return target; } /** * 处理消息的回调,如果没有设置这个回调,调用handler的handleMessage方法进行处理 */ public Runnable getCallback() { return callback; } /** * 获取Bundle类型的data数据,这个数据是通过setData方法传递进来的 * 其实这个方法中的数据可用于进程间数据传递,这儿我们不深究 * * @see #peekData() * @see #setData(Bundle) */ public Bundle getData() { if (data == null) { data = new Bundle(); } return data; } /** * 跟setData差不多,但是不同的是返回的data可能为null */ public Bundle peekData() { return data; } /** * 给data赋值 */ public void setData(Bundle data) { this.data = data; } /** * 发送一个消息给处理该消息的handler,如果这个handler为null,将会抛出空指针异常 */ public void sendToTarget() { target.sendMessage(this); } /** * 如果返回true,该消息就是异步的,将不会手looper的限制。 * 一般来说我们的消息是受looper的控制是同步的 */ public boolean isAsynchronous() { return (flags & FLAG_ASYNCHRONOUS) != 0; } /** * 对消息的异步进行设置,如果设置为true,消息不会受looper限制,将会是异步的 */ public void setAsynchronous(boolean async) { if (async) { flags |= FLAG_ASYNCHRONOUS; } else { flags &= ~FLAG_ASYNCHRONOUS; } } /** * 当前消息是否正在使用 */ boolean isInUse() { return ((flags & FLAG_IN_USE) == FLAG_IN_USE); } /*package*/ void markInUse() { flags |= FLAG_IN_USE; } /** * 构造器,一般来说不要直接通过构造器创建message,尽量用obtain方法 */ public Message() { }
public class Handler { private static final boolean FIND_POTENTIAL_LEAKS = false; private static final String TAG = "Handler"; //当前handler所在线程的looper final Looper mLooper; //跟looper对应的消息队列 final MessageQueue mQueue; //处理消息的回调 final Callback mCallback; //决定处理消息的方式同步的还是异步的 final boolean mAsynchronous; IMessenger mMessenger; /** * 通过设置这个接口去处理消息,就不需要再定义一个Handler的子类 */ public interface Callback { public boolean handleMessage(Message msg); } /** * Subclasses must implement this to receive messages. * Message和Handler都没有callback才会调用处理消息,这儿啥也没做,留着口给子类去完成了 */ public void handleMessage(Message msg) { } /** * 处理系统消息 * Handle system messages here. */ public void dispatchMessage(Message msg) { //如果msg有回调就交给msg处理 if (msg.callback != null) { handleCallback(msg); } else { //msg没有回调,创建Handler的时候有给回调就交给这个回调处理 if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } //调用自己的handleMessage方法处理,什么也没做,交给子类去具体实现,具体操作。平时我们用的匿名内部类的方式就是走了这一步 handleMessage(msg); } } public Handler() { this(null, false); } /** * 构造方法,设定消息处理回调,如果当前线程中没有Looper的时候,将接不到消息,还会抛出异常 */ public Handler(Callback callback) { this(callback, false); } /** * 构造方法,指定一个Looper,用这个Looper替换默认的Looper */ public Handler(Looper looper) { this(looper, null, false); } public Handler(Looper looper, Callback callback) { this(looper, callback, false); } /** * 设定当前Handler是同步处理消息还是异步处理消息 */ public Handler(boolean async) { this(null, async); } /** * 构造方法,初始化 */ public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } //获取一个Looper,UI线程系统自动调用prepareMainLooper方法,创建了UI线程的looper //如果Handler在子线程中创建,必须先调用prepare创建一个looper,否则myLooper返回的是null,会抛出异常 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } // mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } /** * 构造方法,初始化。跟2个参数的构造方法的区别是,这儿直接给定了looper */ public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } /** * 获取trace文件的名字,ANR分析的时候有用 */ public String getTraceName(Message message) { final StringBuilder sb = new StringBuilder(); sb.append(getClass().getName()).append(": "); if (message.callback != null) { sb.append(message.callback.getClass().getName()); } else { sb.append("#").append(message.what); } return sb.toString(); } /** * 获取消息的名称,用消息的callback类名或者what的十六进制命名 * * @param message The message whose name is being queried */ public String getMessageName(Message message) { if (message.callback != null) { //如果规定了message的回调,返回该回调类的名字 return message.callback.getClass().getName(); } //如果message没有指定回调,返回what的十六进制 return "0x" + Integer.toHexString(message.what); } /** * 从消息池中获取一个回收的message对象返回, * 但是这个message的处理handler是调用该方法的handler * 这样效率比直接new一个Message要好 * 如果不想处理消息的handler被指定为调用的handler可以调用Message.obtain方法 */ public final Message obtainMessage() { return Message.obtain(this); } /** * 跟上面的方法差不多,只是多了一点,指定了message的what变量值 */ public final Message obtainMessage(int what) { return Message.obtain(this, what); } public final Message obtainMessage(int what, Object obj) { return Message.obtain(this, what, obj); } public final Message obtainMessage(int what, int arg1, int arg2) { return Message.obtain(this, what, arg1, arg2); } public final Message obtainMessage(int what, int arg1, int arg2, Object obj) { return Message.obtain(this, what, arg1, arg2, obj); } /** * 将一个Runnable放到消息队列中,处理的时候是由当前handler依附的线程处理 */ public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } /** * 跟上面的方法一样,只是多了一个执行的时间指定,会在(uptimeMillis)这段时间过后才执行 */ public final boolean postAtTime(Runnable r, long uptimeMillis) { return sendMessageAtTime(getPostMessage(r), uptimeMillis); } public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) { return sendMessageAtTime(getPostMessage(r, token), uptimeMillis); } public final boolean postDelayed(Runnable r, long delayMillis) { return sendMessageDelayed(getPostMessage(r), delayMillis); } public final boolean postAtFrontOfQueue(Runnable r) { return sendMessageAtFrontOfQueue(getPostMessage(r)); } public final boolean runWithScissors(final Runnable r, long timeout) { if (r == null) { throw new IllegalArgumentException("runnable must not be null"); } if (timeout < 0) { throw new IllegalArgumentException("timeout must be non-negative"); } if (Looper.myLooper() == mLooper) { r.run(); return true; } BlockingRunnable br = new BlockingRunnable(r); return br.postAndWait(this, timeout); } /** * 移除消息队列中所有待处理的任务 */ public final void removeCallbacks(Runnable r) { mQueue.removeMessages(this, r, null); } /** * Remove any pending posts of Runnable <var>r</var> with Object * <var>token</var> that are in the message queue. If <var>token</var> is null, * all callbacks will be removed. */ public final void removeCallbacks(Runnable r, Object token) { mQueue.removeMessages(this, r, token); } /** * 将消息push到消息队列的队尾,轮到处理该消息的时候会回调handleMessage去处理 * 如果push成功返回true,如果这个消息队列已经exiting,将会push失败,返回false */ public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } /** * 将一个没有携带数据,只有what值的空消息push到消息队列中 */ public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); } /** * 将一个没有携带数据,只有what值的空消息push到消息队列中,但是会在特定的时间过后才能被delivered */ public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); } public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageAtTime(msg, uptimeMillis); } /** * 将一个消息放入队列,在当前时间+delayMillis(比如:一个小时之后处理,现在是12点,那就是12:00+1*60*60*1000) * 时间点之前应该要处理的消息全部处理完了之后,会在当前handler依附的线程中处理该消息。 * 这个消息将被传到handleMessage方法中 */ public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } /** * 将一个消息放入消息队列,在uptimeMillis(比如13:00)这个绝对时间点之前应该处理的消息处理完成之后会处理该消息 */ public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); } /** * 讲一个消息放在队首,这个方法一般不会使用,需要在特定的情况下使用。因为这个方法可能会导致 * 消息队列的排序出现问题,或者一些无法想象的异常出现 */ public final boolean sendMessageAtFrontOfQueue(Message msg) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, 0); } /** * 将消息放入队列中,在uptimeMillis(13:00)处理这个消息 * * @param queue 将消息放入这个消息队列 * @param msg 想要处理的消息 * @param uptimeMillis 处理的绝对时间点 * @return */ private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //指定处理该消息的handler为当前调用的handler msg.target = this; if (mAsynchronous) { //将消息设置为可异步 msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } /** * 从消息队列中移除指定what值的,未处理的消息.移除之后这些消息对象会被回收,将不会被取出执行 */ public final void removeMessages(int what) { mQueue.removeMessages(this, what, null); } /** * 从消息队列中移除指定what,和obj值,并且未处理的消息,移除之后这些消息对象会被回收,将不会被取出执行 */ public final void removeMessages(int what, Object object) { mQueue.removeMessages(this, what, object); } /** * 从消息队列中移除所有指定obj值,并且未处理的消息和任务,移除之后这些消息对象会被回收,将不会被取出执行 */ public final void removeCallbacksAndMessages(Object token) { mQueue.removeCallbacksAndMessages(this, token); } /** * 查询消息队列中是否有跟指定what值的消息 */ public final boolean hasMessages(int what) { return mQueue.hasMessages(this, what, null); } /** * Check if there are any pending posts of messages with code 'what' and * whose obj is 'object' in the message queue. */ public final boolean hasMessages(int what, Object object) { return mQueue.hasMessages(this, what, object); } /** * 查询消息队列中是否有指定任务 */ public final boolean hasCallbacks(Runnable r) { return mQueue.hasMessages(this, r, null); } // if we can get rid of this method, the handler need not remember its loop // we could instead export a getMessageQueue() method... public final Looper getLooper() { return mLooper; } public final void dump(Printer pw, String prefix) { pw.println(prefix + this + " @ " + SystemClock.uptimeMillis()); if (mLooper == null) { pw.println(prefix + "looper uninitialized"); } else { mLooper.dump(pw, prefix + " "); } } @Override public String toString() { return "Handler (" + getClass().getName() + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}"; }......}
Handler源码
public class Handler { private static final boolean FIND_POTENTIAL_LEAKS = false; private static final String TAG = "Handler"; //当前handler所在线程的looper final Looper mLooper; //跟looper对应的消息队列 final MessageQueue mQueue; //处理消息的回调 final Callback mCallback; //决定处理消息的方式同步的还是异步的 final boolean mAsynchronous; IMessenger mMessenger; /** * 通过设置这个接口去处理消息,就不需要再定义一个Handler的子类 */ public interface Callback { public boolean handleMessage(Message msg); } /** * Subclasses must implement this to receive messages. * Message和Handler都没有callback才会调用处理消息,这儿啥也没做,留着口给子类去完成了 */ public void handleMessage(Message msg) { } /** * 处理系统消息 * Handle system messages here. */ public void dispatchMessage(Message msg) { //如果msg有回调就交给msg处理 if (msg.callback != null) { handleCallback(msg); } else { //msg没有回调,创建Handler的时候有给回调就交给这个回调处理 if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } //调用自己的handleMessage方法处理,什么也没做,交给子类去具体实现,具体操作。平时我们用的匿名内部类的方式就是走了这一步 handleMessage(msg); } } public Handler() { this(null, false); } /** * 构造方法,设定消息处理回调,如果当前线程中没有Looper的时候,将接不到消息,还会抛出异常 */ public Handler(Callback callback) { this(callback, false); } /** * 构造方法,指定一个Looper,用这个Looper替换默认的Looper */ public Handler(Looper looper) { this(looper, null, false); } public Handler(Looper looper, Callback callback) { this(looper, callback, false); } /** * 设定当前Handler是同步处理消息还是异步处理消息 */ public Handler(boolean async) { this(null, async); } /** * 构造方法,初始化 */ public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } //获取一个Looper,UI线程系统自动调用prepareMainLooper方法,创建了UI线程的looper //如果Handler在子线程中创建,必须先调用prepare创建一个looper,否则myLooper返回的是null,会抛出异常 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } // mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } /** * 构造方法,初始化。跟2个参数的构造方法的区别是,这儿直接给定了looper */ public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } /** * 获取trace文件的名字,ANR分析的时候有用 */ public String getTraceName(Message message) { final StringBuilder sb = new StringBuilder(); sb.append(getClass().getName()).append(": "); if (message.callback != null) { sb.append(message.callback.getClass().getName()); } else { sb.append("#").append(message.what); } return sb.toString(); } /** * 获取消息的名称,用消息的callback类名或者what的十六进制命名 * * @param message The message whose name is being queried */ public String getMessageName(Message message) { if (message.callback != null) { //如果规定了message的回调,返回该回调类的名字 return message.callback.getClass().getName(); } //如果message没有指定回调,返回what的十六进制 return "0x" + Integer.toHexString(message.what); } /** * 从消息池中获取一个回收的message对象返回, * 但是这个message的处理handler是调用该方法的handler * 这样效率比直接new一个Message要好 * 如果不想处理消息的handler被指定为调用的handler可以调用Message.obtain方法 */ public final Message obtainMessage() { return Message.obtain(this); } /** * 跟上面的方法差不多,只是多了一点,指定了message的what变量值 */ public final Message obtainMessage(int what) { return Message.obtain(this, what); } public final Message obtainMessage(int what, Object obj) { return Message.obtain(this, what, obj); } public final Message obtainMessage(int what, int arg1, int arg2) { return Message.obtain(this, what, arg1, arg2); } public final Message obtainMessage(int what, int arg1, int arg2, Object obj) { return Message.obtain(this, what, arg1, arg2, obj); } /** * 将一个Runnable放到消息队列中,处理的时候是由当前handler依附的线程处理 */ public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } /** * 跟上面的方法一样,只是多了一个执行的时间指定,会在(uptimeMillis)这段时间过后才执行 */ public final boolean postAtTime(Runnable r, long uptimeMillis) { return sendMessageAtTime(getPostMessage(r), uptimeMillis); } public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) { return sendMessageAtTime(getPostMessage(r, token), uptimeMillis); } public final boolean postDelayed(Runnable r, long delayMillis) { return sendMessageDelayed(getPostMessage(r), delayMillis); } public final boolean postAtFrontOfQueue(Runnable r) { return sendMessageAtFrontOfQueue(getPostMessage(r)); } public final boolean runWithScissors(final Runnable r, long timeout) { if (r == null) { throw new IllegalArgumentException("runnable must not be null"); } if (timeout < 0) { throw new IllegalArgumentException("timeout must be non-negative"); } if (Looper.myLooper() == mLooper) { r.run(); return true; } BlockingRunnable br = new BlockingRunnable(r); return br.postAndWait(this, timeout); } /** * 移除消息队列中所有待处理的任务 */ public final void removeCallbacks(Runnable r) { mQueue.removeMessages(this, r, null); } /** * Remove any pending posts of Runnable <var>r</var> with Object * <var>token</var> that are in the message queue. If <var>token</var> is null, * all callbacks will be removed. */ public final void removeCallbacks(Runnable r, Object token) { mQueue.removeMessages(this, r, token); } /** * 将消息push到消息队列的队尾,轮到处理该消息的时候会回调handleMessage去处理 * 如果push成功返回true,如果这个消息队列已经exiting,将会push失败,返回false */ public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } /** * 将一个没有携带数据,只有what值的空消息push到消息队列中 */ public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); } /** * 将一个没有携带数据,只有what值的空消息push到消息队列中,但是会在特定的时间过后才能被delivered */ public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); } public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageAtTime(msg, uptimeMillis); } /** * 将一个消息放入队列,在当前时间+delayMillis(比如:一个小时之后处理,现在是12点,那就是12:00+1*60*60*1000) * 时间点之前应该要处理的消息全部处理完了之后,会在当前handler依附的线程中处理该消息。 * 这个消息将被传到handleMessage方法中 */ public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } /** * 将一个消息放入消息队列,在uptimeMillis(比如13:00)这个绝对时间点之前应该处理的消息处理完成之后会处理该消息 */ public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); } /** * 讲一个消息放在队首,这个方法一般不会使用,需要在特定的情况下使用。因为这个方法可能会导致 * 消息队列的排序出现问题,或者一些无法想象的异常出现 */ public final boolean sendMessageAtFrontOfQueue(Message msg) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, 0); } /** * 将消息放入队列中,在uptimeMillis(13:00)处理这个消息 * * @param queue 将消息放入这个消息队列 * @param msg 想要处理的消息 * @param uptimeMillis 处理的绝对时间点 * @return */ private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //指定处理该消息的handler为当前调用的handler msg.target = this; if (mAsynchronous) { //将消息设置为可异步 msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } /** * 从消息队列中移除指定what值的,未处理的消息.移除之后这些消息对象会被回收,将不会被取出执行 */ public final void removeMessages(int what) { mQueue.removeMessages(this, what, null); } /** * 从消息队列中移除指定what,和obj值,并且未处理的消息,移除之后这些消息对象会被回收,将不会被取出执行 */ public final void removeMessages(int what, Object object) { mQueue.removeMessages(this, what, object); } /** * 从消息队列中移除所有指定obj值,并且未处理的消息和任务,移除之后这些消息对象会被回收,将不会被取出执行 */ public final void removeCallbacksAndMessages(Object token) { mQueue.removeCallbacksAndMessages(this, token); } /** * 查询消息队列中是否有跟指定what值的消息 */ public final boolean hasMessages(int what) { return mQueue.hasMessages(this, what, null); } /** * Check if there are any pending posts of messages with code 'what' and * whose obj is 'object' in the message queue. */ public final boolean hasMessages(int what, Object object) { return mQueue.hasMessages(this, what, object); } /** * 查询消息队列中是否有指定任务 */ public final boolean hasCallbacks(Runnable r) { return mQueue.hasMessages(this, r, null); } // if we can get rid of this method, the handler need not remember its loop // we could instead export a getMessageQueue() method... public final Looper getLooper() { return mLooper; } public final void dump(Printer pw, String prefix) { pw.println(prefix + this + " @ " + SystemClock.uptimeMillis()); if (mLooper == null) { pw.println(prefix + "looper uninitialized"); } else { mLooper.dump(pw, prefix + " "); } } @Override public String toString() { return "Handler (" + getClass().getName() + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}"; }......}
HandlerThread
Android API提供了HandlerThread来创建线程。官网的解释是:Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.
HandlerThread不但能提供异步处理,Handler处理消息的方法也会在这个线程中执行,他最重要的作用就是提供了一个线程。简单说就是 消息队列+消息循环。
那么这个Handler对象就是与HandlerThread这个线程绑定了(这时就不再是与UI线程绑定了,这样它处理耗时操作将不会阻塞UI)。
注意:
handler在哪个线程中new,就在哪个线程;
主线程属于handlerthread;
当我们需要一个工作者线程,而不是把它当作一次性消耗品,用过即废弃的话,就可以使用HandlerThread。
public class HandlerThreadActivity extends Activity{ private final String TAG = "HandlerThreadActivity"; @BindView(R.id.btn) Button mBtn; private Handler mHandler; private HandlerThread mHandlerThread; private boolean mRunning; private int mCount; private Runnable mRunnable = new Runnable() { @Override public void run() { while(mRunning){ Log.d(TAG, "test HandlerThread..." + mCount++); try{ Thread.sleep(2000); }catch(Exception e){ e.printStackTrace(); } } } }; @OnClick(R.id.btn) public void btnClick(){ mHandler.postDelayed(mRunnable, 4000); //4s后开始,每隔2s中打印1次 } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handlerthread); ButterKnife.bind(this, this); mHandlerThread = new HandlerThread("HandlerThread", Thread.NORM_PRIORITY); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); } @Override protected void onResume() { mRunning = true; super.onResume(); } @Override protected void onStop() { mRunning = false; super.onStop(); } @Override protected void onDestroy() { mRunning = false; mHandler.removeCallbacks(mRunnable); super.onDestroy(); }}
IntentService
默认的 Service 是执行在主线程的,可是通常情况下,这很容易影响到程序的绘制性能(抢占了主线程的资源)。除了前面介绍过的HandlerThread,我们还可以选择使用 IntentService 来实现异步操作。
IntentService 继承自普通 Service 同时又在内部创建了一个 HandlerThread,在 onHandlerIntent()的回调里面处理扔到 IntentService 的任务。
所以 IntentService 就不仅仅具备了异步线程的特性,还同时保留了 Service 不受主页面生命周期影响的特点。
使用 IntentService 需要特别留意以下几点:
首先,因为 IntentService 内置的是 HandlerThread 作为异步线程,所以每一个交给 IntentService 的任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。
其次,通常使用到 IntentService 的时候,我们会结合使用 BroadcastReceiver 把工作线程的任务执行结果返回给主 UI 线程。使用广播容易引起性能问题,我们可以使用 LocalBroadcastManager 来发送只在程序内部传递的广播,从而提升广播的性能。我们也可以使用 runOnUiThread() 快速回调到主 UI 线程。
最后,包含正在运行的 IntentService 的程序相比起纯粹的后台程序更不容易被系统杀死,该程序的优先级是介于前台程序与纯后台程序之间的。
public class IntentServiceActivity extends Activity { @BindView(R.id.intent_service) TextView mIntentService; @OnClick(R.id.intent_service) public void onClick(View view){ Intent intent = new Intent(IntentServiceActivity.this, MyIntentService.class); intent.putExtra(MyIntentService.TAG, "Hello Jack"); startService(intent); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_intentservice); ButterKnife.bind(this); }}
public class MyIntentService extends IntentService { public final static String TAG = "MyIntentService"; public MyIntentService(){ super(TAG); } @Override protected void onHandleIntent(Intent intent) { String data = intent.getStringExtra(TAG); try{ Thread.sleep(3000); //模拟耗时操作 }catch(Exception e){ e.printStackTrace(); } Log.d(TAG, "data----" + data); Looper.getMainLooper(); Toast.makeText(getApplicationContext(), "get data:" + data, Toast.LENGTH_LONG).show(); Looper.loop(); }}
The Importance of Thread Priority
Android 系统会根据当前运行的可见的程序和不可见的后台程序对线程进行归类,划分为:
foreground 的那部分线程会大致占用掉 CPU 的90%左右的时间片,background 的那部分线程就总共只能分享到5%-10%左右的时间片。
之所以设计成这样是因为 foreground 的程序本身的优先级就更高,理应得到更多的执行时间。
在 Android 系统里面,我们可以通过 android.os.Process.setThreadPriority(int) 设置线程的优先级,参数范围从-20到19,数值越小优先级越高。Android 系统还为我们提供了以下的一些预设值,我们可以通过给不同的工作线程设置不同数值的优先级来达到更细粒度的控制。
大多数情况下,新创建的线程优先级会被设置为默认的0,主线程设置为0的时候,新创建的线程还可以利用 THREAD_PRIORITY_LESS_FAVORABLE 或者 THREAD_PRIORITY_MORE_FAVORABLE 来控制线程的优先级。
线程池
- 一、线程池的思想:开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
- 二、使用线程池的好处
重用已经创建的好的线程,避免频繁创建进而导致的频繁GC;
控制线程并发数,合理使用系统资源,提高应用性能;
可以有效的控制线程的执行,比如定时执行,取消执行等
使用线程池需要特别注意同时并发线程数量的控制,理论上来说,我们可以设置任意你想要的并发数量,但是这样做非常的不好。因为 CPU 只能同时执行固定数量的线程数,一旦同时并发的线程数量超过 CPU 能够同时执行的阈值,CPU 就需要花费精力来判断到底哪些线程的优先级比较高,需要在不同的线程之间进行调度切换。
一旦同时并发的线程数量达到一定的量级,这个时候 CPU 在不同线程之间进行调度的时间就可能过长,反而导致性能严重下降。
另外需要关注的一点是,每开一个新的线程,都会耗费至少 64K+ 的内存。
为了能够方便的对线程数量进行控制,ThreadPoolExecutor 为我们提供了初始化的并发线程数量,以及最大的并发数量进行设置。
一个线程池包括以下4个基本组成部分:
- 1、 ThreadPool(线程池管理器):用于创建并管理线程池,包括: 创建线程池,销毁线程池,添加新任务;
- 2、 PoolWorker(工作线程):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
- 3、 Task(任务接口):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
- 4、 TaskQueue(任务队列):用于存放没有处理的任务。提供一种缓冲机制。
Android中的线程池其实源于Java,Java中和线程有关的东东叫做Executor,Executor本身是一个接口,这个接口有一个非常有用的实现类叫做ThreadPoolExecutor。
ThreadPoolExecutor.java
这里是7个参数(我们在开发中用的更多的是5个参数的构造方法)
1、corePoolSize :线程池中核心线程的数量
2、maximumPoolSize : 线程池中最大线程数量
3、keepAliveTime : 非核心线程的超时时长,当系统中非核心线程闲置时间超过
keepAliveTime之后,则会被回收。
如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,
则该参数也表示核心线程的超时时长。
4、unit :第3个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等。
5、workQueue :线程池中的任务队列,该队列主要用来存储已经被提交但是尚未执行的
任务。存储在这里的任务是由ThreadPoolExecutor的execute方法提交来的。
6、threadFactory :为线程池提供创建新线程的功能,这个我们一般使用默认即可。
7、handler : 拒绝策略,当线程无法执行新任务时(一般是由于线程池中的线程数量已
经达到最大数或者线程池关闭导致的)。默认情况下,当线程池无法处理新线程时,会抛
出一个RejectedExecutionException。
public class ThreadPoolExecutorActivity extends Activity { private final String TAG = "ThreadPoolExecutorActivity"; private ThreadPoolExecutor mThreadPoolExecutor; @BindView(R.id.text) TextView mTextView; @BindView(R.id.button) Button mButton; @OnClick(R.id.button) void onClick(View view){ switch (view.getId()){ case R.id.button: for(int i = 0; i < 30; i++){ final int finalI = i; Runnable runnable = new Runnable() { @Override public void run() { SystemClock.sleep(2000); System.out.println(TAG + "----->" + finalI); } }; mThreadPoolExecutor.execute(runnable); } break; default: break; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_thread_pool); ButterKnife.bind(this); initExecutor(); } private void initExecutor(){ int corePoolSize = 3; int maximumPoolSize = 5; //todo 30 long keepAliveTime = 1; TimeUnit unit = TimeUnit.SECONDS; BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(128); //todo 6 mThreadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); }}
* 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(); * } * } * }}
BlockingQueue的实现
在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利。本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景。
认识BlockingQueue
阻塞队列,顾名思义,首先它是一个队列,而一个队列在数据结构中所起的作用大致如下图所示:
从上图我们可以很清楚看到,通过一个共享的队列,可以使得数据由队列的一端输入,从另外一端输出;
常用的队列主要有以下两种:(当然通过不同的实现方式,还可以延伸出很多不同类型的队列,DelayQueue就是其中的一种)
先进先出(FIFO):先插入的队列的元素也最先出队列,类似于排队的功能。从某种程度上来说这种队列也体现了一种公平性。
后进先出(LIFO):后插入队列的元素最先出队列,这种队列优先处理最近发生的事件。
多线程环境中,通过队列可以很容易实现数据共享,比如经典的“生产者”和“消费者”模型中,通过队列可以很便利地实现两者之间的数据共享。假设我们有若干生产者线程,另外又有若干个消费者线程。如果生产者线程需要把准备好的数据共享给消费者线程,利用队列的方式来传递数据,就可以很方便地解决他们之间的数据共享问题。但如果生产者和消费者在某个时间段内,万一发生数据处理速度不匹配的情况呢?理想情况下,如果生产者产出数据的速度大于消费者消费的速度,并且当生产出来的数据累积到一定程度的时候,那么生产者必须暂停等待一下(阻塞生产者线程),以便等待消费者线程把累积的数据处理完毕,反之亦然。然而,在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。好在此时,强大的concurrent包横空出世了,而他也给我们带来了强大的BlockingQueue。(在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒)
下面两幅图演示了BlockingQueue的两个常见阻塞场景:
如上图所示:当队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞(挂起),直到有数据放入队列。
如上图所示:当队列中填满数据的情况下,生产者端的所有线程都会被自动阻塞(挂起),直到队列中有空的位置,线程被自动唤醒。
这也是我们在多线程环境下,为什么需要BlockingQueue的原因。作为BlockingQueue的使用者,我们再也不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一手包办了。既然BlockingQueue如此神通广大,让我们一起来见识下它的常用方法:
BlockingQueue的核心方法:
放入数据:
offer(anObject):表示如果可能的话,将anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false.(本方法不阻塞当前执行方法的线程)
offer(E o, long timeout, TimeUnit unit),可以设定等待的时间,如果在指定的时间内,还不能往队列中加入BlockingQueue,则返回失败。
put(anObject):把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续.
获取数据:
poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null;
poll(long timeout, TimeUnit unit):从BlockingQueue取出一个队首的对象,如果在指定时间内,队列一旦有数据可取,则立即返回队列中的数据。否则知道时间超时还没有数据可取,返回失败。
take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到 BlockingQueue有新的数据被加入;
drainTo():一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数), 通过该方法,可以提升获取数据效率;不需要多次分批加锁或释放锁。
常见BlockingQueue
重点内容
- ArrayBlockingQueue:这个表示一个规定了大小的BlockingQueue,ABQ的构造函数接受一个int类型的数据,该数据表示BlockingQueue的大小,存储在ABQ中的元素按照FIFO(先进先出)的方式来进行存取。
基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象,这是一个常用的阻塞队列,除了一个定长数组外,ArrayBlockingQueue内部还保存着两个整形变量,分别标识着队列的头部和尾部在数组中的位置。
ArrayBlockingQueue在生产者放入数据和消费者获取数据,都是共用同一个锁对象,由此也意味着两者无法真正并行运行,这点尤其不同于LinkedBlockingQueue;按照实现原理来分析,ArrayBlockingQueue完全可以采用分离锁,从而实现生产者和消费者操作的完全并行运行。Doug Lea之所以没这样去做,也许是因为ArrayBlockingQueue的数据写入和获取操作已经足够轻巧,以至于引入独立的锁机制,除了给代码带来额外的复杂性外,其在性能上完全占不到任何便宜。 ArrayBlockingQueue和LinkedBlockingQueue间还有一个明显的不同之处在于,前者在插入或删除元素时不会产生或销毁任何额外的对象实例,而后者则会生成一个额外的Node对象。这在长时间内需要高效并发地处理大批量数据的系统中,其对于GC的影响还是存在一定的区别。而在创建ArrayBlockingQueue时,我们还可以控制对象的内部锁是否采用公平锁,默认采用非公平锁。
- LinkedBlockingQueue:这个表示一个大小不确定的BlockingQueue,在LBQ的构造方法中可以传一个int类型的数据,这样创建出来的LBQ是有大小的;也可以不传,不传的话, LBQ的大小就为Integer.MAX_VALUE。
基于链表的阻塞队列,同ArrayListBlockingQueue类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。而LinkedBlockingQueue之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。
作为开发者,我们需要注意的是,如果构造一个LinkedBlockingQueue对象,而没有指定其容量大小,LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.MAX_VALUE),这样的话,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了。
public class BlockingQueueTest { public static void main(String[] args) throws InterruptedException { // 声明一个容量为10的缓存队列 BlockingQueue<String> queue = new LinkedBlockingQueue<String>(10); Producer producer1 = new Producer(queue); Producer producer2 = new Producer(queue); Producer producer3 = new Producer(queue); Consumer consumer = new Consumer(queue); // 借助Executors ExecutorService service = Executors.newCachedThreadPool(); // 启动线程 service.execute(producer1); service.execute(producer2); service.execute(producer3); service.execute(consumer); // 执行10s Thread.sleep(10 * 1000); producer1.stop(); producer2.stop(); producer3.stop(); Thread.sleep(2000); // 退出Executor service.shutdown(); }}
/** * 消费者线程 * */public class Consumer implements Runnable { public Consumer(BlockingQueue<String> queue) { this.queue = queue; } public void run() { System.out.println("启动消费者线程!"); Random r = new Random(); boolean isRunning = true; try { while (isRunning) { System.out.println("正从队列获取数据..."); String data = queue.poll(2, TimeUnit.SECONDS); if (null != data) { System.out.println("拿到数据:" + data); System.out.println("正在消费数据:" + data); Thread.sleep(r.nextInt(DEFAULT_RANGE_FOR_SLEEP)); } else { // 超过2s还没数据,认为所有生产线程都已经退出,自动退出消费线程。 isRunning = false; } } } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } finally { System.out.println("退出消费者线程!"); } } private BlockingQueue<String> queue; private static final int DEFAULT_RANGE_FOR_SLEEP = 1000;}/** * 生产者线程 * */public class Producer implements Runnable { public Producer(BlockingQueue queue) { this.queue = queue; } public void run() { String data = null; Random r = new Random(); System.out.println("启动生产者线程!"); try { while (isRunning) { System.out.println("正在生产数据..."); Thread.sleep(r.nextInt(DEFAULT_RANGE_FOR_SLEEP)); data = "data:" + count.incrementAndGet(); System.out.println("将数据:" + data + "放入队列..."); if (!queue.offer(data, 2, TimeUnit.SECONDS)) { System.out.println("放入数据失败:" + data); } } } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } finally { System.out.println("退出生产者线程!"); } } public void stop() { isRunning = false; } private volatile boolean isRunning = true; private BlockingQueue queue; private static AtomicInteger count = new AtomicInteger(); private static final int DEFAULT_RANGE_FOR_SLEEP = 1000;}
ArrayBlockingQueue和LinkedBlockingQueue是两个最普通也是最常用的阻塞队列,一般情况下,在处理多线程间的生产者消费者问题,使用这两个类足以。
- PriorityBlockingQueue:这个队列和LBQ类似,不同的是PBQ中的元素不是按照FIFO来排序的,而是按照元素的Comparator来决定存取顺序的(这个功能也反映了存入PBQ中的数据必须实现了Comparator接口)。
基于优先级的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定),但需要注意的是PriorityBlockingQueue并不会阻塞数据生产者,而只会在没有可消费的数据时,阻塞数据的消费者。因此使用的时候要特别注意,生产者生产数据的速度绝对不能快于消费者消费数据的速度,否则时间一长,会最终耗尽所有的可用堆内存空间。在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁。
一种无缓冲的等待队列,类似于无中介的直接交易,有点像原始社会中的生产者和消费者,生产者拿着产品去集市销售给产品的最终消费者,而消费者必须亲自去集市找到所要商品的直接生产者,如果一方没有找到合适的目标,那么对不起,大家都在集市等待。相对于有缓冲的BlockingQueue来说,少了一个中间经销商的环节(缓冲区),如果有经销商,生产者直接把产品批发给经销商,而无需在意经销商最终会将这些产品卖给那些消费者,由于经销商可以库存一部分商品,因此相对于直接交易模式,总体来说采用中间经销商的模式会吞吐量高一些(可以批量买卖);但另一方面,又因为经销商的引入,使得产品从生产者到消费者中间增加了额外的交易环节,单个产品的及时响应性能可能会降低。
声明一个SynchronousQueue有两种不同的方式,它们之间有着不太一样的行为。公平模式和非公平模式的区别:
如果采用公平模式:SynchronousQueue会采用公平锁,并配合一个FIFO队列来阻塞多余的生产者和消费者,从而体系整体的公平策略;
但如果是非公平模式(SynchronousQueue默认):SynchronousQueue采用非公平锁,同时配合一个LIFO队列来管理多余的生产者和消费者,而后一种模式,如果生产者和消费者的处理速度有差距,则很容易出现饥渴的情况,即可能有某些生产者或者是消费者的数据永远都得不到处理。
Executors.java的常用方法
其实是创建不同类型的ThreadPoolExecutor对象
1、newFixedThreadPool方法
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。
如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。
如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。
Eg: ExecutorService pool = Executors.newFixedThreadPool(2);
2、newSingleThreadExecutor方法
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
注意:如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么如果需要,一个新线程将代替它执行后续的任务。
可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。
与其等效的 newFixedThreadPool(1) 不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。
Eg: ExecutorService pool = Executors.newSingleThreadExecutor();
3、newCachedThreadPool方法
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的 程序而言,这个线程池通常可提高程序性能。
调用execute,将重用以前构造的线程(如果线程可用)。
如果现有线程没有可用的,则创建一个新线程并添加池中。终 止并从缓存中移除那些已有 60s未被使用的线程。
因此,长时间保持空闲的线程池不会使用任何资源。
注意:可以使用 ThreadPoolExecutor 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。
Eg: ExecutorService pool = Executors.newCachedThreadPool();
4.1 FixedThreadPool是一个核心线程数量固定的线程池,创建方式如下:
/** * 打印内容 * @param service */ private void print(final String name, ExecutorService service){ for(int i = 0; i < 30; i++){ final int TEMP = i; Runnable runnable = new Runnable() { @Override public void run() { System.out.println(name + "------->" + TEMP); } }; service.execute(runnable); } } @Test public void testFixThreadPool(){ ExecutorService service = Executors.newFixedThreadPool(10); print("testFixThreadPool", service); } @Test public void testSingleThreadExecutor(){ ExecutorService service = Executors.newSingleThreadExecutor(); print("testSingleThreadExecutor", service); } @Test public void testCachedThreadPool(){ ExecutorService service = Executors.newCachedThreadPool(); print("testCachedThreadPool", service); }
4.2 SingleThreadExecutor
使用SingleThreadExecutor的一个最大好处就是:可以避免我们去处理线程同步问题。
其实如果我们把FixedThreadPool的参数传个1,效果不就和SingleThreadExecutor一致了。
4.3 CachedThreadPool
CachedTreadPool一个最大的优势是它可以根据程序的运行情况自动来调整线程池中的线程数量,CachedThreadPool源码如下:
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());}
4.4 ScheduledThreadPool
ScheduledThreadPool是一个具有定时定期执行任务功能的线程池,源码如下:
public class ScheduledExecutorServiceActivity extends Activity { private ScheduledExecutorService mScheduledExecutorService; @BindView(R.id.testScheduledThreadPool) TextView mTestScheduledThreadPool; @BindView(R.id.testScheduleAtFixedRate) TextView mTestScheduleAtFixedRate; @BindView(R.id.testScheduleWithFixedDelay) TextView mTestScheduleWithFixedDelay; @OnClick({R.id.testScheduledThreadPool, R.id.testScheduleAtFixedRate, R.id.testScheduleWithFixedDelay}) public void onClick(View view){ switch(view.getId()){ case R.id.testScheduledThreadPool: testScheduledThreadPool(); break; case R.id.testScheduleAtFixedRate: testScheduleAtFixedRate(); break; case R.id.testScheduleWithFixedDelay: testScheduleWithFixedDelay(); break; default: break; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_schedule_executor_service); ButterKnife.bind(this); testScheduledThreadPool(); } public void testScheduledThreadPool(){ mScheduledExecutorService = Executors.newScheduledThreadPool(10); Runnable runnable = new Runnable() { @Override public void run() { System.out.println("testScheduledPThreadPool run------"); } }; mScheduledExecutorService.schedule(runnable, 3, TimeUnit.SECONDS); } public void testScheduleAtFixedRate(){ mScheduledExecutorService = Executors.newScheduledThreadPool(3); Runnable runnable = new Runnable() { @Override public void run() { System.out.println("testScheduleAtFixedRate run------"); } }; mScheduledExecutorService.scheduleAtFixedRate(runnable, 5, 3, TimeUnit.SECONDS); } public void testScheduleWithFixedDelay(){ mScheduledExecutorService = Executors.newScheduledThreadPool(3); Runnable runnable = new Runnable() { @Override public void run() { System.out.println("testScheduleWithFixedDelay run------"); } }; mScheduledExecutorService.scheduleWithFixedDelay(runnable, 5, 3, TimeUnit.SECONDS); } @Override protected void onDestroy() { super.onDestroy(); if(mScheduledExecutorService != null && !mScheduledExecutorService.isShutdown()){ mScheduledExecutorService.shutdownNow(); } }}
execute方法
这是ThreadPoolExecutor的构造方法参数的解释,我们的线程提交到线程池之后又是按照什么样的规则去运行呢?它们遵循如下规则:
execute一个线程之后,如果线程池中的线程数未达到核心线程数,则会立马启用一个核心线程去执行;
execute一个线程之后,如果线程池中的线程数已经达到核心线程数,且workQueue未满,则将新线程放入workQueue中等待执行;
execute一个线程之后,如果线程池中的线程数已经达到核心线程数,且workQueue已满,但未超过非核心线程数,则开启一个非核心线程来执行任务;
execute一个线程之后,如果线程池中的线程数已经超过非核心线程数,则拒绝执行该任务;
线程池其它常用方法
shutDown() 关闭线程池,不影响已经提交的任务
shutDownNow() 关闭线程池,并尝试去终止正在执行的线程
allowCoreThreadTimeOut(boolean value) 允许核心线程闲置超时时被回收
submit() 一般情况下我们使用execute来提交任务,但是有时候可能也会用到submit,使用submit的好处是submit有返回值,它会返回一个Fultrue对象来观测线程执行的状态和结果。
/** * Submits a value-returning task for execution and returns a * Future representing the pending results of the task. The * Future's {@code get} method will return the task's result upon * successful completion. * * <p> * If you would like to immediately block waiting * for a task, you can use constructions of the form * {@code result = exec.submit(aCallable).get();} * * <p>Note: The {@link Executors} class includes a set of methods * that can convert some other common closure-like objects, * for example, {@link java.security.PrivilegedAction} to * {@link Callable} form so they can be submitted. * * @param task the task to submit * @param <T> the type of the task's result * @return a Future representing pending completion of the task * @throws RejectedExecutionException if the task cannot be * scheduled for execution * @throws NullPointerException if the task is null */ <T> Future<T> submit(Callable<T> task);
自定义线程池
除了使用submit来定义线程池获取线程执行结果之外,我们也可以通过自定义ThreadPoolExecutor来实现这个功能。
@RunWith(AndroidJUnit4.class)public class CustomThreadPoolTest { private final String TAG = "CustomThreadPoolTest"; @Test public void customThreadPool(){ final MyThreadPool myThreadPool = new MyThreadPool(3, 5, 1, TimeUnit.MINUTES, new LinkedBlockingDeque<Runnable>(){}); for(int i = 0; i < 10; i++){ final int finalI = i; Runnable runnable = new Runnable() { @Override public void run() { SystemClock.sleep(100); Log.d(TAG, "------run:" + finalI); } }; myThreadPool.execute(runnable); } } class MyThreadPool extends ThreadPoolExecutor{ public MyThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue){ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); } @Override protected void beforeExecute(Thread t, Runnable r) { super.beforeExecute(t, r); Log.d(TAG, "------beforeExecute"); } @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); Log.d(TAG, "------afterExecute"); } @Override protected void terminated() { super.terminated(); //当调用shutDown()或者shutDownNow()时调用 Log.d(TAG, "------terminated"); } }}
线程池的配置(参看AsyncTask)
- Android 多线程
- Android多线程
- Android 多线程
- Android 多线程
- Android 多线程
- Android多线程
- android 多线程
- android多线程
- android 多线程
- Android多线程
- android 多线程
- android多线程
- Android多线程
- android 多线程
- Android多线程
- Android 多线程
- android 多线程
- Android多线程
- Jenkins + SVN + Cocoapods 实现iOS自动化打包
- 非Boot节点Eureka服务单向调用,环境隔离
- HNCU 1741: 算法3-2:行编辑程序
- 详解c++中类的六个默认的成员函数
- iOS 转让APP,变更开发商名字 —— HERO博客
- Android多线程
- 802.11无线网络学习(三):802.11 MAC基础
- Android之MappedByteBuffer缓冲用法
- 5-3 A-B (20分)
- java实现在线预览---poi操作ppt转html及03、07版本兼容问题
- MAC安装Securecrt破解
- css画图案
- SimPHP框架介绍
- app接口安全性设计浅析