一、创建线程的三种方式
继承Thread,重写run方法
实现Runnable接口,重新run方法
实现Callable接口,重写call方法
二、Callable接口与Runnable接口的对比
Callable接口实际上是属于Executor框架中的功能类,Callable接口与Runnable接口的功能类似,但提供了比Runnable更加强大的功能。
Callable可以在任务结束的时候提供一个返回值,Runnable无法提供这个功能
Callable的call方法分可以抛出异常,而Runnable的run方法不能抛出异常。
三、线程池
从java5开始,新增了一个Executors工厂类来产生线程池,该工厂有几个静态工厂方法穿件线程池,本文使用其中之一的静态工厂方法创建线程池。newFixedThreadPool(int nThreads);创建一个可重用的,具有固定线程数的线程池。
四、Callable,线程池,AtomicBoolean,SpringMVC整合应用
下面第一个java代码类BatProcessHandler.java这个类的主要作用是把需要批量处理的数据从数据库中查询出来,batProcessBO.qryBatDtlListNeedProcess(QryDtlNum);在这里我们每次查询2000条数据。然后把这些数据的处理
交给线程,BatProcessWorker是我们new的一个已实现Callable接口的实现类。
/**
* 批量处理
* @author bshhe
*/
@Component(“batProcessHandler”)
@Scope(“prototype”)
public class BatProcessHandler implements BatHandler {
private static final Logger LOGGER = LoggerFactory
.getLogger(RefundBatProcessHandler.class);
/** 查询数量 */
private int QryDtlNum = 2000;
/**
* 批量明细处理BO
*/
@Autowired
private BatProcessBO batProcessBO;
/**
* 处理批量明细记录
*/
@Override
public void execute() {
List<Map<String,Object>> needList = null;
int total = 0;
try {
/**
* 查询需要处理的明细
*/
needList = this.batProcessBO.qryBatDtlListNeedProcess(QryDtlNum);
if (CommonUtils.isEmpty(needList)) {
//没有记录需要处理,退出
return;
}
total = needList.size();
//需要处理记录总数total
this.processForOne(needList);
System.out.println("----------------------进入线程模式");
} catch (Exception e) {
LOGGER.error("处理明细错误", e);
return;
}
}
/**
* 线程模式
* @param list
*/
private void processForOne(List<Map<String,Object>> needList){
int threadnum = 0;
ArrayList<Callable<Object>> callers = new ArrayList<Callable<Object>>();
BatProcessWorker t=null;
for(Map<String,Object> o:needList){
try {
String nm = Thread.currentThread().getName()+threadnum;
t=new BatProcessWorker(nm, o);
callers.add(t);
threadnum++;
} catch (Exception e) {
LOGGER.error("处理失败", e);
}
}
ThreadWorkFactory.submit(callers);
}
下面是一个java代码BatProcessWorker.java这个类是Callable接口的实现类,里面的call()方法里面含有单笔处理和批量处理,本次只是用到了单笔处理。
/**
* 批量工作子线程
* @author bshhe
*/
public class BatProcessWorker implements Callable<Object> {
private String workId;
private List<Map<String,Object>> needList;
private BatProcessDtlService service;
private BatProcessBO batProcessBO;
private Map<String,Object> needData;
private boolean isOne = false;
/**
* 批量构造方法
* @param workId
* @param needList
*/
public RefundBatProcessWorker(String workId,List<Map<String,Object>> needList){
this.workId = workId;
this.needList = needList;
service = SpringUtil.getBean("BatProcessDtlService",BatProcessDtlService.class);
batProcessBO = SpringUtil.getBean("batProcessBO", BatProcessBO.class);
}
/**
* 构造方法
* @param workId
* @param data
*/
public RefundBatProcessWorker(String workId,Map<String,Object> data){
this.workId = workId;
service = SpringUtil.getBean("BatProcessDtlService",BatProcessDtlService.class);
batProcessBO = SpringUtil.getBean("batProcessBO", BatProcessBO.class);
isOne = true;
this.needData = data;
}
@Override
public Object call(){
if(isOne){
this.done(needData);
return null;
}else{
for(Map<String,Object> m:needList){
this.done(m);
}
return null;
}
}
/**
* 单笔处理
* @param m
*/
private void done(Map<String,Object> m){
String lockId = null;
String ori_trx_date=(String)m.get("ORI_TRX_DTM");
String transKey=null;
transKey=ori_trx_date.substring(0,8);
}
try{
/**
* 1)加锁
*/
lockId = batProcessBO.lockProcessByTransKey(transKey);//这里的transKey作为一个A表的主键,此bo的lockProcessByTransKey这个方法就是给此表插入transKey,插入成功后返回transKey
}catch(Exception e){
//加锁失败
return;
}
try{
/**
* 这一段是处理数据的业务流程,省略操作service这部分代码
*/
}catch(Exception e){
}finally{
//释放锁
batProcessBO.unlockProcess(lockId);//删除A表里的主键为lockId的记录
}
}
}
下面是一个java代码ThreadWorkFactory类,此类是对线程池的创建,销毁,提交线程的工具类,里面使用AtomicBoolean类的作用是防止线程池被多次初始化,后续将会详细讲解AtomicBoolean在此是怎么运用的。
/**
* 线程池
* @author bshhe
*/
public class ThreadWorkFactory {
private static ExecutorService ES = null;
private static AtomicBoolean ab = new AtomicBoolean(false);
/**
* 初始化
*/
public static void init() {
int size = 20;
if (ab.compareAndSet(false, true)) {
ES = Executors.newFixedThreadPool(size);创建一个可重用的,具有固定线程数20的线程池
}
}
/**
* 销毁
*/
public static void shutdown() {
if (ab.compareAndSet(true, false)) {
ES.shutdown();
ES = null;
}
}
/**
* 提交线程
* @param run
* @return
*/
public static boolean submit(Runnable run) {
if (ab.get()) {
try {
ES.submit(run);
return true;
} catch (Exception e) {
LOGGER.error("提交到线程池失败", e);
}
}
return false;
}
/**
* 提交线程
* @param run
* @return
*/
public static boolean submit(ArrayList<Callable<Object>> callers) {
if (ab.get()) {
try {
ES.invokeAll(callers);
return true;
} catch (Exception e) {
LOGGER.error("提交到线程池失败", e);
}
}
return false;
}
}
五、AtomicBoolean的使用
AtomicBoolean是原子操作类,可以用原子方式更新的 boolean 值。
1、构造函数
1.AtomicBoolean()
使用初始值 false 创建新的 AtomicBoolean。
2.AtomicBoolean(boolean initialValue)
使用给定的初始值创建新的 AtomicBoolean。
参看其中部分源码,AtomicBoolean类里面有一个volatile变量修饰符修饰的私有变量value,AtomicBoolean类几乎所有的方法都是围绕此变量进行操作。
有关volatile变量修饰符的介绍请参看一篇博主写的博客,分享链接:volatile变量修饰符详细介绍
public class AtomicBoolean implements java.io.Serializable {
private static final long serialVersionUID = 4654671469794556979L;
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicBoolean.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
/**
* Creates a new {@code AtomicBoolean} with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicBoolean(boolean initialValue) {
value = initialValue ? 1 : 0;
}
/**
* Creates a new {@code AtomicBoolean} with initial value {@code false}.
*/
public AtomicBoolean() {
}
参看上面部分AtomicBoolean源码我们知道,本文在ThreadWorkFactory线程池工厂类里面使用private static AtomicBoolean ab = new AtomicBoolean(false);来给定初始值false来创建AtomicBoolean,也就是value初始化后为0.
3、AtomicBoolean.compareAndSet(boolean expect,
boolean update)
如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
参数:
expect - 预期值
update - 新值
返回:
如果成功,则返回 true。返回 False 指示实际值与预期值不相等。
源码如下:
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}
我们在ThreadWorkFactory线程池工厂类里面初始化线程池之前使用ab.compareAndSet(false, true)这个if判断条件,来防止线程池被多次初始化
4、AtomicBoolean.get()
public final boolean get()返回当前值。
源码如下
/**
* Returns the current value.
*
* @return the current value
*/
public final boolean get() {
return value != 0;
}
我们在ThreadWorkFactory线程池工厂类里面提交线程使用ab.get()这个if判断条件,来判断线程池是否被初始化(创建),如果已创建就ES.invokeAll(callers);