com.feilong.core.lang.thread.DefaultPartitionThreadExecutor Maven / Gradle / Ivy
Show all versions of feilong Show documentation
/*
* Copyright (C) 2008 feilong
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.feilong.core.lang.thread;
import static com.feilong.core.lang.StringUtil.formatPattern;
import static com.feilong.core.util.CollectionsUtil.partition;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.feilong.core.Validate;
import com.feilong.core.lang.ThreadUtil;
import com.feilong.core.util.CollectionsUtil;
/**
* 默认基于 {@link Thread} 数组的执行实现.
*
* @author feilong
* @since 1.11.0
* @since 2.0.0 move from package com.feilong.core.lang
*/
public class DefaultPartitionThreadExecutor extends AbstractPartitionThreadExecutor{
/** The Constant LOGGER. */
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultPartitionThreadExecutor.class);
//---------------------------------------------------------------
/** Static instance. */
public static final PartitionThreadExecutor INSTANCE = new DefaultPartitionThreadExecutor();
//---------------------------------------------------------------
/**
* Actual execute.
*
* @param
* the generic type
* @param list
* 执行解析的list
*
*
* 比如 100000个 User,不能为null或者empty
*
* @param eachSize
* 每个线程执行多少个对象
*
*
* 比如 一个线程解析 1000个 User, 那么程序内部 会自动创建 100000/1000个 线程去解析;
* 必须{@code >}0
*
* @param paramsMap
* the params map
* @param partitionRunnableBuilder
* the partition runnable builder
*/
@Override
protected void actualExecute(
List list,
int eachSize,
Map paramsMap,
PartitionRunnableBuilder partitionRunnableBuilder){
//1. 自动构造需要启动的线程数组
Thread[] threads = buildThreadArray(list, eachSize, paramsMap, partitionRunnableBuilder);
//2. start 并且 join
ThreadUtil.startAndJoin(threads);
}
//---------------------------------------------------------------
/**
* Builds the thread array.
*
*
* 调用 {@link CollectionsUtil#partition(List, int)} 对list 分成N份,对应的创建N份线程,每个线程的 名字 参见
* {@link #buildThreadName(int, PartitionRunnableBuilder)}
*
*
*
* 会自动创建 ThreadGroup,线程组名字参见 {@link #buildThreadGroupName(List, PartitionRunnableBuilder)},
* 所有新建的线程将归属到该 线程组,你可以在自定义的partitionRunnableBuilder中监控或者管理 该ThreadGroup
*
*
* @param
* the generic type
* @param list
* 执行解析的list
*
*
* 比如 100000个 User,不能为null或者empty
*
* @param eachSize
* 每个线程执行多少个对象
*
*
* 比如 一个线程解析 1000个 User, 那么程序内部 会自动创建 100000/1000个 线程去解析;
* 必须{@code >}0
*
* @param paramsMap
* the params map
* @param partitionRunnableBuilder
* the group runnable builder
* @return the thread[]
*/
private static Thread[] buildThreadArray(
List list,
int eachSize,
Map paramsMap,
PartitionRunnableBuilder partitionRunnableBuilder){
//使用group进行管理
ThreadGroup threadGroup = new ThreadGroup(buildThreadGroupName(list, partitionRunnableBuilder));
//将 list 分成 N 份
List> groupList = partition(list, eachSize);
//-------------------------------------------------------------------
int i = 0;
Thread[] threads = new Thread[groupList.size()];
for (List perBatchList : groupList){
String threadName = buildThreadName(i, partitionRunnableBuilder);
PartitionThreadEntity partitionThreadEntity = new PartitionThreadEntity(
threadName,
list.size(),
eachSize,
i,
perBatchList.size());
Runnable runnable = partitionRunnableBuilder.build(perBatchList, partitionThreadEntity, paramsMap);
threads[i] = new Thread(threadGroup, runnable, threadName);
i++;
}
//---------------------------------------------------------------
LOGGER.info(
"{} totalListSize:[{}],build [{}] threads,inputPerHandleSize:[{}]",
getLogKey(paramsMap),
list.size(),
threads.length,
eachSize);
return threads;
}
//---------------------------------------------------------------
/**
* 构建线程组名称.
*
* 格式:
*
*
* "ThreadGroup-partitionRunnableBuilder 实现类名称-list size"
*
*
* @param
* the generic type
* @param list
* 执行解析的list
*
*
* 比如 100000个 User,不能为null或者empty
*
* @param partitionRunnableBuilder
* the group runnable builder
* @return the string
*/
private static String buildThreadGroupName(List list,PartitionRunnableBuilder partitionRunnableBuilder){
Validate.notNull(partitionRunnableBuilder, "partitionRunnableBuilder can't be null!");
return formatPattern("ThreadGroup-{}-{}", getName(partitionRunnableBuilder), list.size());
}
/**
* 构建线程名称.
*
* 格式:
*
*
* "Thread-partitionRunnableBuilder 实现类名称-{@link com.feilong.core.lang.PartitionThreadEntity#getBatchNumber() batchNumber}"
*
*
* 作用:
*
*
*
*
* - 一来便于管理, 可以使用相关代码来获得线程;
* - 二来常用于日志显示, 比如, 如果是 log4j 的配置文件,如果 ConversionPattern
*
*
* {@code
*
* }
*
*
* 其中 %t 表示 线程名称
*
* 正常情况的日志,会显示(示例)
*
*
* 13:54:43 Thread-NovelpartitionRunnableBuilder-13 INFO (NovelpartitionRunnableBuilder.java:91) 第914章 好手段 3406 [6/20] 14 30%
* 13:54:43 Thread-NovelpartitionRunnableBuilder-5 INFO (NovelpartitionRunnableBuilder.java:91) 第761章 不得其时 3573 [7/20] 6 35%
* 13:54:43 Thread-NovelpartitionRunnableBuilder-3 INFO (NovelpartitionRunnableBuilder.java:91) 第718章 各打各的算盘 3411 [4/20] 4 20%
*
*
* 如果代码有异常, 会显示
*
*
* 13:54:52 Thread-NovelpartitionRunnableBuilder-16 ERROR (DefaultChapterBuilder.java:83) Exception:
* com.feilong.tools.jsoup.JsoupUtilException: urlString:[http://www.37zw.com/0/181/1662249.html],userAgent:[Mozilla/5.0 (X11; Linux
* x86_64) AppleWebKit/535.21 (KHTML, like Gecko) Chrome/19.0.1042.0 Safari/535.21]
* at com.feilong.tools.jsoup.JsoupUtil.getDocument(JsoupUtil.java:87)
* at com.feilong.tools.jsoup.JsoupUtil.getDocument(JsoupUtil.java:65)
* at com.feilong.project.novel.build.DefaultChapterBuilder.getContentElement(DefaultChapterBuilder.java:124)
* at com.feilong.project.novel.build.DefaultChapterBuilder.build(DefaultChapterBuilder.java:68)
* at com.feilong.project.novel.build.NovelpartitionRunnableBuilder$1.run(NovelpartitionRunnableBuilder.java:86)
* at java.lang.Thread.run(Thread.java:745)
*
*
*
*
*
*
*
* @param
* the generic type
* @param batchNumber
* the batch number
* @param partitionRunnableBuilder
* the group runnable builder
* @return 如果 partitionRunnableBuilder
是null,抛出 {@link NullPointerException}
*/
private static String buildThreadName(int batchNumber,PartitionRunnableBuilder partitionRunnableBuilder){
Validate.notNull(partitionRunnableBuilder, "partitionRunnableBuilder can't be null!");
return formatPattern("Thread-{}-{}", getName(partitionRunnableBuilder), batchNumber);
}
}