项目里很多地方用到了多线程,最近我负责的一个功能,也用到了多线程,学习总结一下。
最近在复习操作系统和计算机网络的知识,网络上两台主机的通信,确切来说,是两个进程的通信。进程,运行着的程序。线程,更轻量级,一个进程可以包含一个或多个线程。多线程,同一时间,多个线程在执行。在处理某些业务时,可能业务比较复杂,后续需要及时做出好几种处理,但不要求处理的顺序,这就可以用多线程。
在Java中,多线程的实现,一般有2种实现方法。一是继承Thread类,二是实现Runnable接口。更推荐使用实现Runnable接口,使用这种方式创建线程可以处理同一个资源,重写run()方法,可以实现自己想要的效果。
下面将一些本次用到的多线程:
1、引用jar包,配置application.xml文件,因为我们这个项目用的是spring+Struts2,jar包已经放好了。
<!-- 配置线程池 -->
<bean id ="taskExecutor" name ="taskExecutor" class ="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" >
<!-- 线程池维护线程的最少数量 -->
<property name ="corePoolSize" value ="5" />
<!-- 线程池维护线程所允许的空闲时间 -->
<property name ="keepAliveSeconds" value ="30000" />
<!-- 线程池维护线程的最大数量 -->
<property name ="maxPoolSize" value ="1000" />
<!-- 线程池所使用的缓冲队列 -->
<property name ="queueCapacity" value ="200" />
</bean>
<!-- 配置线程池 结束-->
2、在自己的类里注入线程池对象。在这里我犯了个低级错误,我用spring注解注入,启动时没问题,运行时报空指针异常。由于项目中用Struts做转发跳转,spring扫描没有做配置,运行时找不到bean,需要自己注入。
private static TaskExecutor threadPool = SpringUtils.getBean("taskExecutor");
package com.ustcsoft.framework.util;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
/**
* 获取spring bean的工具类
*/
public final class SpringUtils implements BeanFactoryPostProcessor {
private static ConfigurableListableBeanFactory beanFactory; // Spring应用上下文环境
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
SpringUtils.beanFactory = beanFactory;
}
/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws org.springframework.beans.BeansException
*
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException {
return (T) beanFactory.getBean(name);
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
*
* @param name
* @return boolean
*/
public static boolean containsBean(String name) {
return beanFactory.containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
*
* @param name
* @return boolean
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
return beanFactory.isSingleton(name);
}
/**
* @param name
* @return Class 注册对象的类型
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
return beanFactory.getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
*
* @param name
* @return
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
return beanFactory.getAliases(name);
}
}
3、创建实体类,实现Runnable接口,重写run()方法。在run()方法里,可以实现自己的业务。我这儿是一个邮件发送的业务,拼接数据,发送邮件。
package com.ustcsoft.business.service;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.log4j.Logger;
import com.ustcsoft.framework.application.SystemConstants;
import com.ustcsoft.framework.util.MailUtil;
public class SendMailThread implements Runnable {
private String contacts;
private String payAccountName;
private String mobile;
private static final Logger logger = Logger.getLogger(SendMailThread.class);
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static final String smtp = SystemConstants.SMTP;
private static final String sender = SystemConstants.SENDER;
private static final String receiver = SystemConstants.RECEIVER;
private static final String password = SystemConstants.PASSWORD;
@Override
public void run() {
String message = "";
StringBuffer sb = new StringBuffer();
sb.append("您有一条借条申请订单待处理。\n");
sb.append("订单时间:"+ sdf.format(new Date())+"\n");
sb.append("账户名:"+payAccountName+"\n");
sb.append("姓名:"+contacts+"\n");
sb.append("手机号:"+mobile+"\n");
sb.append("订单来源:微信");
message = sb.toString();
String subject = "借条申请订单通知(账户"+ payAccountName +")";
logger.info("smtp="+smtp+",sender="+sender+",receiver="+receiver+",password="+password+",subject="+subject+",message="+message);
boolean flag = MailUtil.send(smtp, sender, receiver, password, subject,message);
logger.info("邮件发送结果:"+flag);
logger.info("===================SendMailThread执行成功======================");
}
public SendMailThread(String contacts,String payAccountName,String mobile){
this.contacts = contacts;
this.payAccountName = payAccountName;
this.mobile = mobile;
}
public String getContacts() {
return contacts;
}
public void setContacts(String contacts) {
this.contacts = contacts;
}
public String getPayAccountName() {
return payAccountName;
}
public void setPayAccountName(String payAccountName) {
this.payAccountName = payAccountName;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
}
4、在自己的方法里,创建线程,用线程池处理。
/**
* 借条开户申请确认
* 首先调用接口发起借条开通申请,成功后多线程处理后续业务
* 调用接口,进行签约代扣申请
* 调用接口,进行线签留存证明
* 调用接口,给运营人员发送邮件
*/
public String confirmOnlineSign(){
onlineSign = (OnlineSigningInfo) session.getAttribute("onlineSign");
onlineSign.setAppInterface("0");//表示请求来自移动端
onlineSign.setPayAccountNo(IDGenerate.genID());
//借条开户申请,向借条系统发出申请
String urlString = SystemConstants.wangTingUrl + "do/wx/hydApply";
Map<String, Object> requestbody = HTTPUtil.beanToMap(onlineSign);;
log.info("map格式请求参数:"+requestbody);
String string = HTTPUtil.postByHttpwithMap(urlString, requestbody );
if(StringUtil.isNotEmpty(string)){
JSONObject jsonObject = JSONObject.fromObject(string);
String success = jsonObject.getString("success");
if("true".equals(success)){
//申请借条后,账户代扣签约申请
HytLoadRequestVO hytLoadRequestVO = new HytLoadRequestVO();
hytLoadRequestVO.setBusinessIds(onlineSign.getBusinessIds());
hytLoadRequestVO.setChannel("4");//渠道:微信
hytLoadRequestVO.setMobile(onlineSign.getMobile());
hytLoadRequestVO.setPayAccountName(onlineSign.getPayAccountName());
hytLoadRequestVO.setPayAccountNo(onlineSign.getPayAccountNo());
hytLoadRequestVO.setUserId(onlineSign.getUserId());
hytLoadRequestVO.setAppInterface(onlineSign.getAppInterface());
HytAccountLoanSignThread hytLoadSignThread = new HytAccountLoanSignThread(hytLoadRequestVO);
threadPool.execute(hytLoadSignThread );
//申请借条后,调用线签接口,留存证据
OnlineSignThread onlineSignThread = new OnlineSignThread(onlineSign);
threadPool.execute(onlineSignThread);
//借条申请后,发送邮件给运营人员,通知运营人员处理订单
SendMailThread sendMailThread = new SendMailThread(onlineSign.getContacts(),onlineSign.getPayAccountName(),onlineSign.getMobile());
threadPool.execute(sendMailThread);
//借条申请后,跳往待审核页面,展示给用户
return "applicationAuditing";
}
}
return "{\"success\":false}";
}
只需要以上几步,就可以简单实现线程池管理多线程,实现多线程编程。其余的,线程状态,线程同步等,本次暂且不说。