搭建练习环境:
java
public class Test {
public static void main(String[] args){
//初始化文件
construct();
}
//开启26个线程,每个线程调用get方法获取map,从对应的文件读取单词并存储到list中,最后调用accept方法进行统计。
public static <V> void calculate(Supplier<Map<String,V>> supplier,
BiConsumer<Map<String,V>, List<String>> consumer) {
Map<String, V> map = supplier.get();
CountDownLatch count = new CountDownLatch(26);
for (int i = 1; i < 27; i++) {
int k = i;
new Thread(()->{
ArrayList<String> list = new ArrayList<>();
read(list,k);
consumer.accept(map,list);
count.countDown();
}).start();
}
try {
count.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(map.toString());
}
//读单词方法的实现
public static void read(List<String> list,int i){
try{
String element;
BufferedReader reader = new BufferedReader(new FileReader(i + ".txt"));
while((element = reader.readLine()) != null){
list.add(element);
}
}catch (IOException e){
}
}
//生成测试数据
public void construct(){
String str = "abcdefghijklmnopqrstuvwxyz";
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < str.length(); i++) {
for (int j = 0; j < 200; j++) {
list.add(String.valueOf(str.charAt(i)));
}
}
Collections.shuffle(list);
for (int i = 0; i < 26; i++) {
try (PrintWriter out = new PrintWriter(new FileWriter(i + 1 + ".txt"))) {
String collect = list.subList(i * 200, (i + 1) * 200).stream().collect(Collectors.joining("\n"));
out.println(collect);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
实现一
java
calculate(
// 创建 map 集合
// 创建 ConcurrentHashMap 对不对?
() -> new ConcurrentHashMap<String, Integer>(),
// 进行计数
(map, words) -> {
for (String word : words) {
Integer counter = map.get(word);
int newValue = counter == null ? 1 : counter + 1;
map.put(word, newValue);
}
}
);
输出:
sh
{a=185, b=186, c=153, d=28, e=166, f=58, g=103, h=175, i=126, j=165, k=169, l=185, m=173, n=182, o=155, p=148, q=187, r=191, s=71, t=186, u=96, v=167, w=186, x=160, y=168, z=64}
错误原因:
- ConcurrentHashMap虽然每个方法都是线程安全的,但是多个方法的组合并不是线程安全的。
正确答案一
java
calculate(
() -> new ConcurrentHashMap<String, LongAdder>(),
(map, words) -> {
for (String word : words) {
// 注意不能使用 putIfAbsent,此方法返回的是上一次的 value,首次调用返回 null
map.computeIfAbsent(word, (key) -> new LongAdder()).increment();
}
}
);
说明:
- computIfAbsent方法的作用是:当map中不存在以参数1为key对应的value时,会将参数2函数式接口的返回值作为value,put进map中,然后返回该value。如果存在key,则直接返回value
- 以上两部均是线程安全的。
正确答案二
java
calculate(
() -> new ConcurrentHashMap<String, Integer>(),
(map, words) -> {
for (String word : words) {
// 函数式编程,无需原子变量
map.merge(word, 1, Integer::sum);
}
}
);