在介绍Android的线程、进程模型之前,首先看一下Java线程相关的内容。
线程和进程是对操作系统中程序执行、资源分配的一种抽象,进程有独立的地址空间,线程附属于进程并共享进程中的资源。
并发:Concurrent,表面上多个程序在同时执行,实际上就是多个程序在分享不同的CPU时间片。
并行:Parallel,真正意义上的同时执行,如在多核处理器的系统中。
多线程常见的一个问题是线程同步,Java中常用synchronized关键字实现同步,synchronized可用于代码块、非静态方法、静态方法和类。修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号括起来的代码,作用的对象是调用这个代码块的对象;修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
与synchronized关联的还有两个同步工具wait和notify,它们是Java对象固有的,本质上是一个monitor,在并发编程时会生效。wait和notify必须在synchronized块中使用,且作用于同一个monitor。
volatile关键字,为域变量的访问提供了一种免锁机制,使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,因此每次使用该域就要重新计算,而不是使用寄存器中的值,volatile不会提供任何原子操作,它也不能用来修饰final类型的变量。
另外,Java还提供了一些用于线程同步的类,如java.lang.ThreadLocal,java.util.concurrent中的锁、原子操作、并发数据结构等。
(多进程常见的一个问题是进程间通信,常见的机制有管道pipe、命名管道fifo、信号signal、消息队列message queue、共享内存shared memory、存储映射mapped memory、信号量semaphore、套接字socket等,这里不作详细介绍。 )
java.language.Thread.State定义了下面列出的六个线程状态,同一时刻只能处于某一状态。
NEW:至今尚未启动的线程的状态。
RUNNABLE:可运行线程的线程状态。处于可运行状态的某一线程正在 Java 虚拟机中运行,但它可能正在等待操作系统中的其他资源,比如处理器。
BLOCKED:受阻塞并且正在等待监视器锁的某一线程的线程状态。处于受阻塞状态的某一线程正在等待监视器锁,以便进入一个同步的块/方法,或者在调用 Object.wait 之后再次进入同步的块/方法。
WAITING:某一等待线程的线程状态。某一线程因为调用下列方法之一而处于等待状态:不带超时值的 Object.wait;不带超时值的 Thread.join;LockSupport.park。处于等待状态的线程正等待另一个线程,以执行特定操作。 例如,已经在某一对象上调用了 Object.wait() 的线程正等待另一个线程,以便在该对象上调用 Object.notify() 或 Object.notifyAll()。已经调用了 Thread.join() 的线程正在等待指定线程终止。
TIMED_WAITING:具有指定等待时间的某一等待线程的线程状态。某一线程因为调用以下带有指定正等待时间的方法之一而处于定时等待状态:Thread.sleep;带有超时值的 Object.wait;带有超时值的 Thread.join;LockSupport.parkNanos;LockSupport.parkUntil。
TERMINATED:已终止线程的线程状态。线程已经结束执行。
使用Thread创建线程,一般有两种方法,一种是在继承自Thread的子类中Override run方法,另一种是实现Runnable,如下代码所示。
new Thread() {
@Override
public void run() {
Log.d("ThreadTest", "Thread run");
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
Log.d("ThreadTest", "Runnable run");
}
}).start();
另外,还可以使用Future与Runnable结合的FutureTask,实现Callable来创建线程,好处是call方法有返回值,可以抛出异常,代码如下所示。
new Thread(new FutureTask(new Callable() {
@Override
public Integer call() throws Exception {
Log.d("ThreadTest", "Callable call");
return null;
}
}) {
}).start();
Activity中的runOnUiThread可以进行转线程,在Worker Thread调用,传入一个Runnable,这个Runnalbe就会在UI Thread执行,代码如下所示。
new Thread() {
@Override
public void run() {
Log.d("ThreadTest", "run on worker thread");
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d("ThreadTest", "run on ui thread");
}
});
}
}.start();
android.os.AsyncTask用于执行异步任务,内部实现使用了上面提到的Thread、同步方法,以及Android中的Handler。AyncTask泛型包括三个参数,分别为Params、Progress、Result,以及四个回调,onPreExecute、doInBackground、onProgressUpdate、onPostExecute,其中doInBackground在工作线程执行,其它三个在主线程执行,publishProgress用于更新Progress,之后会调到onProgressUpdate,例子代码如下。
new AsyncTask() {
@Override
protected void onPreExecute() {
Log.d("ThreadTest", "AsyncTask onPreExecute");
}
@Override
protected Integer doInBackground(Integer... params) {
Log.d("ThreadTest", "AsyncTask doInBackground " + params);
publishProgress(50);
publishProgress(100);
return 100;
}
@Override
protected void onProgressUpdate(Integer... progress) {
Log.d("ThreadTest", "AsyncTask onProgressUpdate " + progress);
}
@Override
protected void onPostExecute(Integer result) {
Log.d("ThreadTest", "AsyncTask onPostExecute " + result);
}
}.execute(new Integer(1), new Integer(2));
Handler是Android中处理多线程的一种常用机制,涉及的相关类包括Looper、MessageQueue、Message、Runnable。Handler用法主要有两种,一种是Send Message,另一种是Post Runnable,两者本质上是一样的,最终都调用了sendMessageAtTime,将Message放到MessageQueue。示例代码如下所示。
new Thread() {
@Override
public void run() {
Log.d("ThreadTest", "Thread run to send message");
myHandler.sendEmptyMessage(100);
}
}.start();
private MyHandler myHandler = new MyHandler();
private static class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Log.d("ThreadTest", "Handler handleMessage " + msg);
}
}
Android基于Linux Kernel,进程管理类似于Linux的管理方式,如常见的权限管理、OOM机制、进程优先级等。
Android中的App是沙箱隔离的,默认运行在独立的进程中,不过可以通过配置Manifest中的sharedUserId和签名,以运行在同一进程,运行在同一进程的好处就是数据共享。Android中的Activity、Service默认运行在App进程,可以通过配置Manifest中的android:process属性让它们运行在独立于App的进程中。
Binder是Android中非常重要的一个模块,用于数据通信。Binder与Linux既有的IPC机制相比,好处是数据拷贝一次,安全性高,使用了面向对象的思想。
Binder实现包括四个部分,分别是Server、Client、ServiceManager和Driver。Driver运行在内核空间,其它三个运行在用户空间。
Driver是一种广义上的驱动,与硬件并没有关系,只是实现方式类似,如常见的open、mmap、poll、ioctl等文件操作。Binder保证IPC只发生一次数据拷贝,从一个进程拷贝到另一个进程,而普通的IPC一般会发生两次数据拷贝,从用户空间拷贝到内核空间,再从内核控件拷贝到用户空间,甚至会拷贝更多次。
ServiceManager类似于网络中的DNS,将字符形式的Binder名转换为Binder实体引用,方便Client通过Binder名获取Binder实体引用。注册了名字的Binder为具名Binder,没有注册名字的Binder为匿名Binder。Server创建Binder实体,然后通过Driver将其传给ServiceManager,ServiceManager便注册这个Binder到一个索引表。