目的:
计数器控制一批任务的完成再继续下一批。
适合大量耗时计算实时变化的项目场景。
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 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 alloter = new DataSpliter(getAllData(), workderTotal);
//创建计数器
checkControl = new CountDownLatch(workderTotal);
for (int i = 1; i <= workderTotal; i++) {
//获取分配的数据
T[] dataList = alloter.split(i);
CalculateWorker worker = new CalculateWorker(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 {
//总份数
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);
}
}
package demoSpit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CountDownLatch;
/**
* 数据计算苦力者
cdp
*/
public abstract class CalculateWorker 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 {
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();
}
}