我有一个主机名列表,我使用ExecutorService
并行执行以收集每个主机名的所有指标。然后我通过迭代每个主机名将来制作一个List,其中包含所有主机名的所有指标相关信息。由于我并行执行多个主机名,所以我不确定这段代码是否是线程安全的。
这是我的主要代码,我并行执行多个HOSTNAMES
:
final Flows typeOfFlow = Flows.TREE;
List<Future<MachineMetrics>> machineFutureList = new ArrayList<>();
for (final String machine : HOSTNAMES) {
machineFutureList.add(executorService.submit(new Callable<MachineMetrics>() {
@Override
public MachineMetrics call() throws Exception {
MachineMetrics machineMetrics = new MachineMetrics();
String url = "http://" + machine + ":8080/text";
Map<String, String> metrics = getMetrics(machine, url, typeOfFlow);
machineMetrics.setMachineName(machine.split("\\.")[0]);
machineMetrics.setDatacenter(TestUtils.findDatacenter(machine).get().name().toLowerCase());
machineMetrics.setMetrics(metrics);
return machineMetrics;
}
}));
}
List<MachineMetrics> metricsList = new ArrayList<>();
for (Future<MachineMetrics> future : machineFutureList) {
try {
metricsList.add(future.get());
} catch (InterruptedException | ExecutionException ex) {
// log exception here
}
}
// now print all the hostnames metrics information
System.out.println(metricsList);
以下是我上面代码所在的同一个类中的getMetrics
代码:
private Map<String, String> getMetrics(final String machine, final String url, final Flows flowType) {
Map<String, String> holder = new HashMap<String, String>();
try {
RestTemplate restTemplate = RestTemplateClient.getInstance().getClient();
String response = restTemplate.getForObject(url, String.class);
Matcher m = PATTERN.matcher(response);
while (m.find()) {
String key = m.group(1).trim();
String value = m.group(2).trim();
holder.put(key, value);
}
} catch (Exception ex) {
// log here
}
return TestUtils.process(holder);
}
以下是findDatacenter
课程中的TestUtils
代码:
public static Optional<Datacenter> findDatacenter(final String hostname) {
if (!TestUtils.isEmpty(hostname)) {
for (Datacenter dc : DC_LIST) {
String namepart = "." + dc.name().toLowerCase() + ".";
if (hostname.indexOf(namepart) >= 0) {
return Optional.of(dc);
}
}
}
return Optional.absent();
}
以下是process
课程中的TestUtils
方法:
public static Map<String, String> process(final Map<String, String> holder) {
Map<String, String> tempMap = new HashMap<>();
for (Map.Entry<String, String> entry : holder.entrySet()) {
if (!entry.getKey().startsWith("calls_") && !entry.getValue().contains("|")) {
continue;
}
String currentKey = entry.getKey();
String currentValue = entry.getValue();
StringTokenizer tokenizer = new StringTokenizer(currentValue, "|");
String count = tokenizer.nextToken().trim();
String avgData = tokenizer.nextToken().trim();
String medianData = tokenizer.nextToken().trim();
String n95data = tokenizer.nextToken().trim();
String n99data = tokenizer.nextToken().trim();
tempMap.put(generateKey(currentKey, currentKey.contains(MISS), COUNT), count);
tempMap.put(generateKey(currentKey, currentKey.contains(MISS), AVG_IN_MS), avgData);
tempMap.put(generateKey(currentKey, currentKey.contains(MISS), MEDIAN_IN_MS), medianData);
tempMap.put(generateKey(currentKey, currentKey.contains(MISS), N95_IN_MS), n95data);
tempMap.put(generateKey(currentKey, currentKey.contains(MISS), N99_IN_MS), n99data);
holder.remove(currentKey);
}
tempMap.putAll(holder);
return tempMap;
}
以下是generateKey
课程中的TestUtils
方法:
private static String generateKey(final String currentKey, final boolean hasMiss, final String constant) {
StringBuilder newKey = new StringBuilder();
if (hasMiss) {
newKey.append(currentKey).append(constant);
} else {
String firstPart = currentKey.substring(0, currentKey.indexOf("_"));
String secondPart = currentKey.substring(currentKey.lastIndexOf("_") + 1, currentKey.length());
newKey.append(firstPart).append(CACHE).append(secondPart).append(constant);
}
return newKey.toString();
}
以下是我的MachineMetrics
课程:
public class MachineMetrics {
private String machineName;
private String datacenter;
private Map<String, String> metrics;
// normal setters and getters here
}
我的上述代码线程是否安全?我是否因为一些竞争条件或线程安全问题而做错了哪些错误的结果?
答案 0 :(得分:1)
看起来不错。您的方法是stateless。您也可以使用immutable objects作为方法参数。所以你对线程安全没有任何问题。
一句话:
for (Future<MachineMetrics> future : machineFutureList) {
try {
metricsList.add(future.get());
} catch (InterruptedException | ExecutionException ex) {
// log exception here
}
}
get Waits if necessary for the computation to complete, and then retrieves its result.
因此,如果第一次通话速度很慢,您将无法检索其他结果。使用isDone检查您是否可以在不等待的情况下致电get
。