为了将Socket的发送消息和接收消息解耦,笔者实现了一个简单的消息队列,这里重点是,发送队列为空的时候,要使发送线程休眠,接收队列为空的时候,要使读取Message的线程休眠。
这里主要用到Lock和Condition,没有了解过的同学可以翻阅我之前的博客。
package com.qq.client.socket;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.qq.client.util.Queue;
import com.qq.common.Message;
public class MessageQueue{
private Queue<Message> outQue;
private Queue<Message> enQue;
private Lock lock;
private Condition readCondition;
private Condition writerCondition;
private MessageQueue(){}
private Thread outThread;
private Thread enThread;
private volatile static MessageQueue instance = null;
static{
if(instance == null){
synchronized (MessageQueue.class) {
if(instance == null){
instance = new MessageQueue();
instance.lock = new ReentrantLock();
instance.readCondition = instance.lock.newCondition();
instance.writerCondition = instance.lock.newCondition();
instance.outQue = new Queue<Message>();
instance.enQue = new Queue<Message>();
MessageQueue.OutMessageQueue outMessageQueue = instance.new OutMessageQueue();
MessageQueue.EnMessageQueue enMessageQueue = instance.new EnMessageQueue();
instance.outThread = new Thread(outMessageQueue);
instance.outThread.setDaemon(true);
instance.enThread = new Thread(enMessageQueue);
instance.enThread.setDaemon(true);
instance.outThread.start();
instance.enThread.start();
}
}
}
}
public static MessageQueue getInstance(){
return instance;
}
/**
* 写出消息的消息队列
*/
private class OutMessageQueue implements Runnable{
@Override
public void run() {
Socket s = ClientSocket.getInstance().getS();
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(s.getOutputStream());
while(!Thread.currentThread().isInterrupted()){
try{
lock.lock();
while(outQue.isEmpty()){
writerCondition.await();
}
//写出消息
if(!outQue.isEmpty()){
oos.writeObject(outQue.deQueue());
oos.flush();
}
}finally{
lock.unlock();
}
}
} catch (IOException e1) {
e1.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
try {
if(oos != null) oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 接收消息的消息队列
*
*/
private class EnMessageQueue implements Runnable{
@Override
public void run() {
Socket s = ClientSocket.getInstance().getS();
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(s.getInputStream());
while(!Thread.currentThread().isInterrupted()){
Message message = (Message)ois.readObject();
enQue.enQueue(message);
try{
lock.lock();
readCondition.signalAll();
}finally{
lock.unlock();
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally{
try {
if(ois != null)ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 添加消息
*/
public void addMessage(Message message){
try{
lock.lock();
outQue.enQueue(message);
writerCondition.signalAll();
}finally{
lock.unlock();
}
}
/**
* 返回服务器消息
* @return 服务器消息
*/
public Message getMessage(){
try {
lock.lock();
while(enQue.isEmpty()){
readCondition.await();
}
return enQue.deQueue();
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();
}
return null;
}
}
代码思路:
定义了两个Queue,队列为空的时候,发送线程就停止发送,当outQueue中收到元素的时候,才通过Socket发送出去。读取也是一样的,enQueue队列为空的时候,其他线程就读取不到队列中的信息,同时应该停止等待队列中收到信息,当enQueue中收到来自Socket的信息之后,就唤醒读取的线程。
package com.qq.client.util;
import java.util.ArrayList;
import java.util.List;
public class Queue<T> {
private List<T> list = new ArrayList<T>();
public void enQueue(T t){
list.add(t);
}
public T deQueue(){
if(!isEmpty()){
T t = list.get(0);
list.remove(0);
return t;
}
return null;
}
public boolean isEmpty(){
if(list.size() == 0){
return true;
}
return false;
}
public int size(){
return list.size();
}
}