Android线程间交互(Java synchronized & Android Handler)

线程间交互(ITC,InterThread Communication)是多线程编程中一个很重要的内容。ITC涉及的问题有2个方面,其一是线程之间的同步互斥(Synchronization & Mutex),其二是线程之间数据内容的共享交换(Share & Exchange)。Android系统处理线程的同步互斥是使用Java提供的一套完整的同步互斥方法,使用synchronized关键字和一些方法实现;而Android系统提供的消息处理机制(android.os.Handler)可以很好的解决线程数据共享交换的问题,关于android.os.Handler的基本用法详见《Android中的Thread & HandlerThread & Handler》

Java的sychronized关键字可以修饰方法,也可以修饰块(block)。但是无论怎么用,使用了synchronized的地方是一个临界区,也就是这段代码同一时刻内只能被一个运行主体互斥的运行。方法(method)使用sychronized关键字时就是在方法定义时在修饰符的地方加上关键字,下面是使用方法:

synchronized void synMethod() {
	// critical(临界的) segment
	...
}

这种用synchronized修饰方法的可以等价到修饰块,synchronized加锁是以this对象加的,关于加锁的内容稍后介绍,代码如下:

void synMethod() {

	synchronized (this) {
		// critical(临界的) segment
		...
	}

}

上面已经展示了修饰块的惯常用法,synchronized修饰块时需要指明用哪个对象来加锁。锁(lock)是保证互斥访问的前提,好比一个房间的门锁一样,一个人进去之后锁上门,第二个人要进去就必须等第一个人开锁出来后才能进入(并锁上门),这样房间里始终至多只有一个人。

Java中每个对象都可以用来上锁,所以synchronized用来上锁的对象可以是Java里的任意一个对象,但是比较常用的是this或者是全局变量或者直接在synchronized括号中新实例一个对象。如果两个临界区(需要加锁的段)是没有同步互斥关系的,则用来加锁的对象应该是两个不同的对象,否则这两个临界区不能像预想的方式正常运行。因为方法加synchronized关键字是对this对象加锁的,所以下面两段代码就不是等价的:

//   SEGMENT 1: use this object to perform lock
synchronized void method_1() {
	...
}

synchronized void method_2() {
	...
}

//   SEGMENT 2: use separated new objects to perform lock
void method_1() {
	synchronized (new Object()) {
		...
	}
}

void method_2() {
	synchronized (new Object()) {
		...
	}
}

使用synchronized关键字可以很方便的达到临界区互斥的要求,多线程编程运行除了互斥以外有时还需要同步,这需要使用到Java提供的一些其它方法。因为同步比互斥要复杂些,而且还可能造成死锁(当然互斥也有可能造成死锁),所以有关同步的内容会在以后的内容中介绍。下面介绍android.os.Handler在多线程中数据内容共享和交换的方法。

关于android.os.Handler的基本概念和使用方法已经在《Android中的Thread & HandlerThread & Handler》很详细的介绍,这里就直接贴出代码片断。下面是Android中一个线程与主线程(UI线程)通过Handler进行交互的代码,实际功能是每隔3000毫秒改变TextView对象的内容:

public class Test extends Activity {

	// do you remember default Handler constructor use Looper object
	// of current thread (now UI thread)
	// h is global to be visible to other thread
	Handler h = new Handler() {
		public void handleMessage(Message msg) {

			Log.i("com.juwends.www.Test",
					"msg from other thread to main thread");

			// here we can change the content of TextView object
			...
		};
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		...
		// Construct our thread & start it
		new Thread(new Runnable() {

			public void run() {

				while (!Test.this.isFinishing()) {
					h.sendEmptyMessage(0);

					// sleep 3000 ms
					try {
						Thread.sleep(3000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

			}
		}).start();
		...

	};
}

如果需要两个非UI线程的线程进行交互,可以仿造线程与UI线程交互的方式并使用Android提供的HandlerThread来承载Handler的Looper对象,也可以通过UI线程承载的Looper对象的Handler来协助两个非UI线程的线程交互(有点线程同步的意思),代码与线程和UI线程交互基本一样。这2种方法中后者更为常用,这个方法也可以达到数据共享的目的。

Android的多线程编程中数据共享的方法有很多,只要可行都可以被采用,比较常用的方法有使用全局变量存储微量数据(比如就几个字节的或者是几个字符串数据),编写专门负责数据存储的公共类并实例化全局的对象用来存储少量数据,如果数据量比较大则可以考虑使用数据库,有关Android数据库Sqlite的内容将在以后介绍。数据共享一定要注意数据是否可以同时读写,如果不能,则一定要做好互斥操作,否则可能会程序崩溃(Crash)或者出现意想不到的结果。

本文涉及的代码完整工程包可以到Juwend’s Apps – Demos – AITC(待贴)下载学习使用。

本文《Android线程间交互(Java synchronized & Android Handler)》来自 www.juwends.com ,欢迎转载或CV操作,但请注明出处,谢谢!

Android线程间交互(Java synchronized & Android Handler)》上有6条评论