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

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

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


二 . 进程

一般来说,一个程序包对应于一个进程。(当然,这也不是不可改变的,我们可以通过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() {

			@Override
			public 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 {

		@Override
		public 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 block
						e1.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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值