1 要解决的问题
Guarded Suspension(保护暂停),其核心思想是:仅当服务端进程准备好后,才提供服务。假设一种场景,服务器可能会在短时间内接收到大量的客户端请求,可能已经超过了服务器的负载,而服务器又不能随意丢弃任何一个用户的请求。此时,最佳的处理方案莫过于让客户端请求排队,由服务器一个一个的处理。这样,既保证了所有的客户端请求不会丢失,同时也避免了服务器由于同时处理太多请求而崩溃。
2 涉及的角色
(1)ServerThread:服务器线程。从请求队列中获取请求
(2)ClientThread:客户端线程。提交请求,将请求插入共享的请求队列。
(3)Request:表示客户端请求。
(4)RequestQueue:请求队列。由客户端和服务端共同维护。必须是线程安全的。
3 Guarded Suspension的结构
从流程图可以看出,在频繁的客户端请求中,RequestQueue充当了中间缓存,存放了大量的未处理的请求,保证了客户请求不丢失,同时,也保护服务器不会收到大量的并发请求而崩溃。
4 代码实现
4.1 无返回值给客户端线程
(1)Request.java:
public class Request {
private String name;
public Request(String name) {
// TODO Auto-generated constructor stub
this.name=name;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "Request:"+name;
}
}
(2)ServerThread.java:
public class ServerThread extends Thread{
private BlockingQueue<Request> queue;
public ServerThread(BlockingQueue<Request> queue,String name) {
// TODO Auto-generated constructor stub
super(name);
this.queue=queue;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
/**
* 如果当前队列为空,将会阻塞
* 其实这种存在缺点,因为一个线程只能处理一个请求,就会挂掉,
* 这里可以加上一个while(true)的循环,一直监听请求队列,即一个线程处理完一个请求后,
* 将会判断请求队列是否为空,如果为空则阻塞,不为空则继续处理
*/
Request request=queue.take();
System.out.println("Master"+Thread.currentThread().getName()+"处理请求:"+request);
/**
* 模拟耗时操作
*/
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(3)ClientThread.java:
public class ClientThread extends Thread{
private BlockingQueue<Request> queue;
public ClientThread(BlockingQueue<Request> queue,String name) {
// TODO Auto-generated constructor stub
super(name);
this.queue=queue;
}
@Override
public void run() {
// TODO Auto-generated method stub
/**
* 模拟每个客户端线程可以发送多个请求
*/
for(int i=0;i<1;i++){
/**
* 构造请求
*/
Request request=new Request(Thread.currentThread().getName());
/**
* 提交请求
*/
try {
queue.put(request);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 这个方式有一个缺点,即不能获得Server的返回值
*/
}
}
(4)Main.java:
public class Main {
public static void main(String[] args) {
BlockingQueue<Request> queue=new LinkedBlockingQueue<Request>();
/**
* 启动多个服务端线程
*/
for(int i=0;i<30;i++){
new ServerThread(queue, String.valueOf(i)).start();
}
/**
* 启动多个客户端线程
*/
for(int i=0;i<10;i++){
new ClientThread(queue, String.valueOf(i)).start();
}
}
}
4.2 有返回值给客户端线程
(1)Request.java:
public class Request {
private String name;
private Data data;
public Request(String name) {
// TODO Auto-generated constructor stub
this.name=name;
}
public synchronized Data getData() {
return data;
}
public synchronized void setData(Data data) {
this.data=data;
}
}
(2)Data.java:
public class Data {
private boolean isReady;
private String content;
public synchronized void setContent() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
content="finished";
isReady=true;
notifyAll();
}
public synchronized String getContent() {
while (!isReady) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return content;
}
}
(3)ServerTask.java:
/**
* 这里实现了callable接口是为了能够获得返回值
* @author 深蓝色
*
*/
public class ServerTask implements Callable<String>{
/**
* 使用阻塞队列,生产者消费者模式
*/
private BlockingQueue<Request> queue;
public ServerTask(BlockingQueue<Request> queue) {
// TODO Auto-generated constructor stub
this.queue=queue;
}
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
/**
* 从队列中获取请求,队列为空则阻塞
*/
Request request=queue.take();
/**
* 模拟请求的耗时操作
*/
Thread.sleep(1000);
request.getData().setContent();
return null;
}
}
(4)ClientTask.java:
public class ClientTask implements Callable<Data>{
private BlockingQueue<Request> queue;
public ClientTask(BlockingQueue<Request> queue) {
// TODO Auto-generated constructor stub
this.queue=queue;
}
@Override
public Data call() throws Exception {
// TODO Auto-generated method stub
/**
* 构造请求,提交请求
*/
Request request=new Request(Thread.currentThread().getName());
request.setData(new Data());
System.out.println("提交请求");
queue.put(request);
/**
* 将数据交给服务器端处理
* 如果处理完毕,那么getContent直接返回,否则,会阻塞
*/
Data data=request.getData();
System.out.println("获取处理后的结果:"+data.getContent());
return null;
}
}
(5)Main.java:
public class Main {
public static void main(String[] args) throws Exception{
/**
* 公用一个阻塞队列
* client向队列中添加请求
* Server从队列中取出请求,处理
*/
BlockingQueue<Request> queue=new LinkedBlockingQueue<Request>();
ExecutorService executor=Executors.newCachedThreadPool();
for(int i=0;i<5;i++){
executor.submit(new ClientTask(queue));
}
for(int i=0;i<5;i++){
executor.submit(new ServerTask(queue));
}
}
}
5 参考文献
1>.Java程序性能优化. 葛一鸣 等著.