我在实际项目中写过的多线程
多线程中有分主线程和子线程,有用到对象队列ObjectQueue,首先看一下ObjectQueue这个类:

import java.util.ArrayList;
import java.util.NoSuchElementException;
/**
 * Object Queue which support multi-thread consumer/supplier work
 */
public class ObjectQueue implements java.io.Serializable{
    //private Logger logger;
    private int maxLength;
    private ArrayList queue;
    private long waitTime;
    private boolean inPreparing;
    private boolean dying;// when queue is requested to die, this will set to true;
    private String name;
    /**
    * @param maxLength Maximum length of the queue, if element size is equal to this one, we
    * say queue is full. If no positive integer specified, will take as unlimited.
    */
    public ObjectQueue(int maxLength){
        this(maxLength, 0, "queue");
    }
    /**
     * Wait time out in miliseconds
     * @param maxLength
     * @param wait time out in miliseconds, if 0, will wait until programically interrupted
     * @param queueName name of the queue
     */
    public ObjectQueue(int maxLength, long wait, String queueName){
        this.maxLength= maxLength;
        queue=new ArrayList();
        inPreparing=false;
        dying =false;
        this.waitTime=wait;
        name=queueName;
        //logger= LoggerFactory.getLogger(this.getClass().getName()+"_"+queueName);
    }
    public String getName(){
        return name;
    }
    public int getMaxLength(){
        return maxLength;
    }
    /**
     * Change max length of the queue. 
     * @param maxLength
     */
    public void setMaxLength(int maxLength){
        this.maxLength = maxLength;
    }
    /**
     * How long will the supplier wait until there are space to store the object
     * and the customer wait until there are objects to obtain 
     * @param time
     */
    public void setWaitTime(long time){
        this.waitTime=time;
    }
    /**
     * @return size of current queue.
     */
    public int size(){
        return queue.size();
    }
    /**
    * add a new object into queue, if queue is full, the operation will be
    * locked until element been retrieved out
    */
    public synchronized void addElement(Object obj){
        while( !dying && isFull()){
            try {
                wait(waitTime);
            } catch (InterruptedException e) { }
        }
        queue.add(obj);
        notifyAll();
    }
    public synchronized boolean hasMoreElements(){
        if( !dying && queue.size() > 0  ) return true;
        while ( !dying && queue.size() == 0 && inPreparing ){
            try {
                log("wait to check hasMoreElements");
                wait(waitTime);
                log("check hasMoreElements: quesize="+queue.size()+", inpreparing="+inPreparing);
            } catch (InterruptedException e) { }
        }
        notifyAll();
        return ( queue.size()>0);
    }
    /**
    * Tell that data is just preparing, yet not OK
    * This is specillay important if you want consumers wait even when
    * suppliers has nothing to supply.
    */
    public void setInDataPreparing(boolean b){
        inPreparing =b;
    }
    /**
    * after next element being retrieved, the object will be remove from queue
    * @throws NoSuchElementException if could not get next element either because
    * queue is empty or wait timeout
    */
    public synchronized Object nextElement() throws NoSuchElementException  {
        if ( !dying && queue.size() == 0 && inPreparing ){
            try {
                log("wait to getNextele");
                long b= System.currentTimeMillis();
                wait(waitTime);
                log("out wait getNextele:"+queue.size() +", inprepare: "+inPreparing+" wait "+ (System.currentTimeMillis()- b)/1000+" sec, limit time="+(waitTime/1000));

            } catch (InterruptedException e) { }
        }

        if(  queue.size() > 0){
            try{
                Object obj= queue.remove( queue.size() -1);
                notifyAll();
                return obj;
            }catch(Exception e){
                throw new NoSuchElementException("Error:"+e);
            }
        }

        throw new NoSuchElementException("No element found or time out");

    }
    /**
     * If max length specified by Constructor is -1 or 0, always return false
     */
    public boolean isFull(){
        if (maxLength <1 ) return false;
        return (queue.size() >= maxLength);
    }
    public boolean isEmpty(){
        return (queue.size() == 0);
    }
    /**
     * wait current thread until queue size is bigger(not equal) than specified value,
     * this method is helpful when supplier finished and want to sweep trash now
     */
    public synchronized void waitOnSizeBigger(int size){
        if(size <0) return;
        while( queue.size() > size){
            try {
                wait(waitTime);
            } catch (InterruptedException e) { }
        }
        notifyAll();
    }
    /**
    * this queue is no longer used
    */
    public synchronized void destroy(){
        dying=true;
        // queue.clear(); can not clear queue because this would probably called in
        // normal situation when supplier finish while consumer is
        // still extracting objects
        notifyAll();
        log("ObjectQueue destroied");
    }
    private void log(String s){
      //logger.debug(s);
    }
    public String toString(){
        return getName()+", maxlength="+ getMaxLength()+",current="+ size();
    }

}


然后看一下我的多线程:

import java.sql.Connection;
import java.util.ArrayList;
import java.util.NoSuchElementException;

import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.agilecontrol.nea.core.query.QueryEngine;
import com.agilecontrol.nea.util.NDSException;
import com.agilecontrol.nea.util.ObjectQueue;

/**
 * 多线程处理每个商品的铺量
 * 铺量规则:不同评分 商品的订量不同,不评分或0分时商品不铺量,2分或3分时 对应评分组里的店铺订单会对这个商品下量,4分5分时 刷单买手的所有下级买手订单都会对这个商品下量,
 * 尺码比例先以B_SIZE_UPDATE表为准,找不到再以B_SIZE_ALLOC表为准,再找不到则在第一个尺码下量
 * 实现逻辑:构造一个队列对象,队列中有多个商品对象,多个线程并发对队列中的商品铺量,每个线程有自己的connection,当有一个线程有异常时,所有线程包括主线程全部回滚
 * @author ye.huimin
 */
@SuppressWarnings({ "rawtypes", "unchecked", "static-access" })
public class SaveGroupQtyByFav {

    private static final Logger logger = LoggerFactory.getLogger(SaveGroupQtyByFav.class);
    /**
     * 订货会id
     */
    private int fairId;
    /**
     * 当前主买手id,他有多个下级买手
     */
    private int funitId;
    /**
     * 当前主卖手的user id
     */
    private int userId;
    /**
     * 当前商品id、对应评分star和所在组名groupnm,type标明是iPad端还是portal后台的操作:[{pdtid: 1, star: 5, groupnm: 'G1', type: 'ipad'}, {pdtid: 2, star: 3, groupnm: 'G2', type: 'ipad'}, ...]
     * 或者只是商品id和type:[{pdtid: 1, type: 'portal'},{pdtid: 2, type: 'portal'},...]
     */
    private JSONArray pdtStar;
    /**
     * 线程数,是参数 可控
     */
    private int syncThreadCount;
    /**
     * 需要以组为单位,按评分铺量的商品数
     */
    private int syncPdtCount;
    /**
     * 铺量失败的flag
     */
    private boolean isFailed = false;
    /**
     * 对象队列
     */
    private ObjectQueue queueForSync;
    /**
     * 线程的Connection集合
     */
    private ArrayList<Connection> connList;
    private QueryEngine engine;

    /**
     * @param userId 用户ID
     * @param funitId 买手ID
     * @param fairId 订货会ID
     * @param pdtStar 当前商品id、对应评分star和所在组名groupnm,type标明是iPad端还是portal后台的操作:[{pdtid: 1, star: 5, groupnm: 'G1', type: 'ipad'}, {pdtid: 2, star: 3, groupnm: 'G2', type: 'ipad'}, ...]
     *                  或者只是商品id和type:[{pdtid: 1, type: 'portal'},{pdtid: 2, type: 'portal'},...]
     * @throws Exception
     */
    public SaveGroupQtyByFav(int userId, int funitId, int fairId, JSONArray pdtStar) throws Exception {
        this.fairId = fairId;
        this.funitId = funitId;
        this.userId = userId;
        this.pdtStar = pdtStar;
        this.engine = QueryEngine.getInstance();
        this.syncThreadCount = FairConfig.THREADS_SAVE_GROUP_QTY;
        this.connList = new ArrayList<Connection>();
    }

    /**
     * 创建队列,多线程执行队列中每个对象的铺量动作
     * @return
     * @throws Exception
     */
    public String multiThreadSaveQty() throws Exception {
        long startTime = System.currentTimeMillis();
        String err = null;

        try {
            // 线程数默认为4
            if (syncThreadCount <= 0)
                syncThreadCount = 4;
            // 创建队列
            queueForSync = new ObjectQueue(-1, 500L, "savegroupqtyqueue"); // no limit for input
            queueForSync.setInDataPreparing(true);
            // 队列中添加对象
            for (int i = 0; i < pdtStar.length(); i++) {
                queueForSync.addElement(pdtStar.getJSONObject(i));
            }
            syncPdtCount = queueForSync.size();
            // 创建子线程
            ThreadGroup syncg = new ThreadGroup("SavaGroupQty");
            for (int i = 0; i < syncThreadCount; i++) {
                Connection conn = engine.getConnection();
                connList.add(conn);
                SyncOnePdt syncOne = new SyncOnePdt("groupQty" + i, this, conn);
                Thread thread = new Thread(syncg, syncOne);
                thread.start();
            }
            // 主线程等待
            while (queueForSync.hasMoreElements() && !isFailed) {
                try {
                    Thread.currentThread().sleep(500);
                } catch (Throwable tx) {
                    logger.debug("fail to sleep when queue for zip:" + tx.getMessage());
                }
            }
            // 销毁队列
            queueForSync.destroy();
            // 如果存在失败的对象,抛出异常
            if (isFailed) {
                long duration = (long) ((System.currentTimeMillis() - startTime) / 1000.0);
                err = "刷单用户id = " + funitId + "的商品铺量失败,详见log日志。耗时:" + duration + "秒。";
                throw new NDSException(err); // Fail to save quantity of products.
            }

        } finally {
            for (int i = 0; i < connList.size(); i++) {
                if(connList.get(i)!=null) try{ connList.get(i).close(); } catch(Throwable tx) {}
            }
            if (null != err) {
                return err;
            }
        }

        long duration = (long) ((System.currentTimeMillis() - startTime) / 1000.0);

        String msg = "耗时" + duration + "秒," + (this.isFailed ? "刷单用户id = " + funitId + "的商品铺量失败,详见任务日志" : "完成商品铺量");
        return msg;
    }

    /**
     * 下一个商品对象
     * @return
     */
    JSONObject nextQty() {
        return (JSONObject) queueForSync.nextElement();
    }

    /**
     * 铺量成功的下一步 
     * @param pdt 当前商品id、对应评分star和所在组名groupnm,type标明是iPad端还是portal后台的操作:{pdtid: 1, star: 5, groupnm: 'G1', type: 'ipad'},或者只是商品id和type:{pdtid: 1, type: 'portal'}
     */
    synchronized void onQtyComplete(JSONObject pdt) {
        syncPdtCount--;
        if (syncPdtCount == 0)
            this.queueForSync.setInDataPreparing(false);
    }

    /**
     * 铺量失败的下一步
     * @param pdt 当前商品id、对应评分star和所在组名groupnm,type标明是iPad端还是portal后台的操作:{pdtid: 1, star: 5, groupnm: 'G1', type: 'ipad'},或者只是商品id和type:{pdtid: 1, type: 'portal'}
     * @param tx
     */
    synchronized void onQtyFailed(JSONObject pdt, Throwable tx) {
        isFailed = true;
        logger.debug("pdtId = " + pdt.optString("pdtid") + " failed to save qty. " + tx.getMessage());
        syncPdtCount--;
        if (syncPdtCount == 0)
            this.queueForSync.setInDataPreparing(false);
    }

    /**
     * 单个save pdt 线程
     * 如果有某个商品保存失败,线程退出循环;如果没有,线程执行到队列中没有对象后退出循环
     * 2017年8月28日 下午15:33:50
     * @author ye.huimin
     */
    class SyncOnePdt implements Runnable {
        private final Logger logger = LoggerFactory.getLogger(SyncOnePdt.class);
        private Connection conn;
        private SaveGroupQtyByFav groupQty;
        private String id;
        private int fairId;
        private int funitId;
        private int userId;

        /**
         * @param id 线程名
         * @param groupQty 
         * @throws Exception
         */
        public SyncOnePdt(String id, SaveGroupQtyByFav groupQty, Connection conn) throws Exception {
            this.id = id;
            this.groupQty = groupQty;
            this.userId = groupQty.userId;
            this.funitId = groupQty.funitId;
            this.fairId = groupQty.fairId;
            this.conn = conn;
        }

        @Override
        public void run() {
            logger.debug("Sync " + id + " started");
            long beginTime = System.currentTimeMillis();
            JSONObject pdt;
            while (!groupQty.isFailed) {
                try {
                    pdt = groupQty.nextQty();
                    if (pdt == null) break;
                } catch (NoSuchElementException ex) {
                    logger.debug("No elements found for " + id);
                    break;
                }
                try {
                    executeOne(pdt);
                    groupQty.onQtyComplete(pdt);
                } catch (Throwable tx) {
                    logger.error("Fail to save pdt, id = " + pdt.optInt("pdtid"), tx);
                    groupQty.onQtyFailed(pdt, tx);
                    break;
                }
            }
            double duration = (System.currentTimeMillis() - beginTime) / 1000.0;
            logger.debug("Sync " + id + " ends, druation: " + duration + " seconds");
        }

        /**
         * 每一个商品的保存订量动作
         * @param pdt 当前商品id、对应评分star和所在组名groupnm,type标明是iPad端还是portal后台的操作:{pdtid: 1, star: 5, groupnm: 'G1', type: 'ipad'},或者只是商品id和type:{pdtid: 1, type: 'portal'}
         * 117410JZ802405,117400B1707411,117410AZ404203,117410F1114510,117400C3515201,117410F0103515
         * @throws Exception
         */
        private void executeOne(JSONObject pdt) throws Exception {
            String type = pdt.optString("type", "portal"); // 默认是后端的,则商品会以b_fair_fav表里的评分铺量到默认组里的所有下级买手订单上
            int pdtId = pdt.optInt("pdtid", 0);
            ArrayList param = new ArrayList();
            param.add(funitId); // 买手id
            param.add(pdtId); // 商品id

            if (type.equals("portal")) {
                engine.executeStoredProcedure("b_fo_paving", param, conn);
            }

            if (type.equals("ipad")) {
                int star = pdt.optInt("star", 0); // 评分
                String groupnm = pdt.optString("groupnm", ""); // 组名

                param.add(star);
                param.add(groupnm);
                engine.executeStoredProcedure("b_fo_cllect", param, conn);
            }
        }

    }

}



最后调用多线程:

protected void executeUpdateQty(ArrayList<Integer> pdtIds, JSONObject qtyObject, JSONArray pdtStar, String idArr) throws Exception {
    int fairId = context.getFairId();
    int funitId = context.getFUnitId();
    int userId = context.getUserId();

    // 使用多线程铺量
    SaveGroupQtyByFav saveQty = new SaveGroupQtyByFav(userId, funitId, fairId, pdtStar);
    saveQty.multiThreadSaveQty();
    ...
}