众所周知java是不支持线程消息的,而线程消息对于某些操作是非常重要的,UI操作就必须依赖线程消息,比如windows,android等平台,否则UI代码将会臃肿不堪,甚至可能根本无法实现,再比如进行网络通信时由于接收线程必须尽快地从接收缓存取出数据,否则可能因为接收缓存溢出产生丢包,因而使用消息处理线程分派、处理数据,此时如果手动编写同步代码比较麻烦,而且影响代码简洁性,逻辑上也不够简洁,如果让处理线程等待线程消息,接收线程收到消息时只需要发送一个线程消息并唤醒处理线程即可返回继续等待网络数据
代码如下
import java.util.LinkedList;
import java.util.concurrent.locks.ReentrantLock;
/**
* 用于支持线程消息
*/
public class MessageLooperThread extends Thread{
public static class ThreadMessage{
public int type;
public Object data;
}
public static final int thread_msg_type_hart=Integer.MIN_VALUE;
private long timeOut=Long.MAX_VALUE;
private long next;
//由于只在队列末尾添加只在队列前端出栈所以使用linkedList效率最高
protected LinkedList<ThreadMessage>msgs=new LinkedList<>();
//锁定过程中只进行进队出队操作
protected ReentrantLock lock=new ReentrantLock();
public MessageLooperThread() {
}
public MessageLooperThread(long timeOut) {
this.timeOut=timeOut;
next=System.currentTimeMillis()+timeOut;
next=next<0?Long.MAX_VALUE:next;
}
public final void run() {
while(!isInterrupted()) {
long l=next-System.currentTimeMillis();
//如果等待没有等待超时则准备等待
if(l>0) {
synchronized (msgs) {
//如果没有消息则等待
if(msgs.isEmpty()) {
try {
msgs.wait(l);
} catch (InterruptedException e) {
//被其他线程终止
//e.printStackTrace();
break;
}
}
}
}
ThreadMessage msg=null;
lock.lock();
if(!msgs.isEmpty()) {
msg=msgs.removeLast();
}
lock.unlock();
//回调处理方法
if(msg!=null) {
onThreadMessage(msg);
}
if(next<=System.currentTimeMillis()) {
ThreadMessage m=new ThreadMessage();
m.type=thread_msg_type_hart;
m.data=System.currentTimeMillis();
next=next+timeOut;
next=next<0?Long.MAX_VALUE:next;
//发送超时消息
onThreadMessage(m);
}
}
onExit();
}
protected ThreadMessage popMessage(){
lock.lock();
try{
return msgs.pollLast();
}catch(Exception e){
return null;
}finally{
lock.unlock();
}
}
public void onExit(){
}
public void onThreadMessage(ThreadMessage obj){
}
public final void sendThreadMessage(ThreadMessage obj) {
//run方法的wait方法调用时不会与此处阻塞,尽量减小阻塞几率
lock.lock();
msgs.push(obj);
lock.unlock();
//msgs对象pop方法不会与此处阻塞
synchronized (msgs) {
msgs.notify();
}
}
}
为了保证线程消息先进先出故使用队列操作,链表效率最高也最方便。
使用重入锁的原因是接收线程调用msgs.wait方法时必须进入同步代码块,而这段代码中只是判断大小而已,添加操作不会影响这个方法,为了避免添加操作与之发生阻塞所以新建了一个重入锁用于进行进出队同步,然而发生这个现象的几率非常小,而且后面调用notify方法时同样进入了同步代码块,好像有点得不偿失,还不如直接将sendMessage方法标记为synchronized。代码简洁很多。
此类还实现了一个定时器,构造方法传入间隔,onMessage便会周期性地调用。当进行UDP丢失重传时这个功能很有用,完美实现添加、移除正在等待ACK的消息和周期重发的功能,可以避免访问临界数据。