多线程是java入门的必修课程,然而到了接触J2EE时,这份功课就还给了老师了,至少本人是这样的,呵呵.不过等到用到的时候,我还是能想起大概,再加上度娘帮忙,就能重拾回来了,这里我插播一个小故事,是我自身的亲身经历,希望给同道小生有所帮助.以前在做学生的时候,学习java并不是那么用心,有些东西只是知其一不知其二,很多知识点在脑子里有点印象卻不是很深刻,记得有一次我我去一家公司面试,面试官就问我,线程这块,你熟么?作为面试者,谁会说不熟啊,虽然我真的不太熟,但是我当场就回答还行,然后就闹笑话了,面试官问,线程锁有什么用?但是当时我觉得这问题很简单,很激动和紧张的卻一时脑抽了,回答道:为了确保"线性"安全的,然后就没有然后了!线性?.......呃,我还觉得答对的,后来才发觉自己错了,哎....希望大家引以为戒,虽然身为程序员,无需对概念的东西咬文嚼字,但是在别人面前,还是容不得错一丝一毫的犯错,毕竟出来给工作之后,很多细节就决定你的前途!
好了话不多少,回归正题,今天我想说的是在web项目,也就是在J2EE里运用多线程服务,我也是在项目有需求上才注意到这块上,事前做了比较多的功课,毕竟之前也没用过嘛,特意记下和大家分享自身的做法和想法.这里我说下我的项目环境是ssh的,其他框架也类似啦
既然是多线程,就少不了主角线程类,主角上场
package com.smartsoft.thread;
import java.util.ArrayList;
import java.util.List;
import net.sf.json.JSONObject;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import com.google.gson.Gson;
import com.smartsoft.common.Constants;
import com.smartsoft.model.Gameset;
import com.smartsoft.service.GamesetService;
import com.smartsoft.task.SocketResultModel;
import com.smartsoft.util.SystemConfig;
/**
* 主线程
*
*
*/
public class NewGameSetThread extends Thread {
private Logger logger = Logger.getLogger(NewGameSetThread.class);
private List<Gameset> tempList = new ArrayList<Gameset>();
private long sleepTime = 3 * 1000;
private Gson gson = new Gson();
public NewGameSetThread() {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
@Autowired
private GamesetService gamesetService;
public void run() {
// 防止多個Thread同時進行數據庫操作造成資源消耗過多
try {
Thread.currentThread();
Thread.sleep(5 * 1000);
} catch (InterruptedException e1) {
logger.error(e1);
}
logger.info("NewGameSet Thread Is Started。。。");
List<Gameset> standbyList = new ArrayList<Gameset>();
// 循环执行任务
while (true) {
try {
boolean bool = false;
// 获取消息任务
getTask(standbyList);
if (!standbyList.isEmpty()) {// 如果待推送的信息集合中有數據
for (int i = 0; i < standbyList.size(); i++) {
bool = false;
try {
Gameset task = standbyList.get(i);
// Push report
bool = httpPush(task);
// update message
updateStatus(task, bool);
} catch (Exception e) {
logger.error(e.toString(), e);
continue;
} finally {
try {
super.finalize();
} catch (Throwable e) {
logger.error(e.toString(), e);
}
}
}
}
standbyList.clear();
Thread.sleep(sleepTime);
} catch (Exception e) {
logger.error(e.toString(), e);
continue;
}
}
}
private void updateStatus(Gameset task, boolean bool) {
if (bool) {
task.setPush_status("1");
} else {
task.setPush_status("0");
}
gamesetService.update(task);
logger.info("NewGameSet Thread Update Push Status id:" + task.getId());
}
private boolean httpPush(Gameset task) {
PushModel pushModel = new PushModel("all", task);
String json = gson.toJson(pushModel);
logger.info("NewGameSet Thread Push Message: \n" + json);
int result = sendHttpRequestToSocketServerWithJson(json);
logger.info("NewGameSet Thread id : " + task.getId() + " Push Result: " + (result == 1 ? "success" : "failure"));
return result == 1 ? true : false;
}
/**
* httpClient
*
* @param reqStr
* @param urlConfig
* @param contentType
* @return
*/
private static int sendHttpRequest(String reqStr, String urlConfig, String contentType) {
try {
PostMethod postMethod = new PostMethod(urlConfig);
RequestEntity requestEntity = new StringRequestEntity(reqStr, contentType, Constants.CONTENT_ENCODING_UTF8);
postMethod.setRequestEntity(requestEntity);
HttpClient httpClient = new HttpClient();
httpClient.executeMethod(postMethod);
SocketResultModel socketResult = (SocketResultModel) JSONObject.toBean(JSONObject.fromObject(postMethod.getResponseBodyAsString()), SocketResultModel.class);
Integer httpStatus = socketResult.getCode();
return httpStatus == 1 ? 1 : 0;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
private static int sendHttpRequestToSocketServerWithJson(String reqStr) {
String url = SystemConfig.get("socket.host") + ":" + SystemConfig.get("socket.port");
return sendHttpRequest(reqStr, url, Constants.HTTP_CONTENT_TYPE_APPLICATION_JSON);
}
/**
* 获取消息任务
*
* @param standbyList
*/
private void getTask(List<Gameset> standbyList) {
// 获取高优先级发送任务
tempList = new ArrayList<Gameset>(ThreadVariables.newGameSetList);
ThreadVariables.newGameSetList.removeAll(tempList);
standbyList.addAll(tempList);
// 清空临时列表
tempList.clear();
}
public static void main(String[] args) {
Gson gson = new Gson();
Gameset task = new Gameset();
task.setId(1L);
task.setGame_no(8);
PushModel pushModel = new PushModel("all", task);
String json = gson.toJson(pushModel);
System.out.println(json);
}
}
需求是不定时的去把消息推送到另一台服务器上,但是消息是不确定什么时候来的,来多少的,这种东西就不好用定时器去实现了,只能用线程监听去跑了
业务逻辑很简单,我会在别的地方把消息添加到集合,又线程去不断监听集合里是否有消息,有就立马推送出去,没有就不做任何事情
package com.smartsoft.thread;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.smartsoft.model.Gameset;
public class ThreadVariables {
/**
* callback 任務列表
*/
public static List<Gameset> newGameSetList = Collections.synchronizedList(new ArrayList<Gameset>());
public static void addnewGameSetList(Gameset gameset){
newGameSetList.add(gameset);
}
}
这个集合当然是要自带线程安全的啦,呵呵
主角已定,当然是剧本啦,我们需要制定主角出场的时间,这里我用到servlet去启动线程,具体如下
package com.smartsoft.thread;
import java.util.Map;
public class ServiceThreadMap {
Map<String, Thread> serviceMap;
public ServiceThreadMap(Map<String, Thread> serviceMap) {
super();
this.serviceMap = serviceMap;
}
public Map<String, Thread> getServiceMap() {
return serviceMap;
}
}
package com.smartsoft.servlet;
import java.util.Iterator;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.smartsoft.thread.ServiceThreadMap;
/**
* 啟用已經註冊的線程
* @author Dan
*
*/
public class BootStrapper extends HttpServlet{
private static final long serialVersionUID = 1L;
private WebApplicationContext webApp;
private ServiceThreadMap serviceThreadMap;
/* (non-Javadoc)
* @see javax.servlet.GenericServlet#destroy()
*/
@SuppressWarnings("deprecation")
@Override
public void destroy() {
// TODO Auto-generated method stub
Iterator<Thread> service = serviceThreadMap.getServiceMap().values().iterator();
while(service.hasNext()){
service.next().stop();
}
}
/* (non-Javadoc)
* @see javax.servlet.GenericServlet#init()
*/
@Override
public void init() throws ServletException {
// TODO Auto-generated method stub
//super.init();
//得到WebApplicationContext對象
webApp = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
// start send message thread
serviceThreadMap = (ServiceThreadMap) webApp.getBean("serviceMap");//得到service-spring.xml中註冊的線程列表
Iterator<Thread> service = serviceThreadMap.getServiceMap().values().iterator();//得到線程列表中的線程迭代器
/*
* 啟動列表中的每一個線程
*/
while(service.hasNext()){
System.out.println("---------------------------------------------------");
service.next().start();
}
}
}
需要注意的是:这里是写成一个map集合,因为可以扩展同时启动多个线程
在spring的xml里注册线程类
<bean id="newGameSetThread" class="com.smartsoft.thread.NewGameSetThread">
</bean>
<!-- 线程注册 -->
<bean id="serviceMap" class="com.smartsoft.thread.ServiceThreadMap">
<constructor-arg index="0">
<map>
<!-- Dan -->
<entry key="newGameSetThread" value-ref="newGameSetThread"/>
</map>
</constructor-arg>
</bean>
这里只有一个线程类启动,如有多个可以在这里添加
最后在web.xml随项目启动启动servlet
<servlet>
<servlet-name>bootstrapper</servlet-name>
<servlet-class>com.smartsoft.servlet.BootStrapper</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
这样就可以在web启动线程服务了,还是蛮简单的.哈哈,毕竟我也是站在巨人的肩膀上才能完成的