??xml version="1.0" encoding="utf-8" standalone="yes"?> Spring框架则简化了使用JEElg(包括JMS)的Q务。它提供的模板机刉藏了典型的JMS实现的细节,q样开发h员可以集中精力放在处理消息的实际工作中,而不用担心如何去创徏Q访问或清除JMS资源?/p>
本文对Spring JMS API作一个概qͼq过一个运行在JBoss MQ服务器上的web例程来介l如何用Spring JMS API来异步处理(发送和接收Q消息。我通过传统JMS实现和Spring JMS实现两者间的比较,来展CZ用Spring JMS处理消息是如何的单和灉|?/p>
在现实中Q大多数webh都是同步处理的。例如,当用戯d一个网站,首先输入用户名和密码Q然后服务器验证d合法性。如果验证成功,E序允许该用户q入|站。这里,dh在从客户端接收以后被x处理了。信用卡验证是另一个同步处理的例子Q只有服务器证实输入的信用卡h有效的,同时客户在帐户上有够的存款Q客h被允许l操作。但是让我们思考一下在序处理pȝ上的支付l算步骤。一旦系l证实该用户信用卡的信息是准的Qƈ且在帐户上有_的资金,׃必等到所有的支付l节落实、{账完成。支付结可以异步方式进行,q样客户可以l箋q行核查操作?/p>
需要比典型同步h耗费更长旉的请求,可以使用异步处理。另一个异步处理的例子是,在本地贷Ƒ֤理程序中Q提交至自动扉KpȝQAUSQ的信用h处理q程。当借方提交h甌后,抉|公司会向AUS发送请求,以获取信用历史记录。由于这个请求要求得到全面而又详细的信用报告,包括借方C和过ȝ帐户Q最q的付款和其他胦务资料,服务器需要耗费较长的时_几小时或着有时甚至是几天)来对q些h作出响应。客LE序Q应用)要与服务器连接ƈ耗费如此长的旉来等待结果,q是毫无意义的。因此通信应该是异步发生的Q也是Q一旦请求被提交Q它p攄在队列中Q同时客L与服务器断开q接。然后AUS服务从指定的队列中选出hq行处理Qƈ处理得到的消息攄在另一个消息队列里。最后,客户端程序从q个队列中选出处理l果Q紧接着处理q个信用历史数据?/p>
如果您用过JMS代码Q您会发现它与JDBC或JCA很像。它所包含的样本代码创建或JMS资源对象回溯Q得每一ơ您需要写一个新cL发送和接收消息Ӟ都具有更好的代码密集性和重复性。以下序列显CZ传统JMS实现所包括的步骤: 您可以看刎ͼ步骤6是处理消息的唯一地方。其他步骤都只是理与实际业务要求无关的JMS资源Q但是开发h员必ȝ写ƈl护q些额外步骤的代码?/p>
Spring框架提供了一个模板机制来隐藏Java APIs的细节。JEE开发h员可以用JDBCTemplate和JNDITemplatecL分别讉K后台数据库和JEE资源Q数据源Q连接池Q。JMS也不例外。Spring提供JMSTemplatec,因此开发h员不用ؓ一个JMS实现ȝ写样本代码。接下来是在开发JMS应用E序时Spring所h一些的优势?/p>
说明Q因为只是ؓ了演C如何用spring~写jms的应用,所以本例没有什么实际用途?/p>
E序功能QMessageProducer.javaҎ一用户信息产生一个消息发送到 JMS ProviderQ由MessageConsumer.java接收?/p>
1.在Jboss里配|XML文g创徏一个新的JMS provider?br />
打开位于%JBOSS_HOME%server\default\deploy\jms文g夹下的jbossmq-destinations-service.xml文gQ加入以下代码片断: (4).试用例Q?br />
//======== 生者测试用?=============== //============ 消费者测试用?=============== 回复(1): Title: Description: 负责初始化线E池以及启动服务?/span> Copyright: Copyright (c) 2003 Company: Title: Description: 负责监听端口以及Q务交l线E池处理 Copyright: Copyright (c) 2003 Company:
<exception-mapping exception="org.apache.struts.register.exceptions.SecurityBreachException" result="securityerror" />
<exception-mapping exception="java.lang.Exception" result="error" />
</global-exception-mappings>
<global-results>
<result name="securityerror">/securityerror.jsp</result>
<result name="error">/error.jsp</result>
</global-results>异步消息传递和面向服务架构
JMS
Spring JMS
CZE序
<!-- Register User Send/Receive Queue -->
<mbean code="org.jboss.mq.server.jmx.Queue"
name="jboss.mq.destination:service=Queue,name=registerUserQueue">
<depends optional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends>
</mbean>
<!-- Register User Send/Receive Topic -->
<mbean code="org.jboss.mq.server.jmx.Topic"
name="jboss.mq.destination:service=Topic,name=registerUserTopic">
<depends optional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends>
</mbean>
2.在spring的配|文件中配置JMSlg的具体细节?br />
Q?QJNDI上下文是取得JMS资源的v始位|,因此首先我们要配|JNDI模板Q?br />
<!-- JNDI上下?它是取得JMS资源的v始位|? -->
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<props>
<prop key="java.naming.factory.initial">
org.jnp.interfaces.NamingContextFactory
</prop>
<prop key="java.naming.provider.url">localhost</prop>
<prop key="java.naming.factory.url.pkgs">
org.jnp.interfaces:org.jboss.naming
</prop>
</props>
</property>
</bean>
注意Q此JNDI模板用到了org.jnp.interfaces.NamingContextFactory所以要?JBOSS_HOME%\client下的jbossall-client.jar加到你的目的classpath中?br />
Q?Q配|连接工厂:
<!-- JMSq接工厂 -->
<bean id="jmsConnectionFactory"class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate">
<ref bean="jndiTemplate" />
</property>
<property name="jndiName">
<value>XAConnectionFactory</value>
</property>
</bean>
注意QXAConnectionFactoryq个JNDI名字是在%JBOSS_HOME%server\default\deploy\jms文g夹下的jms-ds.xml中定义的(它是由JBoss指定??br />
Q?Q配|JmsTemplatelg。在例程中我们用JmsTemplate102。同时用defaultDestination属性来指定JMS目标?br />
<!-- JMS模板配置 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate102">
<property name="connectionFactory" ref="jmsConnectionFactory" />
<property name="defaultDestination" ref="destination" />
<property name="pubSubDomain">
<value>true</value>
</property>
<!-- {待消息的时?ms) -->
<property name="receiveTimeout">
<value>30000</value>
</property>
</bean>
注意Q如果用topic-subscribe(主题订阅)模式Q该模板的pubSubDomain属性gؓtrue;若用PToP(点对?模式QpubSubDomain属性gؓfalse或不配置该属性?br />
(4)定义一个JMS目标来发送和接收消息:
<bean id="destination" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate">
<ref bean="jndiTemplate" />
</property>
<property name="jndiName">
<value>topic/registerUserTopic</value>
</property>
</bean>
(5)配置发送者和接收者组?
<!-- 消息发布?-->
<bean id="msgProducer" class="com.boco.jms.MessageProducer">
<property name="jmsTemplate" ref="jmsTemplate" />
</bean>
<!-- 消息接收?-->
<bean id="msgConsumer" class="com.boco.jms.MessageConsumer">
<property name="jmsTemplate" ref="jmsTemplate" />
</bean>
3.相应的类:
(1). User对象?br />
/**
* User.java
* created on Jul 2, 2006
* Copyrights 2006 BOCO,Inc. All rights reserved.
*/
package com.boco.dto;
import java.io.Serializable;
/**
* desc: 用户信息 Bean
* @author qiujy
*/
public class User {
private int id;
private String username;
private String password;
private String email;
public User(){}
//以下为Getter,setterҎ?br />
......
}
(2).消息生者:
/**
* MessageProducer.java
* created on Jul 22, 2006
* Copyrights 2006 BOCO,Inc. All rights reserved.
*/
package com.boco.jms;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import com.boco.dto.User;
/**
* desc:消息生?br />
* @author qiujy
*
*/
public class MessageProducer {
/** JMS模板 */
private JmsTemplate jmsTemplate;
public void setJmsTemplate(JmsTemplate jmsTemplate){
this.jmsTemplate = jmsTemplate;
}
public void sendMessage(final User user){
//调用模板的send来发送消?br />
jmsTemplate.send(new MessageCreator(){
public Message createMessage(Session session) throws JMSException {
//构造一个要发送的消息
MapMessage message = session.createMapMessage();
message.setInt("id", user.getId());
message.setString("username", user.getUsername());
message.setString("password", user.getPassword());
message.setString("email", user.getEmail());
System.out.println("send success!!");
return message;
}
});
}
}
(3).消息消费者:
/**
* MessageConsumer.java
* created on Jul 22, 2006
* Copyrights 2006 BOCO,Inc. All rights reserved.
*/
package com.boco.jms;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import org.springframework.jms.core.JmsTemplate;
import com.boco.dto.User;
/**
* desc:消息消费?br />
* @author qiujy
*
*/
public class MessageConsumer {
/** JMS模板 */
private JmsTemplate jmsTemplate;
public void setJmsTemplate(JmsTemplate jmsTemplate){
this.jmsTemplate = jmsTemplate;
}
public User receiveMessage(){
//参数为Destination的JNDI名字L前面的模式类型标?br />
//MapMessage msg = (MapMessage)jmsTemplate.receive("registerUserQueue");
MapMessage msg = (MapMessage)jmsTemplate.receive("registerUserTopic");
User user = new User();
try {
user.setId(msg.getInt("id"));
user.setUsername(msg.getString("username"));
user.setPassword(msg.getString("password"));
user.setEmail(msg.getString("email"));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return user;
}
}
/**
* TestMsgProducer.java
* created on Jul 22, 2006
* Copyrights 2006 BOCO,Inc. All rights reserved.
*/
package com.boco.jms;
import junit.framework.TestCase;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.boco.dto.User;
/**
* desc:
* @author qiujy
*
*/
public class TestMsgProducer extends TestCase {
private ApplicationContext context;
/**
* @param arg0
*/
public TestMsgProducer(String arg0) {
super(arg0);
context = new ClassPathXmlApplicationContext("applicationContext_jms.xml");
}
/* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception {
super.setUp();
}
/* (non-Javadoc)
* @see junit.framework.TestCase#tearDown()
*/
protected void tearDown() throws Exception {
super.tearDown();
}
/**
* Test method for {@link com.boco.jms.MessageProducer#sendMessage(com.boco.dto.User)}.
*/
public void testSendMessage() {
User user = new User();
user.setId(132);
user.setUsername("JMSTest");
user.setPassword("password");
user.setEmail("support@boco.com.cn");
MessageProducer producer = (MessageProducer)context.getBean("msgProducer");
producer.sendMessage(user);
}
}
/**
* TestMsgConsumer.java
* created on Jul 22, 2006
* Copyrights 2006 BOCO,Inc. All rights reserved.
*/
package com.boco.jms;
import junit.framework.TestCase;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.boco.dto.User;
/**
* desc:
* @author qiujy
*
*/
public class TestMsgConsumer extends TestCase {
private ApplicationContext context;
/**
* @param arg0
*/
public TestMsgConsumer(String arg0) {
super(arg0);
context = new ClassPathXmlApplicationContext("applicationContext_jms.xml");
}
/* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception {
super.setUp();
}
/* (non-Javadoc)
* @see junit.framework.TestCase#tearDown()
*/
protected void tearDown() throws Exception {
super.tearDown();
}
/**
* Test method for {@link com.boco.jms.MessageConsumer#receiveMessage()}.
*/
public void testReceiveMessage() {
MessageConsumer consumer = (MessageConsumer)context.getBean("msgConsumer");
User user = consumer.receiveMessage();
assertNotNull(user);
System.out.println( "id========" + user.getId()
+ "\nname======" + user.getUsername()
+ "\npassword==" + user.getPassword()
+ "\nemail=====" + user.getEmail());
}
}
摘要 : 在很多Y仉目中QJAVA语言常常被用来开发后台服务程序。线E池技术是提高q类E序性能的一个重要手Dc在实践中,该技术已l被q泛的用。本文首?对设计后台服务程序通常需要考虑的问题进行了基本的论qͼ随后介绍了JAVAU程池的原理、用和其他一些相关问题,最后对功能强大的JAVA开放源码线 E池包util.concurrent 在实际编E中的应用进行了详细介绍?br />
关键? JAVAQ线E池Q后台服务程序;util.concurrent
1 引言
在Y仉目开发中Q许多后台服务程序的处理动作程都具有一个相同点Q就是:接受客户端发来的hQ对hq行一些相关的处理Q最后将处理l果q回l客?端。这些请求的来源和方式可能会各不相同Q但是它们常帔R有一个共同点Q数量巨大,处理旉短。这cL务器在实际应用中h较大的普遍性,如web服务 器,短信服务器,DNS服务器等{。因此,研究如何提高此类后台E序的性能Q如何保证服务器的稳定性以及安全性都h重要的实用h倹{?br />
2 后台服务E序设计
2.1 关于设计原型
构徏服务器应用程序的一个简单的模型是:启动一个无限@环,循环里放一个监听线E监听某个地址端口。每当一个请求到辑ְ创徏一个新U程Q然后新U程求服务,监听U程q回l箋监听?br />
单D例如下:
import java.net.*;
public class MyServer extends Thread{
public void run(){
try{
ServerSocket server=null;
Socket clientconnection=null;
server = new ServerSocket(8008);//监听某地址端口?br />
while(true){q入无限循环
clientconnection =server.accept();//收取h
new ServeRequest(clientconnection).start();//启动一个新服务U程q行服务
……
}
}catch(Exception e){
System.err.println("Unable to start serve listen:"+e.getMessage());
e.printStackTrace();
}
}
}
实际上,q只是个单的原型Q如果试N|以q种方式q行的服务器应用E序Q那么这U方法的严重不很明显?br />
首先Qؓ每个h创徏一个新U程的开销很大Qؓ每个h创徏新线E的服务器在创徏和销毁线E上p的时间和消耗的pȝ资源, 往往有时候要比花在处理实际的用户h的时间和资源更多。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后q行垃圾回收。所以提 高服务程序效率的一个手D就是尽可能减少创徏和销毁对象的ơ数。这L合看来,pȝ的性能瓉在于线E的创徏开销?br />
其次Q除了创建和销毁线E的开销之外Q活动的U程也消耗系l资源。在一?JVM 里创建太多的U程可能会导致系l由于过度消耗内存而用完内存或“切换q度”。ؓ了防止资源不I服务器应用程序需要一些办法来限制Ml定时刻q行的处?U程数目Q以防止服务器被“压死”的情况发生。所以在设计后台E序的时候,一般需要提前根据服务器的内存、CPU{硬件情况设定一个线E数量的上限倹{?br />
如果创徏和销毁线E的旉相对于服务时间占用的比例较大Q那末假讑֜一个较短的旉内有成千上万的请求到达,惌一下,服务器的旉和资源将会大量的花在 创徏和销毁线E上Q而真正用于处理请求的旉却相对较,q种情况下,服务器性能瓉在于创建和销毁线E的旉。按照这个模型写一个简单的E序试一?卛_看出Q由于篇q关p,此处略。如果把Q服务时?创徏和销毁线E的旉Q作量服务器性能的一个参敎ͼ那末q个比D大,服务器的性能p高?br />
应此Q解xc问题的实质是量减少创徏和销毁线E的旉Q把服务器的资源可能多地用到处理请求上来,从而发挥多U程的优点(q发Q,避免多线E的~点Q创建和销毁的时空开销Q?br />
U程池ؓU程生命周期开销问题和资源不问题提供了解决Ҏ。通过对多个Q务重用线E,U程创徏的开销被分摊到了多个Q务上。其好处是,因ؓ在请求到达时 U程已经存在Q所以无意中也消除了U程创徏所带来的gq。这P可以立即ؓh服务Q应用E序响应更快。而且Q通过适当地调整线E池中的U程数目Q也 是当请求的数目过某个阈值时Q就强制其它M新到的请求一直等待,直到获得一个线E来处理为止Q从而可以防止资源不?br />
3 JAVAU程池原?br />
3.1 原理以及实现
在实践中Q关于线E池的实现常常有不同的方法,但是它们的基本思\大都是相似的Q服务器预先存放一定数目的“?#8221;的线E,q发E序需要用线E的时候,?服务器取用一条已l创建好的线E(如果U程池ؓI则{待Q,使用该线E对h服务Q用结束后Q该U程q不删除Q而是q回U程池中Q以备复用,q样可以?免对每一个请求都生成和删除线E的昂贵操作?br />
一个比较简单的U程池至应包含U程池管理器、工作线E、Q务队列、Q务接口等部分。其中线E池理器(ThreadPool ManagerQ的作用是创建、销毁ƈ理U程池,工作线E放入线E池中;工作U程是一个可以@环执行Q务的U程Q在没有d时进行等待;d队列的作 用是提供一U缓冲机Ӟ没有处理的d攑֜d队列中;d接口是每个Q务必d现的接口Q主要用来规定Q务的入口、Q务执行完后的收尾工作、Q务的?行状态等Q工作线E通过该接口调度Q务的执行。下面的代码实现了创Z个线E池Q?br />
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
4.2 框架与结?br />
下面让我们来看看util.concurrent的框架结构。关于这个工具包概述的e文原版链接地址是http: //gee.cs.oswego.edu/dl/cpjslides/util.pdf。该工具包主要包括三大部分:同步、通道和线E池执行器。第一部分 主要是用来定刉Q资源管理,其他的同步用途;通道则主要是为缓冲和队列服务的;U程池执行器则提供了一l完善的复杂的线E池实现?br />
--主要的结构如下图所C?br />
4.2.1 Sync
acquire/release协议的主要接?br />
- 用来定制锁,资源理Q其他的同步用?br />
- 高层抽象接口
- 没有区分不同的加锁用?br />
实现
-Mutex, ReentrantLock, Latch, CountDown,Semaphore, WaiterPreferenceSemaphore, FIFOSemaphore, PrioritySemaphore
q有Q有几个单的实现Q例如ObservableSync, LayeredSync
举例Q如果我们要在程序中获得一独占锁,可以用如下简单方式:
try {
lock.acquire();
try {
action();
}
finally {
lock.release();
}
}catchQException eQ{
}
E序?使用lock对象的acquire()Ҏ获得一独占锁,然后执行您的操作Q锁用完后,使用release()Ҏ释放之即可。呵呵,单吧Q想 想看Q如果您亲自撰写独占锁,大概会考虑到哪些问题?如果关键的锁得不到怎末办?用v来是不是会复杂很多?而现在,以往的很多细节和Ҏ异常情况在这里都 无需多考虑Q您可以把_֊花在解决您的应用问题上去?br />
4.2.2 通道(Channel)
为缓Ԍ队列{服务的L?br />
具体实现
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();
}
}
在后台服务器中,~冲和队列都是最常用到的。试惻I如果Ҏ有远端的h不排个队列,让它们一拥而上的去争夺cpu、内存、资源,那服务器瞬间不当掉才怪。而在q里Q成熟的队列和缓冲实现已l提供,您只需要对其进行正初始化q用即可,大大~短了开发时间?br />
4.2.3执行?Executor)
Executor是这里最重要、也是我们往往最l写E序要用到的Q下面重点对其进行介l?br />
cMU程的类的主接口
- U程?br />
- 轻量U运行框?br />
- 可以定制调度法
只需要支持execute(Runnable r)
- 同Thread.startcM
实现
- PooledExecutor, ThreadedExecutor, QueuedExecutor, FJTaskRunnerGroup
PooledExecutorQ线E池执行器)是个最常用到的c,以它ZQ?br />
可修改得属性如下:
- d队列的类?br />
- 最大线E数
- 最线E数
- 预热(预分?和立?分配)U程
- 保持z跃直到工作U程l束
-- 以后如果需要可能被一个新的代?br />
- 饱和(Saturation)协议
-- dQ丢弃,生者运行,{等
可不要小看上面这数条属性,对这些属性的讄完全可以{同于您自己撰写的线E池的成百上千行代码。下面以W者撰写过得一个GIS服务器ؓ例:
该GIS服务器是一个典型的“hQ服?#8221;cd的服务器Q遵循后端程序设计的一般框架。首先对所有的h按照先来先服务排入一个请求队列,如果瞬间到达?h过了请求队列的定wQ则溢出的h转移至一个旉列。如果旉列也排满了,则对以后辑ֈ的请求给予一?#8220;服务器忙”的提C后其单抛弃。这 个就够忙zM늚了?br />
然后Q结合链表结构实C个线E池Q给池一个初始容量。如果该池满Q以x2的策略将池的定w动态增加一倍,依此cLQ直到ȝE数服务辑ֈpȝ能力上限Q?之后U程池容量不在增加,所有请求将{待一个空余的q回U程。每从池中得C个线E,该线E就开始最hq行GIS信息的服务,如取坐标、取地图Q等{?服务完成后,该线E返回线E池l箋求队列离地后l请求服务,周而复始。当时用矢量链表来暂存请求,用wait()?notify() ?synchronized{原语结合矢量链表实现线E池QdU?00行程序,而且在运行时间较长的情况下服务器不稳定,U程池被取用的线E有异常消失?情况发生。而用util.concurrent相关cM后,仅用了几十行E序完成了相同的工作而且服务器运行稳定,U程池没有丢qE的情况发生。由 此可见util.concurrent包极大的提高了开发效率,为项目节省了大量的时间?br />
使用PooledExecutor例子
import java.net.*;
/**
*
*
*
*
* @author not attributable
* @version 1.0
*/
public class MainServer {
//初始化常?br />
public static final int MAX_CLIENT=100; //pȝ最大同时服务客h
//初始化线E池
public static final PooledExecutor pool =
new PooledExecutor(new BoundedBuffer(10), MAX_CLIENT); //chanel定w?0,
//在这里ؓU程池初始化了一?br />
//长度?0的Q务缓冲队列?br />
public MainServer() {
//讄U程池运行参?br />
pool.setMinimumPoolSize(5); //讄U程池初始容量ؓ5个线E?br />
pool.discardOldestWhenBlocked();//对于出队列的请求,使用了抛弃策略?br />
pool.createThreads(2); //在线E池启动的时候,初始化了h一定生命周期的2?#8220;?#8221;U程
}
public static void main(String[] args) {
MainServer MainServer1 = new MainServer();
new HTTPListener().start();//启动服务器监听和处理U程
new manageServer().start();//启动理U程
}
}
cHTTPListener
import java.net.*;
/**
*
*
*
*
* @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);//服务套接字监听某地址端口?br />
while(true){//无限循环
clientconnection =server.accept();
System.out.println("Client connected in!");
//使用U程池启动服?br />
MainServer.pool.execute(new HTTPRequest(clientconnection));//如果收到一个请求,则从U程池中取一个线E进行服务,d完成后,该线E自动返q线E池
}
}catch(Exception e){
System.err.println("Unable to start serve listen:"+e.getMessage());
e.printStackTrace();
}
}
}
关于util.concurrent工具包就有选择的介l到q,更详l的信息可以阅读q些java源代码的API文档。Doug Lea是个很具?#8220;open”_的作者,他将util.concurrent工具包的java源代码全部公布出来,有兴的读者可以下载这些源代码q细 l品呟?span class="Apple-converted-space">
5 l束?br />
以上内容介绍了线E池基本原理以及设计后台服务E序应考虑到的问题Qƈl合实例详细介绍了重要的多线E开发工具包util.concurrent的构架和使用。结合用已有完善的开发包Q后端服务程序的开发周期将大大~短Q同时程序性能也有了保障?br />
一、什么是Java事务
通常的观念认为,事务仅与数据库相兟?/span>
事务必须服从ISO/IEC所制定?/span>ACID原则?/span>
ACID是原子性(atomicityQ、一致性(consistencyQ、隔L(isolationQ和持久性(durabilityQ的~写?/span>
事务的原子性表CZ务执行过E中的Q何失败都导致事务所做的M修改失效?/span>
一致性表C当事务执行p|Ӟ所有被该事务媄响的数据都应该恢复到事务执行前的状态?/span>
隔离性表C在事务执行q程中对数据的修改,在事务提交之前对其他事务不可见?/span>
持久性表C已提交的数据在事务执行p|Ӟ数据的状态都应该正确?/span>
通俗的理解,事务是一l原子操作单元,从数据库角度_是一l?/span>SQL指oQ要么全部执行成功,若因为某个原因其中一条指令执行有错误Q则撤销先前执行q的所有指令。更{的说就是:要么全部执行成功Q要么撤销不执行?/span>
既然事务的概念从数据库而来Q那Java事务是什么?之间有什么联p? 实际上,一?/span>Java应用pȝQ如果要操作数据库,则通过JDBC来实现的。增加、修攏V删除都是通过相应Ҏ间接来实现的Q事务的控制也相应{UdJavaE序代码中。因此,数据库操作的事务习惯上就UCؓJava事务?/span>
二、ؓ什么需要事?/span>
事务是ؓ解决数据安全操作提出的,事务控制实际上就是控制数据的安全讉K。具一个简单例子:比如银行转帐业务Q̎?/span>A要将自己账户上的1000 元{?/span>B账户下面Q?/span>A账户余额首先要减?/span>1000元,然后B账户要增?/span>1000元。假如在中间|络出现了问题,A账户减去1000元已l结束,B因ؓ|络中断而操作失败,那么整个业务p|Q必d出控Ӟ要求A账户转帐业务撤销。这才能保证业务的正性,完成q个操走需要事务,?/span>A账户资金减少?/span>B账户资金增加方到一个事务里面,要么全部执行成功Q要么操作全部撤销Q这样就保持了数据的安全性?/span>
三?/span>Java事务的类?/span>
Java 事务的类型有三种Q?/span>JDBC事务?/span>JTA(Java Transaction API)事务、容器事务?/span>
1?/span>JDBC事务
JDBC 事务是用 Connection 对象控制的?/span>JDBC Connection 接口( java.sql.Connection )提供了两U事务模式:自动提交和手工提交?/span> java.sql.Connection 提供了以下控制事务的ҎQ?/span>
public void setAutoCommit(boolean)
public boolean getAutoCommit()
public void commit()
public void rollback()
使用 JDBC 事务界定Ӟ您可以将多个 SQL 语句l合C个事务中?/span>JDBC 事务的一个缺Ҏ事务的范围局限于一个数据库q接。一?/span> JDBC 事务不能跨越多个数据库?/span>
2?/span>JTA(Java Transaction API)事务
JTA 是一U高层的Q与实现无关的,与协议无关的APIQ应用程序和应用服务器可以?/span>JTA来访问事务?/span>
JTA允许应用E序执行分布式事务处?/span>--在两个或多个|络计算源上讉Kq且更新数据Q这些数据可以分布在多个数据库上?/span>JDBC驱动E序?/span>JTA支持极大地增Z数据讉K能力?/span>
如果计划?/span> JTA 界定事务Q那么就需要有一个实?/span> javax.sql.XADataSource ?/span> javax.sql.XAConnection ?/span> javax.sql.XAResource接口?/span> JDBC 驱动E序。一个实Cq些接口的驱动程序将可以参与 JTA 事务。一?/span> XADataSource 对象是一?/span>XAConnection 对象的工厂?/span> XAConnection s 是参?/span> JTA 事务?/span> JDBC q接?/span>
您将需要用应用服务器的理工具讄 XADataSource 。从应用服务器和 JDBC 驱动E序的文档中可以了解到相关的指导?/span>
J2EE 应用E序?/span> JNDI 查询数据源。一旦应用程序找C数据源对象,它就调用 javax.sql.DataSource.getConnection() 以获得到数据库的q接?/span>
XA q接与非 XA q接不同。一定要C XA q接参与?/span> JTA 事务。这意味着 XA q接不支?/span> JDBC 的自动提交功能。同Ӟ应用E序一定不要对 XA q接调用 java.sql.Connection.commit() 或?/span> java.sql.Connection.rollback() 。相反,应用E序应该使用 UserTransaction.begin()?/span> UserTransaction.commit() ?/span> serTransaction.rollback() ?/span>
3、容器事?/span>
容器事务主要?/span>J2EE应用服务器提供的Q容器事务大多是ZJTA完成Q这是一个基?/span>JNDI的,相当复杂?/span>API实现。相对编码实?/span>JTA 事务理Q我们可以通过EJB容器提供的容器事务管理机ӞCMTQ完成同一个功能,q项功能?/span>J2EE应用服务器提供。这使得我们可以单的指定哪个方法加入事务,一旦指定,容器负责事务管理Q务。这是我们土建的解决方式Q因为通过q种方式我们可以事务代码排除在逻辑~码之外Q同时将所有困难交l?/span> J2EE容器去解冟뀂?/span>EJB CMT的另外一个好处就是程序员无需兛_JTA API的编码,不过Q理Z我们必须使用EJB?/span>
四、三U事务差?/span>
1?/span>JDBC事务控制的局限性在一个数据库q接内,但是其用简单?/span>
2?/span>JTA事务的功能强大,事务可以跨越多个数据库或多个DAOQ用也比较复杂?/span>
3、容器事务,主要指的?/span>J2EE应用服务器提供的事务理Q局限于EJB应用使用?/span>
JTA
Java事务APIQJTAQJava Transaction APIQ和它的同胞Java事务服务(JTSQJava Transaction Service)QؓJ2EEq_提供了分布式事务服务。一个分布式事务Qdistributed transactionQ包括一个事务管理器Qtransaction managerQ和一个或多个资源理?resource manager)。一个资源管理器Qresource managerQ是Lcd的持久化数据存储。事务管理器Qtransaction managerQ承担着所有事务参与单元者的怺通讯的责仅R下图显CZ事务理器和资源理的间的关pR?/span>
JTA事务比JDBC事务更强大。一个JTA事务可以有多个参与者,而一个JDBC事务则被限定在一个单一的数据库q接?/span>下列M个Javaq_的组仉可以参与C个JTA事务中:
JDBCq接
DAO
DAO是Data Access Object数据讉K接口Q数据访问:思义是与数据库打交道。夹在业务逻辑与数据库资源中间。对数据库进行CURDQ增删查Ҏ作)?/span>
在核?/span>J2EE模式中是q样介绍DAO模式的:Z建立一个健壮的J2EE应用Q应该将所有对数据源的讉K操作抽象装在一个公共API中。用E序设计的语a来说Q就是徏立一个接口,接口中定义了此应用程序中会用到的所有事务方法。在q个应用E序中,当需要和数据源进行交互的时候则使用q个接口Qƈ且编写一个单独的cL实现q个接口在逻辑上对应这个特定的数据存储?/span>
JDBC
JDBCQJava Data Base Connectivity,java数据库连接)是一U用于执行SQL语句的Java APIQ可以ؓ多种关系数据库提供统一讉KQ它׃l用Java语言~写的类和接口组成。JDBC为工?数据库开发h员提供了一个标准的APIQ据此可以构建更高的工具和接口Q数据库开发h员能够用U?Java API ~写数据库应用程序,同时QJDBC也是个商标名?/span>
有了JDBCQ向各种关系数据发送SQL语句是一件很Ҏ的事。换a之,有了JDBC APIQ就不必问Sybase数据库专门写一个程序,问Oracle数据库又专门写一个程序,或ؓ讉KInformix数据库又~写另一个程序等{,E序员只需用JDBC API写一个程序就够了Q它可向相应数据库发送SQL调用。同ӞJava语言和JDBCl合h使程序员不必Z同的q_~写不同的应用程序,只须写一遍程序就可以让它在Q何^Cq行Q这也是Java语言“~写一ơ,处处q行”的优ѝ?/span>
单地_JDBC 可做三g事:与数据库建立q接、发?操作数据库的语句q处理结果?/span>
JDO
JDO(Java Data Object )是一个JAVA用于存取某种数据仓库中的对象的标准化API。JDO提供了透明的对象存储,因此对开发h员来_存储数据对象完全不需要额外的代码Q如JDBC API的用)。这些繁琐的例行工作已经转移到JDO产品提供商n上,使开发h员解脱出来,从而集中时间和_֊在业务逻辑上。另外,JDO很灵z,因ؓ它可以在M数据底层上运行。JDBC只是面向关系数据库(RDBMSQJDO更通用Q提供到M数据底层的存储功能,比如关系数据库、文件、XML以及对象数据库(ODBMSQ等{,使得应用可移植性更强?/span>
JNDI
英文全称?Java Naming and Directory Interface
术语解释Q一l帮助做多个命名和目录服务接口的API?/span>
JNDI(Java Naming and Directory Interface)是SUN公司提供的一U标准的Java命名pȝ接口QJNDI提供l一的客LAPIQ通过不同的访问提供者接口JNDI SPI的实玎ͼq理者将JNDI API映射为特定的命名服务和目录系l,使得Java应用E序可以和这些命名服务和目录服务之间q行交互。集JNDI实现了高可靠性JNDI[8]Q通过服务器的集群Q保证了JNDI的负载^衡和错误恢复。在全局׃n的方式下Q集中的一个应用服务器保证本地JNDI树的独立性,q拥有全局的JNDI树。每个应用服务器在把部v的服务对象绑定到自己本地的JNDI树的同时Q还l定C个共享的全局JNDI树,实现全局JNDI和自wJNDI的联pR?/span>
JNDI与JDBC
JNDI提供了一U统一的方式,可以用在|络上查扑֒讉K服务。通过指定一个资源名Uͼ该名U对应于数据库或命名服务中的一个记录,同时q回数据库连接徏立所必须的信息?/span>
JNDI主要有两部分l成Q应用程序编辑接口和服务供应商接口。应用程序编E接口提供了Java应用E序讉K各种命名和目录服务的功能Q服务供应商接口提供了Q意一U服务的供应商用的功能?/span>