java多线程抽象类多任务计算的一个结构框架

使用java计数器控制多线程任务的案例框架

1.CountDownLatch计数器的使用

目的:

计数器控制一批任务的完成再继续下一批。

2、应用场景

  适合大量耗时计算实时变化的项目场景。

3、核心代码

  • 抽象类父类
package demoSpit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 数据计算工头
 */
public abstract class CalculateBoss<T> implements Runnable {
    private Logger logger = LoggerFactory.getLogger(TTRecCalculation.class);
    //
    protected volatile AtomicBoolean run = new AtomicBoolean(false);

    protected volatile AtomicBoolean taskRun = new AtomicBoolean(false);

    protected ExecutorService exec;

    //检查控制,线程计数器
    protected CountDownLatch checkControl;

    //计算间隔周期
    protected int intervalMS;

    //数据版本
    protected volatile long dataVersion = 0L;

    //分布在多少个线程中执行
    public int workderTotal = 0;


    /**
     * 构造计算线程
     *
     * @param workderTotal   分布在多少个线程中执行
     * @param threadPoolSzie 线程池线程数大小
     * @param intervalsecond 计算间隔时间
     */
    public CalculateBoss(int workderTotal, int threadPoolSzie, int intervalsecond) {
        this.workderTotal = workderTotal;
        this.exec = Executors.newFixedThreadPool(threadPoolSzie);
        this.run.set(true);
        this.intervalMS = intervalsecond;
    }


    @Override
    public void run() {
        long startTime = 0L;
        long endTime = 0L;
        while (run.get()) {
            try {
                if (commandCondition()) {

                    //条件达到时执行任务
                    taskRun.set(true);
                    startTime = System.currentTimeMillis();

                    //定义分配器
                    DataSpliter<T> alloter = new DataSpliter<T>(getAllData(), workderTotal);

                    //创建计数器
                    checkControl = new CountDownLatch(workderTotal);

                    for (int i = 1; i <= workderTotal; i++) {
                        //获取分配的数据
                        T[] dataList = alloter.split(i);
                        CalculateWorker<T> worker = new CalculateWorker<T>(dataList, checkControl) {
                            @Override
                            public void work(T[] da) {
                                //工头将指令下达给苦力者
                                calExcute(da);
                            }
                        };
                        exec.submit(worker);
                    }

                    //工头在等待检查任务,await是阻塞方法,等到workderTotal个worker全部完成后才往后执行。
                    try {
                        this.checkControl.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //所有的苦力者完成任务
                    taskRun.set(false);
                    endTime = System.currentTimeMillis();
                }
                logger.info("分配计算完成");
                try {
                    //时间间隔最少等待intervalMS,否则就等耗时操作完成后再等1秒进行下一次计算。
                    TimeUnit.MILLISECONDS.sleep(endTime - startTime < intervalMS ? intervalMS : 1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                continue;
            }


        }
    }

    public void stop() {
        run.set(false);
    }

    /**
     * 控制计算线程的停止
     * @return
     */
    public boolean checkTaskRunning() {
        return taskRun.get();
    }


    /**
     * 获取所有需要计算的数据集合
     *
     * @return
     */
    public abstract T[] getAllData();


    /**
     * 执行条件
     *
     * @return 判断是否是计算时间
     */
    public abstract boolean commandCondition();


    /**
     * 执行具体计算任务
     *
     * @param list 执行任务的key
     */
    public abstract void calExcute(T[] list);


}
  • 任务分配分割类
package demoSpit;

import java.util.Arrays;

/**
 * 数据分配器
 * User: hcc
 * Date: 12-9-5
 * Time: 下午5:01
 */
public class DataSpliter<T> {
    //总份数
    private int workderTotal;

    //总分配的数据
    private T[] list;

    public DataSpliter(T[] list, int workderTotal) {
        this.workderTotal = workderTotal;
        this.list = list;
    }

    /**
     * 分割
     * 根据index 获取对应的每一段数据.如list.size=16,分3个线程跑。那index分别为1,2,3,对应截取到数据下标0到6,7到12,12到16的数据返回。
     * @param index workderTotal的数量
     * @return
     */
    public T[] split(int index) {

        if (index > workderTotal || index < 1) {
            throw new RuntimeException("split error:index illegal ");
        }

        int size = list.length / workderTotal;
        if (list.length % workderTotal > 0) {
            size = size + 1;
        }
        int start = (index - 1) * size;
        int end = (start + size) > list.length ? list.length : (start + size);

        return Arrays.copyOfRange(list, start, end);

    }

}

  • 计算任务task,
package demoSpit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CountDownLatch;

/**
 * 数据计算苦力者
 cdp
 */
public abstract class CalculateWorker<T> implements Runnable {

    public static Logger logger = LoggerFactory.getLogger(CalculateWorker.class);
    //检查控制计数器
    private CountDownLatch checkControl;

    private T[] data;

    public CalculateWorker(T[] data, CountDownLatch checkControl) {
        this.data = data;
        this.checkControl = checkControl;
    }

    public T[] getData() {
        return data;
    }

    public abstract void work(T[] data);

    @Override
    public void run() {
        long s = System.currentTimeMillis();
        work(data);
        //单个线程完成耗时操作后计数器完成,所有的workderTotal个任务完成后,checkControl.await()方法解除阻塞,继续执行后续代码块.
        this.checkControl.countDown();
        logger.info("#############################CalculateWorker[" + Thread.currentThread().getId() + "] task succeed,time:" + (System.currentTimeMillis() - s) + " ms");
    }
}

  • 业务实现类,具体数据控制地方
package demoSpit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;


/**
 * 计算类
 */
public class TTRecCalculation extends CalculateBoss<Long> {
    private Logger logger = LoggerFactory.getLogger(TTRecCalculation.class);
    public static SimpleDateFormat sd2 = new SimpleDateFormat("HHmm");

    /**
     * 构造计算线程
     *
     * @param workderTotal   分布在多少个线程中执行
     * @param threadPoolSzie 线程池线程数大小
     * @param intervalsecond 计算间隔时间
     */
    public TTRecCalculation(int workderTotal, int threadPoolSzie, int intervalsecond) {
        super(workderTotal, threadPoolSzie, intervalsecond);
    }

    @Override
    public Long[] getAllData() {
        //从缓存容器或者其它地方获取需要参与计算的值
        Long[] blockCalIDArray = new Long[15];
        for (int i = 0; i < 15; i++) {
            blockCalIDArray[i] = Long.valueOf(i);
        }
        return blockCalIDArray;
    }

    /**
     * 当前条件是否满足,比如时间条件
     *
     * @return
     */
    public boolean commandCondition() {
        int time = Integer.parseInt(sd2.format(new Date()));
        if (time > 915 && time < 2330) {
            return true;
        }
        return false;
    }

    @Override
    public void calExcute(Long[] sidlist) {

        long startTime = System.currentTimeMillis();
        for (Long sid : sidlist
                ) {
            //执行耗时计算操作
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        logger.info("ttindex cal success! cost " + (endTime - startTime) + "ms!");
    }


}

  • 主方法
package demoSpit;

/**
 * User: cdp
 * Date: 2018/12/28
 * Time: 17:24
 */
public class Main {
    public static void main(String[] args) {
        //构造任务,开始任务计算.
        Thread thread=new Thread(new TTRecCalculation(2,2,5000));
        thread.start();
    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值