Linux 下线程的运行模式是抢占式的,为了防止全局资源(可能是一个全局变量,也可能是系统的硬件资源,图扬声器)因为线程的切换出现错误,通常需要使用系统提供的同步机制来“独占”全局资源的访问权。虽然同步机制能解决资源访问的冲突问题,但也不可避免的带来了性能上的损失,因此,在不影响正确性的前提下,应当尽量避免使用同步机制。
1.原子操作
2.Android Native层的同步方法
3.Android Java层的同步方法:
Java语言提供了同步关键字,synchronized来支持线程间的同步操作。
synchronized锁住的是对象,不是一段代码。synchronized起到的是自动加锁和解锁的作用。
synchronized最常见的用法是保护一段代码:
class Foo implements Runnable {
private String mLock;
public void lockedMethod () {
......
synchronized(mLock) {
......
}
......
}
}
假设类Foo 有两个实例对象foo1 和foo2,如果在两个线程中分别调用foo1.lockedMethod()方法,在同一时间,只有一个线程能执行被synchronized括起来的代码,也就起到了互斥作用。但是如果另一个线程调用的是foo2.lockedMethod(),这样其实是两把不同的锁,不会起到互斥作用。
synchronized还可以用在类的方法前面,
class FooA implements Runnable {
public synchronized void lockedMethod () { ......}
public synchronized voidunlockedMethod () { ......}
}
语法上等价于synchronized (this){...... },表明上锁的对象是类的实例对象本身。假定类FooA有一个实例对象foo_a,两个线程都访问foo_a.lockedMethod()一定会互斥,一个线程访问foo_a.lockedMethod(),另一个线程访问foo_a.unlockedMethod()同样会互斥。当然如果是两个实例类对象foo_a和foo_b,分别调用他们的函数,都不会互斥。
synchronized的第三种用法是修饰一个静态方法,class FooB implements Runnable {
public synchronized static void lockedMethod () { ......}
}
语法上等价于 synchronized (FooB.class){......},表明上锁的对象是类本身。一旦有一个线程调用FooB类的任何实例对象 lockedMethod () ,另一个线程对于FooB类的任何对象的 lockedMethod ()方法,都将会产生互斥。
synchronized 只能提供互斥访问的同步机制,如果需要完成一些更复杂的同步操作,需要Object 类来配合完成。Object类是所有Java对象的基类,Object类中提供了一些与同步相关的方法:
//唤醒挂起的线程
public final native void notify();
public final native void notifyAll();
// 挂起调用者所在的线程
public final void wait();
public final void wait(long millis);
public final native void wait(long millis, int nanos);
以上函数的调用必须是在本线程取得对Object 对象的控制权以后才能进行。
更准确的说,应该是在synchronized的作用域内调用。
Android 的消息机制
消息驱动是一种进程或线程的运行模式。内部、外部的各种事件都可以放到消息队列中按序处理。这种模式特别适合处理大量的交互事件。Android 应用的UI线程,同样采用了消息驱动模式,所有外部来的按键消息,触屏消息,各种系统的Intent,广播等都会转化为内部消息,然后在主线程中分发处理。
Android 没有全局的消息队列,消息队列是和某个线程关联在一起的,每个线程最多有一个消息队列,消息的取出和处理在线程中完成。处理前必须为线程构造消息队列,发送消息也必须先得到消息队列的Handler对象。
Android中与消息机制相关的类主要是
1.Looper,
线程的消息循环处理器,每个线程只能有一个Looper对象。Looper内部有一个消息队列MessageQueue,所有线程的消息都存放在这个队列中。新建一个线程时,系统并不会马上为这个线程创建一个Looper对象,需要程序自己创建。
2.Handler,
Handler对象时Message的接收者和处理者。用户使用Handler对象把Message添加到消息队列中,同时通过Handler的回调方法handleMessage()来对消息队列当中的Message进行处理。Handler对象在构造时和某个Looper对象关联在一起。Handler和Looper是多对一的关系,多个Handler对象可以和一个Looper对象关联在一起。
在一个线程中可以只用一个Handler对象来处理所有的消息,也可以使用多个。构造一个Handler对象需要两个参数,线程的Lopper对象和消息的处理函数。对于Handler对象而言,参数Looper是必须的,因为它只能给某个线程的Looper对象发送消息,如果构造方法不指定特定的Looper对象,它会使用当前线程的Looper对象。但是参数callback不是必须的,应用程序可以通过这个callback方法来实现对消息的集中处理。也可以把处理消息的callback方法直接放在消息对象中。
Handler类是消息框架的一部分,消息的定义和响应还需要在应用层的代码中完成。Android 把消息的定义和处理完全独立了出来,线程只是提供了一个消息队列和消息响应代码的运行环境。Android主线程的实现都是在Framework中,但是可以使用下面的方法来构造一个带有callback方法的消息发送给主线程。
public static Message obtain (Handler handler, Runnable callback)
这样callback方法将在主线程中执行。
Handler类的另一个功能是发送消息:
send类:send只是把消息插入到了消息队列中,同时指定消息处理的时间。如果指定的时间为0,表示要立即处理,MessageQueue会把这条消息插到队列的头部,MessageQueue类中接收消息的接口如下:
boolean enqueueMessage (Message msg, long when)
发送消息的接口虽然多,但是都是在时间上玩花样,让应用方便使用而已。
public final boolean sendMessage (Message msg) // 希望马上处理,但是不打算插队
public final boolean sendEmptyMessage (int what) // 如果定义的消息只有ID, 不用附加参数,这个函数更佳方便。
public final boolean sendEmptyMessageDelayed (int what, long delayMillis) //
public final boolean sendEmptyMessageAtTime (nt what, long uptimeMillis) //
public final boolean sendMessageDelayed (Message msg, long delayMillis) // 希望延时一段时间处理
public boolean sendMessageAtTime (Message msg, long uptimeMillis) // 希望在指定时间处理
public final boolean sendMessageAtFrontOfQueue (Message msg) //非常紧急,希望尽快处理
post类:也是在使用send类的方法在发送消息,只是它们的参数要求是Runnable类的对象,然后在方法中调用了getPostMessage(r)获取了一个Message对象来发送。
public final boolean post(Runnable r){
return sendMessageDelayed (getPostMessage(r), 0);
}
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), 0);
}
综上,post类型的方法用来发送带有处理方法的消息,send类型的方法则用于发送传统的带有消息ID的消息。dispatchMessage():
消息回优先分发给消息中自带的回调方法。否则,如果Handler定义了回调方法,先调用这个方法处理,如果这个方法没有处理,还会调用Handler自己的HandleMessage()方法,这个方法缺省不做任何事情。如果实现了一个Handler类的继承类,也可以通过重载HandleMessage()方法来达到处理消息的目的。
消息的同步-——Message类的setAsynchronous()方法(给一条消息加上异步标志)。
一种情况:收到一条广播,或者在Activity刚启动时,可能需要完成一个比较耗时的操作,但是,如果消息处理函数长时间不返回,很容易发生ANR。常用的解决办法是发送一个消息,然后在消息处理函数中完成这个耗时操作。
当MessageQueue调用enqueueSyncBarrier(long when)后,使用setAsynchronous()方法来给一条消息做上标志,MessageQueue检测到消息的标志后,会正常处理这条消息,但是别的消息还是暂停处理,直到调用removeSynvBarrier()方法移走挡在消息队列前面的“SyncBarrier”
3.Message
Message是消息的载体,Message设计为Parcelable类的派生类,这表明Message可以通过binder来夸进程发送。
4.MessageQueue。
不同线程间发送消息,有同步保护吗?如何实现的?
消息队列不同于普通队列,每条消息都有时间,如何实现按时间分发消息?
没有消息时消息队列会挂起吗?来了新的消息又是如何被唤醒的?
消息队列是如何组织的?新消息是如何插入的?都是在队尾吗?
SyncBarrier是如何工作的?