前言:我们都知道Android的线程通信是用Handler、Looper机制实现的,面试也经常问道,网上也有很多文章介绍源码但是可能很多小白只是机械是的记忆,回答不清楚原理究竟是怎么回事。下边我将一步一步仿写一个Handler、Looper模拟Android的线程间通信,很简单一看就懂。
第一节 线程间通信原理
所谓的通信无非就是把“你”和“我”的消息传达到对方,方式很多种发信息、打电话、写信、说话、手势、眼神、留言等等。我觉得多数人都是被这个"线程间通信"给误导了,我敢肯定每个人做开发过程中都实现过这个线程间通信。比如大家熟悉的多线程并发中同步的问题,多线程访问同一个变量需要加锁,也就是说多线程可以访问一个共有的变量!
利用多线程可以访问同一个共有变量,我们下边实现一个简易的留言板。要实现线程2先留言,线程1过一会查看留言:
public class TestThreadMsg0 {
static String message;//全局公共变量
static class Thread1 extends Thread{
@Override
public void run() {
super.run();
for (;;) {
if (TestThreadMsg0.message != null){
System.out.println("线程:"+Thread.currentThread().getId()+" 收到:"+TestThreadMsg0.message);
TestThreadMsg0.message = null;
System.out.println("");
System.out.println("");
}
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class Thread2 extends Thread{
@Override
public void run() {
super.run();
for (;;) {
try {
String message="来自线程"+Thread.currentThread().getId()+"的消息";
System.out.println("线程:"+Thread.currentThread().getId()+" 发送:"+message);
TestThreadMsg0.message = message;
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args){
Thread1 t1 = new Thread1();
t1.start();
Thread2 t2 = new Thread2();
t2.start();
}
}
代码很简单不必多说,这样就实现了一个简单的留言板,也就是实现了线程2和线程1的通信。这就是线程间通信的基本原理是不是很简单!看到这里你肯定会想起来以前一定写过类似的代码!
这个线程通信实现有两个严重问题:(1)不是即时通信,线程1每隔一秒才看消息不及时 (2)如果线程2发消息过快会导致线程1还没看就被覆盖了会丢失消息。针对这两个问题来个优化版本,采用消息队列缓冲消息:
public class TestThreadMsg1 {
//阻塞式消息队列
static LinkedTransferQueue mQueue = new LinkedTransferQueue();
static class Thread1 extends Thread{
@Override
public void run() {
super.run();
for (;;) {
String message = null;
try {
message = (String) mQueue.take();//消息队列取出最新消息
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:"+Thread.currentThread().getId()+" 收到:"+ message);
System.out.println("");
System.out.println("");
}
}
}
static class Thread2 extends Thread{
@Override
public void run() {
super.run();
for (;;) {
try {
String message="来自线程"+Thread.currentThread().getId()+"的消息";
System.out.println("线程:"+Thread.currentThread().getId()+" 发送:"+message);
mQueue.put(message);//放到消息队列
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args){
Thread1 t1 = new Thread1();
t1.start();
Thread2 t2 = new Thread2();
t2.start();
}
}
上边采用LinkedTransferQueue阻塞是队列,实现消息队列。这样线程2可以随意发任性发消息不会丢失,线程1用一个死循环不断读取可以做到一有消息及时响应!
第二个改进版本虽然实现了2个线程间的即时通信,但是还有2个显而易见的问题。只有一个公共的全局消息队列,如果再来几个线程怎么办?(1)多个线程读取这个公共的消息队列就涉及到了上边提到的加锁同步问题,加锁就有等待意味不可能即时通信。(2)还有一个问题就是所有线程的消息都在一个公共的全局消息队列每个线程都可能看到这不是线程安全的。
针对存在的2个问题,优化方案是每个线程都有一个消息队列,牺牲内存空间换时间这样即安全由能即时通讯每个线程只遍历自己的消息队列就好了。详细代码请看下一节: