总论
1.可以不用多线程最好不要用
2.如果可以不共享数据最好不要共享
3.服务器端最佳线程数量=((线程等待时间+线程cpu时间)/线程cpu时间) * cpu数量
因为数据库访问等待造成线程等待时间长比较长见,下面的例子就是以数据库数据迁徙程序说明。
常用模式
1.分几个线程处理不同数据
适用场景:数据可以容易的分开处理
int dbMax = 10;
int tableMax = 16;
ExecutorService executorService = Executors.newFixedThreadPool(40);
latch = new CountDownLatch(dbMax * tableMax);
for (int tableNo = 1; tableNo <= tableMax; tableNo++) {
for (int dbNo = 1; dbNo <= dbMax; dbNo++) {
// 启动线程
executorService.execute(new UserinfoRunable(dbNo, tableNo));
}
}
executorService.shutdown();
try {
latch.await();
} catch (InterruptedException e1) {
log.error("threadLatch.await()", e1);
}
log.info("main End");
下类省去了部分代码
static class UserinfoRunable implements Runnable {
int dbNo;
int tableNo;
public UserinfoRunable(int dbNo, int tableNo) {
this.dbNo = dbNo;
this.tableNo = tableNo;
}
@Override
public void run() {
while (opSize == bantchSize) {
log.debug("db-table:" + dbNo + "-" + tableNo + " dealSize:" + dealNum);
//省去代码
}
latch.countDown();
}
}
2.线程协作来处理同一批数据
适用场景:数据处理是一个类似生产线情况,每个生产过程费时不同
数据操作方法
package com.jd.sns.dc.common;
import java.util.Collection;
import java.util.concurrent.BlockingQueue;
/**
* 阻塞队列
* @author guanpanpan
*
*/
public class BlockingQueueUtil {
/**
* 向队列加入一组数据,如果对队已满会阻塞
*/
public static <T> void add(BlockingQueue<T> blockingQueue, Collection<T> collection) throws InterruptedException {
for (T object : collection) {
blockingQueue.put(object);
}
}
/**
* 向队列加入单个数据,如果对队已满会阻塞
*/
public static <T> void add(BlockingQueue<T> blockingQueue, T object) throws InterruptedException {
blockingQueue.put(object);
}
}
BlockingQueue<ChangeLogBantch> idBantchQueue = new LinkedBlockingQueue<ChangeLogBantch>(1000);
取数据
while (run) {
try {
List<ChangeLog> changeLogs = changeLogDao.getChangeLogs1(dbNo, checkStartId, checkIdBantchCount);
List<ChangeLogBantch> idBantchBeans = ChangeLogBantch.createIdBantchBeanList(dbNo, DcConst.BANTCH_COUNT,
changeLogs, true);
// 当取出的数据不满批查询数量的时候,认为这次的增量校验完成
if (changeLogs.size() < checkIdBantchCount) {
run = false;
}
// 更新最大ID
if (idBantchBeans.size() > 0) {
// 遍历,获得这次取出的最大的ID,作为下次查询的参数
checkStartId = changeLogs.get(changeLogs.size() - 1).getChangeLogId();
log.debug("idGeter:" + idBantchBeans);
dataGeter.addIdBantchAll(idBantchBeans);
}
} catch (Exception ex) {
log.error("Error in read db!", ex);
}
}
IdGeter.idGeterLatch.countDown();
消费数据
/**
* 获取一个IDBantchBean 非阻塞式调用,返回null表示已拿完
*/
public ChangeLogBantch getIdBantch() {
return idBantchQueue.poll();
}
public void run() {
log.debug("get CompareBeans start" + ThreadUtil.getLogName());
while (run) {
ChangeLogBantch idBantch = getIdBantch();
if (idBantch == null) {
if (!ID_GETER_RUN) {
run = false;
}
} else {
try {
List<CompareBean> compareBeans = checkService.getCompareBeans(idBantch);
dataCompareter.addCompareBean(compareBeans);
log.debug("CompareBeans:" + ThreadUtil.getLogName() + compareBeans);
// 两边都为空的数据记录日志
CkDataGeter.notInChangeLogIds.addAll(checkService.getNotInChangeLogIds(idBantch, compareBeans));
} catch (SQLException e) {
log.error("Get data from db error!", e);
// 出错后将这批ID放回队列中
addIdBantch(idBantch);
}
}
}
log.debug("get CompareBeans end" + ThreadUtil.getLogName());
CkDataGeter.dataGeterLatch.countDown();
}
附一:使用取模来实现多线程处理不同数据
@Override
public long getMode(String pin, int modeSize) {
return getHash(pin) % modeSize + 1;
}
if (modeNo != routeUtil.getMode(pin, modeSize)) {
continue;
}