[英]Java Supporting Embedded Multi-Threading
需要 Java 多線程方面的幫助
我有一個案例如下:
有很多記錄。 每條記錄大約有 250 個字段。 每個字段都需要根據預定義的規則進行驗證。
所以我定義了一個類 FieldInfo 來表示每個字段:
public class FieldInfo {
private String name;
private String value;
private String error_code;
private String error_message;
// ignore getters and setters
}
一個類 Record 來表示一條記錄:
public class Record {
List<FieldInfo> fields;
// omit getter and setter here
}
以及規則接口和類:
public interface BusinessRule {
// validating one field needs some other fields' value in the same record. So the list of all fields for a certain record passed in as parameter
public FieldInfo validate(List<FieldInfo> fields);
}
public class FieldName_Rule implements BusinessRule {
public FieldInfo validate(List<FieldInfo> fields) {
// will do
// 1. pickup those fields required for validating this target field, including this target field
// 2. performs validation logics A, B, C...
// note: all rules only read data from a database, no update/insert operations.
}
}
用戶可以一次提交 5000 條或更多記錄進行處理。 性能要求很高。 我正在考慮為提交的多個線程,例如5000條記錄(意味着一個線程運行多個記錄),並且在每個線程中,在每個記錄上分叉另一個多個線程以運行規則。
但不幸的是,這種嵌入式多線程在我的情況下總是消亡。
以下是上述解決方案的一些關鍵部分:
public class BusinessRuleService {
@Autowired
private ValidationHandler handler;
public String process(String xmlRequest) {
List<Record> records = XmlConverter.unmarshall(xmlRequest).toList();
ExecutorService es = Executors.newFixedThreadPool(100);
List<CompletableFuture<Integer> futures =
records.stream().map(r->CompletableFuture.supplyAsync(()-> handler.invoke(r), es)).collect(Collectors.toList());
List<Integer> result = future.stream().map(CompletableFuture::join).collect(Collectors.toList());
System.out.println("total records %d processed.", result.size());
es.shutdown();
return XmlConverter.marshallObject(records);
}
}
@Component
public class ValidationHandlerImpl implements ValidationHandler {
@Autowired
private List<BusinessRule> rules;
@Override
public int invoke(Record record) {
ExecutorService es = Executors.newFixedThreadPool(250);
List<CompletableFuture<FieldInfo> futures =
rules.stream().map(r->CompletableFuture.supplyAsync(()-> r.validate(record.getFields()), es)).collect(Collectors.toList());
List<FieldInfo> result = future.stream().map(CompletableFuture::join).collect(Collectors.toList());
System.out.println("total records %d processed.", result.size());
es.shutdown();
return 0;
}
}
工作流程是:用戶以 xml 字符串格式提交記錄列表。 應用程序端點之一在 BusinessRuleService 對象中啟動流程方法。 該過程使用 CompletableFuture 來組合任務並將任務提交給一個 ExecutorService,該服務具有一個大小為 100 的線程池。 CompletableFuture 列表中的每個任務然后啟動 ValidationHandler 對象。 ValidationHandler 對象組成另一個 CompletableFuture 任務,並將任務提交給另一個池大小與規則列表大小相同的 ExecutorService。
上面的解決方案是否合適?
注意:我目前的解決方案是:對提交的記錄進行順序處理。 每條記錄並行處理 250 條規則。 使用這個方案,5000條記錄需要2個多小時。 這種糟糕的表現是企業不能接受的。
我對並發/多線程編程很陌生。 非常感謝各種幫助!
這是眾所周知的“單生產者-多消費者”模式。 經典的解決方案是創建一個BlockingQueue<Record> queue
,並按照讀取的速度將記錄放在那里。 在隊列的另一端,一些工作線程從queue
讀取記錄並處理它們(在我們的例子中,驗證字段):
class ValidatingThread extends Tread {
BlockingQueue<Record> queue;
FieldName_Rule validator = new FieldName_Rule();
public Validator (BlockingQueue<Record> queue) {
this.queue = queue;
}
public void run() {
Record record = queue.take();
validator.validate(collectFields(record));
}
}
最佳線程數等於Runtime.getRuntime().availableProcessors()
。 一開始就啟動它們,不要使用“嵌入式多線程”。 在處理完所有記錄后如何停止線程的任務留作學習任務。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.