- 浏览: 278609 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
tan_1208815066:
传送pdf 的文件 不能正确的 传送
试试用Socket传文件 -
richardri:
结果是0怎么解决?支持mov、mp4、3gp吗?
JAVA獲取視頻文件的播放長度 -
zhujia130:
xiaoyaodandan 写道结果是0.。。。。。你给的路径 ...
JAVA獲取視頻文件的播放長度 -
xiaoyaodandan:
结果是0.。。。。。
JAVA獲取視頻文件的播放長度 -
flowerjacky:
明了
Java事务处理类
何 恐
摘要 : 在很多软件项目中,JAVA语言常常被用来开发后台服务程序。线程池技术是提高这类程序性能的一个重要手段。在实践中,该技术已经被广泛的使用。本文首先 对设计后台服务程序通常需要考虑的问题进行了基本的论述,随后介绍了JAVA线程池的原理、使用和其他一些相关问题,最后对功能强大的JAVA开放源码线 程池包util.concurrent 在实际编程中的应用进行了详细介绍。
关键字: JAVA;线程池;后台服务程序;util.concurrent
1 引言
在软件项目开发中,许多后台服务程序的处理动作流程都具有一个相同点,就是:接受客户端发来的请求,对请求进行一些相关的处理,最后将处理结果返回给客户 端。这些请求的来源和方式可能会各不相同,但是它们常常都有一个共同点:数量巨大,处理时间短。这类服务器在实际应用中具有较大的普遍性,如web服务 器,短信服务器,DNS服务器等等。因此,研究如何提高此类后台程序的性能,如何保证服务器的稳定性以及安全性都具有重要的实用价值。
2 后台服务程序设计
2.1 关于设计原型
构建服务器应用程序的一个简单的模型是:启动一个无限循环,循环里放一个监听线程监听某个地址端口。每当一个请求到达就创建一个新线程,然后新线程为请求服务,监听线程返回继续监听。
简单举例如下:
import java.net.*;
public class MyServer extends Thread{
public void run(){
try{
ServerSocket server=null;
Socket clientconnection=null;
server = new ServerSocket(8008);//监听某地址端口对
while(true){进入无限循环
clientconnection =server.accept();//收取请求
new ServeRequest(clientconnection).start();//启动一个新服务线程进行服务
……
}
}catch(Exception e){
System.err.println("Unable to start serve listen:"+e.getMessage());
e.printStackTrace();
}
}
}
实际上,这只是个简单的原型,如果试图部署以这种方式运行的服务器应用程序,那么这种方法的严重不足就很明显。
首先,为每个请求创建一个新线程的开销很大,为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源, 往往有时候要比花在处理实际的用户请求的时间和资源更多。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提 高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数。这样综合看来,系统的性能瓶颈就在于线程的创建开销。
其次,除了创建和销毁线程的开销之外,活动的线程也消耗系统资源。在一个 JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻运行的处理 线程数目,以防止服务器被“压死”的情况发生。所以在设计后台程序的时候,一般需要提前根据服务器的内存、CPU等硬件情况设定一个线程数量的上限值。
如果创建和销毁线程的时间相对于服务时间占用的比例较大,那末假设在一个较短的时间内有成千上万的请求到达,想象一下,服务器的时间和资源将会大量的花在 创建和销毁线程上,而真正用于处理请求的时间却相对较少,这种情况下,服务器性能瓶颈就在于创建和销毁线程的时间。按照这个模型写一个简单的程序测试一下 即可看出,由于篇幅关系,此处略。如果把(服务时间/创建和销毁线程的时间)作为衡量服务器性能的一个参数,那末这个比值越大,服务器的性能就越高。
应此,解决此类问题的实质就是尽量减少创建和销毁线程的时间,把服务器的资源尽可能多地用到处理请求上来,从而发挥多线程的优点(并发),避免多线程的缺点(创建和销毁的时空开销)。
线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时 线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也 就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。
3 JAVA线程池原理
3.1 原理以及实现
在实践中,关于线程池的实现常常有不同的方法,但是它们的基本思路大都是相似的:服务器预先存放一定数目的“热”的线程,并发程序需要使用线程的时候,从 服务器取用一条已经创建好的线程(如果线程池为空则等待),使用该线程对请求服务,使用结束后,该线程并不删除,而是返回线程池中,以备复用,这样可以避 免对每一个请求都生成和删除线程的昂贵操作。
一个比较简单的线程池至少应包含线程池管理器、工作线程、任务队列、任务接口等部分。其中线程池管理器(ThreadPool Manager)的作用是创建、销毁并管理线程池,将工作线程放入线程池中;工作线程是一个可以循环执行任务的线程,在没有任务时进行等待;任务队列的作 用是提供一种缓冲机制,将没有处理的任务放在任务队列中;任务接口是每个任务必须实现的接口,主要用来规定任务的入口、任务执行完后的收尾工作、任务的执 行状态等,工作线程通过该接口调度任务的执行。下面的代码实现了创建一个线程池:
public class ThreadPool
{
private Stack threadpool = new Stack();
private int poolSize;
private int currSize=0;
public void setSize(int n)
{
poolSize = n;
}
public void run()
{
for(int i=0;i
(发帖时间:2003-11-30 11:55:56)
---岑心 J
回复(1):
4.2 框架与结构
下面让我们来看看util.concurrent的框架结构。关于这个工具包概述的e文原版链接地址是http: //gee.cs.oswego.edu/dl/cpjslides/util.pdf。该工具包主要包括三大部分:同步、通道和线程池执行器。第一部分 主要是用来定制锁,资源管理,其他的同步用途;通道则主要是为缓冲和队列服务的;线程池执行器则提供了一组完善的复杂的线程池实现。
--主要的结构如下图所示
4.2.1 Sync
acquire/release协议的主要接口
- 用来定制锁,资源管理,其他的同步用途
- 高层抽象接口
- 没有区分不同的加锁用法
实现
-Mutex, ReentrantLock, Latch, CountDown,Semaphore, WaiterPreferenceSemaphore, FIFOSemaphore, PrioritySemaphore
还有,有几个简单的实现,例如ObservableSync, LayeredSync
举例:如果我们要在程序中获得一独占锁,可以用如下简单方式:
try {
lock.acquire();
try {
action();
}
finally {
lock.release();
}
}catch(Exception e){
}
程序中,使用lock对象的acquire()方法获得一独占锁,然后执行您的操作,锁用完后,使用release()方法释放之即可。呵呵,简单吧,想 想看,如果您亲自撰写独占锁,大概会考虑到哪些问题?如果关键的锁得不到怎末办?用起来是不是会复杂很多?而现在,以往的很多细节和特殊异常情况在这里都 无需多考虑,您尽可以把精力花在解决您的应用问题上去。
4.2.2 通道(Channel)
为缓冲,队列等服务的主接口
具体实现
LinkedQueue, BoundedLinkedQueue,BoundedBuffer, BoundedPriorityQueue, SynchronousChannel, Slot
通道例子
class Service { // ...
final Channel msgQ = new LinkedQueue();
public void serve() throws InterruptedException {
String status = doService();
msgQ.put(status);
}
public Service() { // start background thread
Runnable logger = new Runnable() {
public void run() {
try {
for(;;)
System.out.println(msqQ.take());
}
catch(InterruptedException ie) {} }
};
new Thread(logger).start();
}
}
在后台服务器中,缓冲和队列都是最常用到的。试想,如果对所有远端的请求不排个队列,让它们一拥而上的去争夺cpu、内存、资源,那服务器瞬间不当掉才怪。而在这里,成熟的队列和缓冲实现已经提供,您只需要对其进行正确初始化并使用即可,大大缩短了开发时间。
4.2.3执行器(Executor)
Executor是这里最重要、也是我们往往最终写程序要用到的,下面重点对其进行介绍。
类似线程的类的主接口
- 线程池
- 轻量级运行框架
- 可以定制调度算法
只需要支持execute(Runnable r)
- 同Thread.start类似
实现
- PooledExecutor, ThreadedExecutor, QueuedExecutor, FJTaskRunnerGroup
PooledExecutor(线程池执行器)是个最常用到的类,以它为例:
可修改得属性如下:
- 任务队列的类型
- 最大线程数
- 最小线程数
- 预热(预分配)和立即(分配)线程
- 保持活跃直到工作线程结束
-- 以后如果需要可能被一个新的代替
- 饱和(Saturation)协议
-- 阻塞,丢弃,生产者运行,等等
可不要小看上面这数条属性,对这些属性的设置完全可以等同于您自己撰写的线程池的成百上千行代码。下面以笔者撰写过得一个GIS服务器为例:
该GIS服务器是一个典型的“请求-服务”类型的服务器,遵循后端程序设计的一般框架。首先对所有的请求按照先来先服务排入一个请求队列,如果瞬间到达的 请求超过了请求队列的容量,则将溢出的请求转移至一个临时队列。如果临时队列也排满了,则对以后达到的请求给予一个“服务器忙”的提示后将其简单抛弃。这 个就够忙活一阵的了。
然后,结合链表结构实现一个线程池,给池一个初始容量。如果该池满,以x2的策略将池的容量动态增加一倍,依此类推,直到总线程数服务达到系统能力上限, 之后线程池容量不在增加,所有请求将等待一个空余的返回线程。每从池中得到一个线程,该线程就开始最请求进行GIS信息的服务,如取坐标、取地图,等等。 服务完成后,该线程返回线程池继续为请求队列离地后续请求服务,周而复始。当时用矢量链表来暂存请求,用wait()、 notify() 和 synchronized等原语结合矢量链表实现线程池,总共约600行程序,而且在运行时间较长的情况下服务器不稳定,线程池被取用的线程有异常消失的 情况发生。而使用util.concurrent相关类之后,仅用了几十行程序就完成了相同的工作而且服务器运行稳定,线程池没有丢失线程的情况发生。由 此可见util.concurrent包极大的提高了开发效率,为项目节省了大量的时间。
使用PooledExecutor例子
import java.net.*;
/**
*
Title:
*
Description: 负责初始化线程池以及启动服务器
*
Copyright: Copyright (c) 2003
*
Company:
* @author not attributable
* @version 1.0
*/
public class MainServer {
//初始化常量
public static final int MAX_CLIENT=100; //系统最大同时服务客户数
//初始化线程池
public static final PooledExecutor pool =
new PooledExecutor(new BoundedBuffer(10), MAX_CLIENT); //chanel容量为10,
//在这里为线程池初始化了一个
//长度为10的任务缓冲队列。
public MainServer() {
//设置线程池运行参数
pool.setMinimumPoolSize(5); //设置线程池初始容量为5个线程
pool.discardOldestWhenBlocked();//对于超出队列的请求,使用了抛弃策略。
pool.createThreads(2); //在线程池启动的时候,初始化了具有一定生命周期的2个“热”线程
}
public static void main(String[] args) {
MainServer MainServer1 = new MainServer();
new HTTPListener().start();//启动服务器监听和处理线程
new manageServer().start();//启动管理线程
}
}
类HTTPListener
import java.net.*;
/**
*
Title:
*
Description: 负责监听端口以及将任务交给线程池处理
*
Copyright: Copyright (c) 2003
*
Company:
* @author not attributable
* @version 1.0
*/
public class HTTPListener extends Thread{
public HTTPListener() {
}
public void run(){
try{
ServerSocket server=null;
Socket clientconnection=null;
server = new ServerSocket(8008);//服务套接字监听某地址端口对
while(true){//无限循环
clientconnection =server.accept();
System.out.println("Client connected in!");
//使用线程池启动服务
MainServer.pool.execute(new HTTPRequest(clientconnection));//如果收到一个请求,则从线程池中取一个线程进行服务,任务完成后,该线程自动返还线程池
}
}catch(Exception e){
System.err.println("Unable to start serve listen:"+e.getMessage());
e.printStackTrace();
}
}
}
关于util.concurrent工具包就有选择的介绍到这,更详细的信息可以阅读这些java源代码的API文档。Doug Lea是个很具有“open”精神的作者,他将util.concurrent工具包的java源代码全部公布出来,有兴趣的读者可以下载这些源代码并细 细品味。
5 结束语
以上内容介绍了线程池基本原理以及设计后台服务程序应考虑到的问题,并结合实例详细介绍了重要的多线程开发工具包util.concurrent的构架和使用。结合使用已有完善的开发包,后端服务程序的开发周期将大大缩短,同时程序性能也有了保障。
参考文献
[1] Chad Darby,etc. 《Beginning Java Networking》. 电子工业出版社. 2002年3月.
[2] util.concurrent 说明文件 http://gee.cs.oswego.edu/dl/cpjslides/util.pdf
[3] 幸勇.线程池的介绍及简单实现.http://www-900.ibm.com/developerWorks/cn/java/l-threadPool/index.shtml
2002年8月
[4]BrianGoetz. 我的线程到哪里去了http://www-900.cn.ibm.com/developerworks/cn/java/j-jtp0924/index.shtml
2002 年 12 月
发表评论
-
反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
2009-06-09 13:52 1473好长时间没有用过Spring了. 突然拿起书.我都发现自己对A ... -
Resin 3 LOG 设置
2009-06-04 11:51 2083<stdout-log path='${resin.ho ... -
Servlet和ThreadLocal的测试
2009-05-27 15:30 1034作者:中国IT实验室 来 ... -
技巧:利于ThreadLocal模式管理Session
2009-05-27 15:28 1036作者:dxaw 来源:赛迪网 在利用Hibernate ... -
运用Filter,ThreadLocal和Reflection,实现form到bean的填充
2009-05-27 15:27 1314开发者在线 Builder.com.cn 更新时间:2008- ... -
通通透透理解ThreadLocal
2009-05-27 15:24 1035开发者在线 Builder.com.cn ... -
centos 5.2中安装LAMP
2009-05-22 14:52 2329wget至以下目录:/tmp --------------- ... -
基于CentOS的LAMP
2009-05-22 14:50 3391基于CentOS的LAMP 作者:NetSeek ... -
http header详解
2009-01-19 12:19 3159HTTP(HyperTextTransferProtoco ... -
鼠标提示
2008-12-16 17:04 995<!DOCTYPE html PUBLIC " ... -
SQL备份语句
2008-12-16 16:43 1426--完整备份 Backup Database Northwin ... -
MySQL中修改密码及访问限制设置详解
2008-12-16 16:27 938一、MySQL修改密码方法总结 首先要说明一点的是:一般 ... -
通过GUID生成主键,保证主键全球惟一性
2008-12-16 11:34 1496使用GUID作为数据表主键的好处 使用GUID作为数 ... -
Tomcat对错误页面处理方法的问题
2008-12-15 16:45 2929各位都知道,我们可以在web.xml中定义对错误发生时的提示页 ... -
Hibernate下数据批量处理解决方案
2008-12-12 14:22 867很多人都对Java在批量数据的处理方面是否是其合适的场所持 ... -
任务调度表达式
2008-12-12 14:15 1090字段 允许值 允许的特殊字符 秒0-59 , - * / 分0 ... -
Windows动态库与Linux共享对象比较
2008-12-12 14:13 1488摘要:动态链接库技术 ... -
Java事务处理类
2008-12-10 11:59 1328Mysql5很好的支持了事物处理功能。不过支持这个功能的只有两 ... -
cookie 和session 的区别详解
2008-12-09 13:57 1016这些都是基础知识,不 ... -
jQuery插件---键盘快捷键.
2008-12-01 10:51 2251<!DOCTYPE html PUBLIC " ...
相关推荐
Java后台程序利用`java.concurrent`包的线程池技术,可以有效地解决高并发场景下的性能和资源管理问题。正确设计和配置线程池,结合合适的工作队列,可以大大提高服务器的处理能力,同时保证系统的稳定性和安全性。...
此外,`java.util.concurrent`包下的`ConcurrentHashMap`、`CopyOnWriteArrayList`等容器类,为并发编程提供了线程安全的数据结构。 在`DaemonThreadTest.java`这个文件中,可能涉及到了Java的守护线程(Daemon ...
在Java后台程序设计中,多线程互斥与同步控制是关键的概念,它们确保了并发执行的线程在访问共享资源时的安全性。线程互斥是指在同一时刻,只有一个线程能够访问特定的共享资源,以防止数据的不一致性。线程同步则是...
Java后台定时器是一种在应用程序运行时执行特定任务的机制,常用于执行周期性的维护操作、数据同步或发送通知等。在Java中,我们可以使用多种方式实现后台定时任务,包括使用`java.util.Timer`类,`java.util....
8. 轻量级锁定模式:Java 5引入了`java.util.concurrent.locks`包,其中的`ReentrantLock`和`Condition`提供了一种可中断、可重入的锁机制,比`synchronized`关键字更灵活。 9. 双向绑定模式:通过`java.util....
例如,使用无锁数据结构或原子操作(`java.util.concurrent.atomic`包)。 3. **避免死锁、活锁和饥饿**:理解并预防这些并发问题至关重要。死锁发生在两个或多个线程相互等待对方释放资源导致僵局;活锁是线程不断...
9. **并发集合**:Java的`java.util.concurrent`包提供了线程安全的集合,如`ConcurrentHashMap`、`BlockingQueue`等,它们设计用于多线程环境,能提高性能并减少同步开销。 10. **Future和Callable接口**:`Future...
Java的多线程框架在JDK5中得到了显著增强,引入了由Doug Lea设计的java.util.concurrent包,这是一个高效且强大的并发工具集。Doug Lea不仅是一位教授,还是一位杰出的作者和开发者,他的并发库对Java社区产生了深远...
此外,java.util.concurrent包中的BlockingQueue和CountDownLatch等工具类也是线程间通信的有效手段。 四、设计模式 1. 生产者消费者模式:使用BlockingQueue作为缓冲区,生产者负责将产品放入队列,消费者则从队列...
3. **线程同步**:为了避免多个线程对共享数据的竞态条件,Java提供了多种同步机制,如`synchronized`关键字、`volatile`变量、`Lock`接口(包括`ReentrantLock`)以及`java.util.concurrent.atomic`包下的原子类。...
- `java.util.concurrent.atomic` 包:提供了原子操作的变量类,如`AtomicInteger`、`AtomicLong`,它们提供了无锁的原子更新操作,适用于高性能并发场景。 6. ** volatile 与 synchronized 的区别** - `volatile...
8. **多线程考虑**: 如果截图过程需要时间,为了避免阻塞UI,可以考虑在后台线程(如使用`java.util.concurrent`包中的工具)执行截图操作,然后通过回调或者事件更新UI。 以上就是Java实现截图程序所涉及的主要...
本示例适用于API级别17及以上,因为它是针对Android 4.2(Jelly Bean)及更高版本设计的。 1. ** GridView的基础理解** - GridView是ListView的一个变体,它将每个项目安排在一个网格布局中,允许水平和垂直滚动。...
- `java.util.concurrent.locks.Lock`接口:提供了比synchronized更灵活的锁操作,如可重入锁(ReentrantLock)。 - `java.util.concurrent.CountDownLatch`:用于协调多个线程,例如等待所有线程完成后再继续执行...
虽然Java标准库中的`java.util.concurrent`包提供了强大的并发编程工具,但鉴于Android设备的性能限制和框架本身提供的Task机制,如`AsyncTask`,直接使用`java.util.concurrent`中的类(如`ExecutorService`、`...
6. **多线程**: 虽然记事本应用通常在单线程环境下运行,但开发者可能使用`java.lang.Thread`类或`java.concurrent`包中的工具来实现某些异步操作,如后台保存文件。 7. **版本控制**: 从文件名列表“codefans.net...
`java.lang.Thread`和`java.util.concurrent`包提供了线程管理和并发工具。 7. **数据存储**:如果需要保存用户的闹钟设置或历史记录,可以使用文件系统、SQLite数据库或者更高级的持久化框架如Hibernate。Java的`...
【Java 语言程序设计:第8章线程】 在Java编程中,线程是一个至关重要的概念,它使得程序能够同时执行多个任务,提高了系统资源的利用率和程序的响应速度。本章主要涵盖了多线程编程的基础知识,包括线程的生命周期...
这可以通过使用`synchronized`关键字、`java.util.concurrent`包中的工具或者`SwingUtilities.invokeLater()`来实现。 6. **设计模式**:在构建这样的应用程序时,可能会用到观察者模式(Observer Pattern),使...