Guarded Suspention模式主要思想是:
当条件不满足时,线程等待,直到条件满足时,等待该条件的线程被唤醒。
我们设计一个客户端线程和一个服务器线程,客户端线程不断发送请求给服务器线程,服务器线程不断处理请求。当请求队列为空时,服务器线程就必须等待,直到客户端发送了请求。
先定义一个请求队列:Queue
java 代码
- package com.javaeedev.thread;
-
- import java.util.*;
-
- public class Queue {
- private List queue = new LinkedList();
- public synchronized Request getRequest() {
- while(queue.size()==0) {
- try {
- this.wait();
- }
- catch(InterruptedException ie) {
- return null;
- }
- }
- return (Request)queue.remove(0);
- }
-
- public synchronized void putRequest(Request request) {
- queue.add(request);
- this.notifyAll();
- }
-
- }
蓝色部分就是服务器线程的等待条件,而客户端线程在放入了一个request后,就使服务器线程等待条件满足,于是唤醒服务器线程。
客户端线程:ClientThread
java 代码
- package com.crackj2ee.thread;
-
- public class ClientThread extends Thread {
- private Queue queue;
- private String clientName;
-
- public ClientThread(Queue queue, String clientName) {
- this.queue = queue;
- this.clientName = clientName;
- }
-
- public String toString() {
- return "[ClientThread-" + clientName + "]";
- }
-
- public void run() {
- for(int i=0; i<100; i++) {
- Request request = new Request("" + (long)(Math.random()*10000));
- System.out.println(this + " send request: " + request);
- queue.putRequest(request);
- try {
- Thread.sleep((long)(Math.random() * 10000 + 1000));
- }
- catch(InterruptedException ie) {
- }
- }
- System.out.println(this + " shutdown.");
- }
- }
服务器线程:ServerThread
java 代码
- package com.crackj2ee.thread;
- public class ServerThread extends Thread {
- private boolean stop = false;
- private Queue queue;
-
- public ServerThread(Queue queue) {
- this.queue = queue;
- }
-
- public void shutdown() {
- stop = true;
- this.interrupt();
- try {
- this.join();
- }
- catch(InterruptedException ie) {}
- }
-
- public void run() {
- while(!stop) {
- Request request = queue.getRequest();
- System.out.println("[ServerThread] handle request: " + request);
- try {
- Thread.sleep(2000);
- }
- catch(InterruptedException ie) {}
- }
- System.out.println("[ServerThread] shutdown.");
- }
- }
服务器线程在红色部分可能会阻塞,也就是说,Queue.getRequest是一个阻塞方法。这和java标准库的许多IO方法类似。
最后,写一个Main来启动他们:
java 代码
- package com.crackj2ee.thread;
-
- public class Main {
-
- public static void main(String[] args) {
- Queue queue = new Queue();
- ServerThread server = new ServerThread(queue);
- server.start();
- ClientThread[] clients = new ClientThread[5];
- for(int i=0; i clients[i] = new ClientThread(queue, ""+i);
- clients[i].start();
- }
- try {
- Thread.sleep(100000);
- }
- catch(InterruptedException ie) {}
- server.shutdown();
- }
- }
我们启动了5个客户端线程和一个服务器线程,运行结果如下:
[ClientThread-0] send request: Request-4984
[ServerThread] handle request: Request-4984
[ClientThread-1] send request: Request-2020
[ClientThread-2] send request: Request-8980
[ClientThread-3] send request: Request-5044
[ClientThread-4] send request: Request-548
[ClientThread-4] send request: Request-6832
[ServerThread] handle request: Request-2020
[ServerThread] handle request: Request-8980
[ServerThread] handle request: Request-5044
[ServerThread] handle request: Request-548
[ClientThread-4] send request: Request-1681
[ClientThread-0] send request: Request-7859
[ClientThread-3] send request: Request-3926
[ServerThread] handle request: Request-6832
[ClientThread-2] send request: Request-9906
......
可以观察到ServerThread处理来自不同客户端的请求。
思考
Q: 服务器线程的wait条件while(queue.size()==0)能否换成if(queue.size()==0)?
A: 在这个例子中可以,因为服务器线程只有一个。但是,如果服务器线程有多个(例如Web应用程序有多个线程处理并发请求,这非常普遍),就会造成严重问题。
Q: 能否用sleep(1000)代替wait()?
A: 绝对不可以。sleep()不会释放锁,因此sleep期间别的线程根本没有办法调用getRequest()和putRequest(),导致所有相关线程都被阻塞。
Q: (Request)queue.remove(0)可以放到synchronized() {}块外面吗?
A: 不可以。因为while()是测试queue,remove()是使用queue,两者是一个原子操作,不能放在synchronized外面。
总结
多线程设计看似简单,实际上必须非常仔细地考虑各种锁定/同步的条件,稍不小心,就可能出错。并且,当线程较少时,很可能发现不了问题,一旦问题出现又难以调试。
所幸的是,已有一些被验证过的模式可以供我们使用,我们会继续介绍一些常用的多线程设计模式。
源代码地址:http://javaeedev.googlecode.com/svn/trunk/GuardedSuspension/
Zip包下载:http://javaeedev.googlecode.com/files/GuardedSuspension.zip
Worker Pattern
相关推荐
为能和大家能共同探讨"设计模式",我将自己在学习中的心得写下来,只是想帮助更多人更容易理解 GoF 的《设计模式》。由 于原著都是以C++为例, 以Java为例的设计模式基本又都以图形应用为例,而我们更关心Java在中间件等...
- 《深入理解Java虚拟机:JVM高级特性与最佳实践》:周志明的书,深入讲解JVM内存模型和多线程,对并发编程有较大帮助。 - 《Java EE 6 权威指南.基础篇.Basic concepts》:尽管现代开发更多依赖Spring等框架,...
10. **设计模式**:习题可能涉及到一些常见的设计模式,如单例、工厂、观察者、装饰器等,设计模式是解决常见编程问题的通用解决方案。 通过解答这些习题,学习者不仅能巩固Java语言的基础,还能提高解决实际问题的...
Java作为一门广泛使用的编程语言,其面试题涵盖了众多的知识领域,包括基础语法、面向对象、集合框架、多线程、异常处理、IO流、网络编程、设计模式、JVM优化、数据库操作等。以下是一些Java面试中常被问到的知识点...
8. **设计模式**:良好的软件设计通常会运用设计模式,比如单例模式用于控制抽奖逻辑的实例化,工厂模式用于创建GUI组件,观察者模式用于更新界面状态等。 9. **程序测试**:代码已经测试并确认无误,这意味着...
过滤流(FilterStream)是Java I/O中的一种设计模式,它们在已存在的流之上添加额外功能。例如,DataInputStream和DataOutputStream扩展了字节流,增加了对基本类型数据的读写支持;而BufferedInputStream和...
9. 设计模式:总结Java中常用的23种设计模式的定义、使用场景和实现方式。 10. Spring/SpringMVC:介绍Spring框架的核心特性,包括IoC容器、AOP、事务管理、Spring MVC框架等。 11. SpringBoot/SpringCloud:...
3. **多线程** - **线程状态**:新建、运行、阻塞、等待、终止等五种状态。 - **同步机制**:synchronized关键字,wait()、notify()和notifyAll()方法,以及Lock接口。 - **并发工具类**:如CountDownLatch、...
9. **设计模式**:可能包含一些常见设计模式的实现,如单例、工厂、观察者等。 10. **异常与日志记录**:如何有效地捕获和记录程序中的异常信息。 11. **反射机制**:用于在运行时动态获取类的信息和操控对象。 ...
《深入理解Java虚拟机》、《并发编程的艺术》、《Java多线程核心编程艺术》、《Java8函数式编程》、《Redis设计与实现》、《RocketMQ技术内幕》、《Spring技术内幕》、《Spring源码深度解析》、《剑指Offer》、...
11.1 多线程的servlet模型 350 11.2 线程安全的servlet 351 11.2.1 变量的线程安全 351 11.2.2 属性的线程安全 360 11.3 singlethreadmodel接口 362 11.4 小结 363 11.5 思考题 363 第3部分 jsp篇 第12章 ...
11.1 多线程的servlet模型 350 11.2 线程安全的servlet 351 11.2.1 变量的线程安全 351 11.2.2 属性的线程安全 360 11.3 singlethreadmodel接口 362 11.4 小结 363 11.5 思考题 363 第3部分 jsp篇 第12章 ...
【Java】在Java部分,面试可能涵盖基础语法、面向对象编程、集合框架、多线程、异常处理、IO流、JVM内存模型以及设计模式等方面。例如,可能会问到如何优化代码性能,如何处理并发问题,或者对Java 8的新特性如...
3.Java语言的特点简单、解释性、面向对象、健壮、动态、高性能、多线程、分布式处理、安全性、开源、结构中立、跨平台。 3大特性 (1).安全性 (2).虚拟机JVM(一次编译到处运行) (3).GC垃圾回收机制 开发环境...
11.1 多线程的servlet模型 350 11.2 线程安全的servlet 351 11.2.1 变量的线程安全 351 11.2.2 属性的线程安全 360 11.3 singlethreadmodel接口 362 11.4 小结 363 11.5 思考题 363 第3部分 jsp篇 第12章 ...
11.1 多线程的servlet模型 350 11.2 线程安全的servlet 351 11.2.1 变量的线程安全 351 11.2.2 属性的线程安全 360 11.3 singlethreadmodel接口 362 11.4 小结 363 11.5 思考题 363 第3部分 jsp篇 第12章 ...
11. **代码结构与设计模式**:良好的代码组织和设计模式(如工厂模式、观察者模式)可以使项目更易于维护和扩展。 通过学习和实践这个【小小图片爬虫】项目,开发者不仅可以掌握HTTP请求的基本操作,还能了解到如何...
11. 设计模式:单例、工厂、装饰器等23种设计模式的实例解析。 12. 数据库操作:SQL语言、JDBC编程、ORM框架(如Hibernate)等。 13. 测试:单元测试、集成测试,JUnit、Mockito等工具的使用。 博主可能通过Word...
过滤器链的设计模式使得功能模块化,方便代码复用和扩展。 2. **Session**:MINA中的Session代表了客户端与服务器之间的一个会话。它包含了会话状态,如连接状态、读写缓冲区、心跳机制等,同时也提供了发送和接收...