• 多线程之线程安全集合类


    1 概述

    在这里插入图片描述

    线程安全集合类可以分为三大类:

    • 遗留的线程安全集合如 Hashtable , Vector

    • 使用 Collections 装饰的线程安全集合,如:

      • Collections.synchronizedCollection
      • Collections.synchronizedList
      • Collections.synchronizedMap
      • Collections.synchronizedSet
      • Collections.synchronizedNavigableMap
      • Collections.synchronizedNavigableSet
      • Collections.synchronizedSortedMap
      • Collections.synchronizedSortedSet
    • java.util.concurrent.*包下面类

    2 JUC安全集合

    JUC安全集合中主要包括Blocking、CopyOnWrite、Concurrent.

    • Blocking 大部分实现基于锁,并提供用来阻塞的方法
    • CopyOnWrite 之类容器修改开销相对较重
    • Concurrent 类型的容
      • 内部很多操作使用 cas 优化,一般可以提供较高吞吐量
      • 弱一致性
        • 遍历时弱一致性,例如,当利用迭代器遍历时,如果容器发生修改,迭代器仍然可以继续进行遍 历,这时内容是旧的
        • 求大小弱一致性,size 操作未必是 100% 准确
        • 读取弱一致性

    注:

    遍历时如果发生了修改,对于非安全容器来讲,使用 fail-fast 机制也就是让遍历立刻失败,抛出 ConcurrentModificationException,不再继续遍历.

    3 ConcurrentHashMap应用

    案例: 统计单词数

    测试数据

    static final String ALPHA = "abcedfghijklmnopqrstuvwxyz";
    public static void main(String[] args) {
         int length = ALPHA.length();
         int count = 200;
         List<String> list = new ArrayList<>(length * count);
         for (int i = 0; i < length; i++) {
             char ch = ALPHA.charAt(i);
             for (int j = 0; j < count; j++) {
                 list.add(String.valueOf(ch));
             }
         }
         Collections.shuffle(list);
        
         for (int i = 0; i < 26; i++) {
             try (PrintWriter out = new PrintWriter(
             new OutputStreamWriter(
             new FileOutputStream("tmp/" + (i+1) + ".txt")))) {
                 String collect = list.subList(i * count, (i + 1) * count).stream()
                 .collect(Collectors.joining("\n"));
                 out.print(collect);
             } catch (IOException e) {
             }
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    执行代码

    private static <V> void demo(Supplier<Map<String,V>> supplier, 
    BiConsumer<Map<String,V>,List<String>> consumer) {
         Map<String, V> counterMap = supplier.get();
         List<Thread> ts = new ArrayList<>();
        
         for (int i = 1; i <= 26; i++) {
             int idx = i;
             Thread thread = new Thread(() -> {
                 List<String> words = readFromFile(idx);
                 consumer.accept(counterMap, words);
             });
             ts.add(thread);
         }
         ts.forEach(t->t.start());
         ts.forEach(t-> {
             try {
                 t.join();
             } catch (InterruptedException e) {
                 e.printStackTrace(); 
             }
         });
         System.out.println(counterMap);
    }
    public static List<String> readFromFile(int i) {
         ArrayList<String> words = new ArrayList<>();
         try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream("tmp/"
        + i +".txt")))) {
             while(true) {
                 String word = in.readLine();
                 if(word == null) {
                     break;
                 }
                 words.add(word);
             }
             return words;
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    目标结果:

    {a=200, b=200, c=200, d=200, e=200, f=200, g=200, h=200, i=200, j=200, k=200, l=200, m=200, 
    n=200, o=200, p=200, q=200, r=200, s=200, t=200, u=200, v=200, w=200, x=200, y=200, z=200} 
    
    • 1
    • 2

    实现代码

    demo(
         // 创建 map 集合
         () -> new HashMap<String, Integer>(),
         // 进行计数
         (map, words) -> {
             for (String word : words) {
                 Integer counter = map.get(word);
                 int newValue = counter == null ? 1 : counter + 1;
                 map.put(word, newValue);
             }
         }
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    执行结果,没有得到预期效果, 因为循环中组合不是线程安全操作.

    优化方案1

    demo(
     () -> new ConcurrentHashMap<String, LongAdder>(),
     (map, words) -> {
         for (String word : words) {
             // 注意不能使用 putIfAbsent,此方法返回的是上一次的 value,首次调用返回 null
             map.computeIfAbsent(word, (key) -> new LongAdder()).increment();
         }
     }
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    优化方案2

    demo(
     () -> new ConcurrentHashMap<String, Integer>(),
     (map, words) -> {
         for (String word : words) {
             // 函数式编程,无需原子变量
             map.merge(word, 1, Integer::sum);
         }
     }
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    Elasticsearch——Docker单机部署安装
    Kafka元数据拉取流程
    [Qt] Qt Creator中配置 Vs-Code 编码风格
    ChatGPT:Maven构建问题——配置Maven以生成可执行JAR文件并设置主类、解决Maven生成多个JAR文件的问题
    提效小技巧——记录那些不常用的代码片段
    Cell子刊综述:药物研发进入智能生成时代
    基于PaddlePaddle框架对CIFAR-100数据集在简易CNN(LeNet-5修改)和简易DNN的效果对比
    2022 计算机网络考点大纲【太原理工大学】
    全志D1s开发板裸机开发之坏境搭建
    LocalDateTime的使用
  • 原文地址:https://blog.csdn.net/ABestRookie/article/details/126516769