SpringMVC之多线程开发应用(一)

一、创建线程的三种方式

继承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);

猜你喜欢

转载自blog.csdn.net/qq_17586893/article/details/80666457
今日推荐