Android进程/线程管理——深入源码解读+分析

来源:互联网 发布:ppt软件有哪些 编辑:程序博客网 时间:2024/06/04 18:27

一. 为什么要了解进程/线程

从操作系统的角度,进程是操作系统管理的一个实体,操作系统为其定义了虚拟地址空间,进程之间的数据调度等。从应用开发的角度,我们写的程序就必须运行在一个进程实例中。同时对于进程,如果采用了多线程方法,也涉及到多线程的管理。所以学习并掌握一个平台,了解进程/线程是十分基础且必须的一件事情。


二 . 进程

一般来说,一个程序包对应于一个进程。(当然,这也不是不可改变的,我们可以通过NDK提供的本地接口,来fork子进程。)多个Activity共享一个主线程(ActivityThread),Service也是寄存于ActivityThread中的。Activity和Service一样,他们启动是都需要两个Binder线程的支持。


三. 线程

对于一个Activity必定会包含一个main thread,此外还有可能有一些binder thread。同时Zygote系统进程负责为每个新启动的应用fork新的进程。此外,Zygote为Activity创建的主线程是ActivityThread。


四. Handler,MessageQueue,Looper,ActivityThread, Thread

Looper不断从MessageQueue中获取Message(或者是Runnable),然后交由Handler处理。

Handler。(frameworks/base/core/java/android/os/Handler.java)

使用示例:(在主线程中创建一个Handler,在子线程中发送Message。后面有在子线程中使用Handler的例子)

定义handler

    handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case 0:popupWindowLogo.showAtLocation(logoPop,Gravity.CENTER_HORIZONTAL, 10, 60);break;case 1:popupWindowWelcome.showAtLocation(welcomePop,Gravity.CENTER_HORIZONTAL, 10, 100);break;case 2:if (null != popupWindowLogo && popupWindowLogo.isShowing())popupWindowLogo.dismiss();if (null != popupWindowWelcome&& popupWindowWelcome.isShowing())popupWindowWelcome.dismiss();break;case 3:initUserInfo();default:break;}}};

定义Thread

    class PauseRun implements Runnable {@Overridepublic void run() {try {Thread.sleep(1000);handler.sendEmptyMessage(0);Thread.sleep(1000);handler.sendEmptyMessage(1);Thread.sleep(1000);handler.sendEmptyMessage(2);Thread.sleep(1000);handler.sendEmptyMessage(3);} catch (InterruptedException e) {e.printStackTrace();}}}

分析如下:

1)Handler将消息压入MessageQueue中。以上示例使用的是sendEmptyMessage()函数,除此之外,send系的函数还有sendMessageAtFrontOfQueue(Message msg); sendMessageAtTime(Message msg, long uptimeMillis); sendMessageDelayed(Message msg, long delayMillis);等。对应的还有post系的函数,包括post(Runnable r), postAtTime(Runnable r, long uptimeMillis)。

send和post二者主要的区别在于,send传递的直接是Message,而post传递的是其他类型的消息,如Runnable。post会先将该信息转换为Message,然后在通过send系的函数(先调用sendMessageDelayed,再调用sendMessageAtTime将消息压入MessageQueue)。

2)Thread和Handler的关系。从以上示例中,可以看出一个Thread可以对应于多个Handler。同样的,也可以从Thread类和Handler类之间的关系中看出

Looper{    MessageQueue mQueue;    Thread mThread;}Handler{    MessageQueue mQueue;    Looper mLooper;    Callback mCallback;}MessageQueue{    Message mMessages;}Message{    Handler target;}

3)我们在Thread中不能直接操作UI元素,只能在Handler中进行处理。所以Handler也给我们提供了一种在多线程环境下操作UI元素的方式。


MessageQueue。(frameworks/base/core/java/android/os)

MessageQueue主要负责维护消息队列,其中提供了对该队列的各种操作:新建(构造函数+nativeInit()),元素入队(enqueueMessage(Message msg, long when)),元素出队(next()),删除元素(removeMessage(Handler h, int what, Object object)),销毁队列(removeMessage(Handler h, Runnable r, Object object))。


Looper。(frameworks/base/core/java/android/os)

Looper.prepare()源码如下:

    public static void prepare() {        prepare(true);//true表示可以退出; ActivityThread中调用的是perpareMainLooper(), 其中会调用perpare(false)。说明ActivityThread的loop不能退出。    }    private static void prepare(boolean quitAllowed) {        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper(quitAllowed));    }
    private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }

注:其中sThreadLocal的定义如下:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

ThreadLocal可以为每个线程维护变量,并使其成为该线程相关的独立副本。所以Looper利用ThreadLocal使得每个线程拥有彼此独立的Looper实例。


Looper.loop()源码如下:

    public static void loop() {        final Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        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.        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            Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            msg.target.dispatchMessage(msg);            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.recycle();        }    }

其中myLooper()用来获取在prepare()中建立的本线程相关的Looper实例:

    public static Looper myLooper() {        return sThreadLocal.get();    }

Handler类中存在下面几个成员变量:

Handler{    final MessageQueue mQueue;     final Looper mLooper;    final Callback mCallback;}
我们在建立Handler实例的时候,就已经给这几个变量赋值了:

    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());            }        }        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;    }
所以,通过如下代码,就能使Looper, MessageQueue, Handler联系起来:(一般开发人员也是这么在用)

 class LooperThread extends Thread {        public Handler mHandler;          public void run() {            Looper.prepare();              mHandler = new Handler() {                public void handleMessage(Message msg) {                    // process incoming messages here                }            };              Looper.loop();        }    }

ActivityThread。(frameworks/base/core/java/android/app/ActivityThread.java)

ActivityThread是应用程序的主线程。在该主线程中主要的工作如下:

    public static void main(String[] args) {        SamplingProfilerIntegration.start();        // CloseGuard defaults to true and can be quite spammy.  We        // disable it here, but selectively enable it later (via        // StrictMode) on debug builds, but using DropBox, not logs.        CloseGuard.setEnabled(false);        Environment.initForCurrentUser();        // Set the reporter for event logging in libcore        EventLogger.setReporter(new EventLoggingReporter());        Security.addProvider(new AndroidKeyStoreProvider());        Process.setArgV0("<pre-initialized>");        Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        AsyncTask.init();        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }

其中prepareMainLooper()会建立主线程相关的Looper以及MessageQueue。

    public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        }    }


对于主线程来说,其Looper是可以全局访问的(其他线程也可以访问):在Looper类中定义了指向主线程Looper的sMainLooper对象引用。

    private static Looper sMainLooper;  // guarded by Looper.class
其他线程通过调用getMainLooper()函数即可访问:
    public static Looper getMainLooper() {        synchronized (Looper.class) {            return sMainLooper;        }    }

所以,当前进程中的各个线程都可以访问主线程的消息队列。

Thread。(/*libcore/libdvm/src/main/java/java/lang/Thread.java*/)

Java中多线程采用抢占式调度。(另一种调度模式是分时调度:分时会平分CPU时间,抢占则根据优先级分配CPU时间)

synchronized。该关键字主要用于java多线程中同步,它可以保证只有一个线程执行其修饰范围内的部分。作用范围可以是一个代码块,一个方法,一个对象,一个类的字面常量(如 Person.class)(普通变量不得行)。

  • 当其修饰一个对象时(synchronized(this)),只能有一个线程能执行synchronized修饰的同步代码块,其他线程须等到该线程执行完成后,才能进入并执行。对于类中的其他非同步代码块,其他线程可以访问。
  • (接上)对于其他的同步代码,其他线程对其访问将处于阻塞状态。因为synchronized(this)修饰该对象,也就是说当前运行的线程获取了该对象的对象锁。其他所有线程只有得到了该对象锁才能访问该对象中的方法。
  • synchronized方法块的出现是为了避免synchronized修饰一个大的方法而引发的程序运行效率降低。
  • synchronized修饰对象实例和类的字面常量是不同的。后者范围更大,对于所有该类的实例都会产生同步效果,即,所有该类的实例共享一个对象锁。而前者的各个对象实例的对象锁是分离的。注:类的静态(static)synchronized函数也可以达到和类字面常量同样的效果,即,取得该类的全局锁。然而,这两种锁并不具有包含关系,他们只是作用的范围不一样,当两种同步语句作用于统一各类的不同方法时,多线程中是可以同时访问这两个方法的,即,并不具有同步关系。
//synchronized方法public synchronized void accessVal(int newVal);  //synchronized方法块synchronized(syncObject) {      //允许访问控制的代码  }

每个对象实例或者类都有一个锁。Synchronized关键字将某些方法,变量保护起来,防止多线程访问冲突,以免造成不可预期的程序错误。一个类(对象)可以比喻成一个房间,类(对象)中的方法好比是抽屉。synchronized方法是带锁的抽屉,所有的抽屉共用一把钥匙,且在房间门口放着,只有获得了对象锁才能访问该抽屉。所以对于想要访问带锁抽屉的人(线程)必须要先获取到门口的钥匙,等到访问抽屉结束,则要将钥匙归还,从而后面的人(线程)才可以继续访问带锁的抽屉。而非synchronized方法是不带锁的抽屉,所以大家都可以访问。

说到多线程,不得不说多线程协同工作的方法,wait(), notity(), notifyAll(), interrupt(), join(), sleep()等方法,是java中的多线程控制(同步)与调度方法,下面对他们进行详细的分析。

wait()

该方法是Object类的成员方法。也就是说java中所有的对象都可以调用此方法,类似于没有对象都对应于一个对象锁。所以每当我们谈论wait()方法都必须要跟对象锁挂钩。该线程进入等待状态后会释放该对象锁。

notify()/notifyAll()

该方法相对于wait()方法。使得等待状态的线程可以重新运行。值得注意的是,notify()方法是随机任意的选择一个在等待该object的线程来唤醒,具体看虚拟机的实现方式。而notifyAll()则是唤醒等待该object的全部线程。

以下是wait()和notifyAll()的使用示例:

程序大概意思:两个线程协同工作,操作整型num。当第一个线程对num操作到50时,线程1进入等待状态,并释放对象锁。此时线程2获得对象锁,并开始操作num,将其从100减到1。线程2完成该操作后,调用notifyAll()函数,唤醒线程1,让其完成余下工作:将num从50继续减到1。

class Third {int num = 100;boolean wait = true;String str = new String();public synchronized void noti() {notifyAll();wait = false;}public void A() {synchronized (this) {while (num > 0) {System.out.println(Thread.currentThread().getName()+ "this is " + num--);if (num == 50 && wait)try {wait();} catch (InterruptedException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}}}}public void B() {synchronized (this) {num = 100;while (num > 0) {System.out.println(Thread.currentThread().getName()+ "this is " + num--);}num = 50;noti();}}}
class TxtThread implements Runnable {public TxtThread(Third obj) {third = obj;}Third third;public void run() {third.A();}}class TxtThread2 implements Runnable {public TxtThread2(Third obj) {third = obj;}Third third;public void run() {third.B();}}
public class Test {public static void main(String args[]) throws IOException,InterruptedException {Third th = new Third();TxtThread tt = new TxtThread(th);TxtThread2 t2 = new TxtThread2(th);new Thread(tt).start();new Thread(t2).start();}}
总结:

  • 对于wait(), notify(), notifyAll()的调用,必须对于同步代码,也就是说,调用这些方法前要获取到对象锁。
  • 对于wait(), notify(), notifyAll()的调用,必须要对应于同一个对象锁,否则没有同步效果。
  • 对于wait(), notify(), notifyAll()的调用,对象锁不能是类字面常量对应的锁。否则wait()方法会出错,同时notify()/notifyAll()方法也会出错。

interrupt()

比较好理解,此处不做说明。

join()

该方法主要用于保证线程运行的顺序。

示例如下:

public static void main(String args[]) throws IOException,InterruptedException { ThreadTest t1 = new ThreadTest("Thread1"); ThreadTest2 t2 = new ThreadTest2("Thread2"); ThreadTest3 t3 = new ThreadTest3("Thread3"); t3.start(); t3.join(); t2.start(); t2.join(3000); t1.start();}
该例子可以保证,线程t2只有在线程t3运行结束后才能运行;线程t1会等待线程t2运行3000ms,如果超时,线程t1也会开始运行。

sleep()

也是使线程处于等待状态,于wait()不同,wait()一般是等待某个object(上面已经详细介绍过)。而sleep()则是等待一段时间,且sleep()不会释放锁(如果获得了的话)。

1 0