JUC-java并发编程-并发工具类- 线程安全的集合类-ConcurrentHashMap用法(1)

7 篇文章 0 订阅
6 篇文章 0 订阅

JUC

线程安全的集合类-ConcurrentHashMap(1)

错误用法:

1.将26个英文字母打散,每个字母出现两百次,将字母平均分到26个txt文件中。

先创建测试文件

/**
 * 初始化数据
 * 单词计数
 * @author jiangl
 * @version 1.0
 * @date 2021/4/15 14:58
 */
public class InitDataClass{
    static final String ALPHA = "abcdefghijklmnopqrstuvwxyz";

    public static void main(String[] args) {
        int length = ALPHA.length();
        //字母出现次数是200次
        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++){
            //存入26个文件
            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){
                e.printStackTrace();
            }
        }
    }
}

2.遍历文件,分别统计26个字母出现个数

使用HashMap

/**
 * @author jiangl
 * @version 1.0
 * @date 2021/4/15 14:57
 */
public class ConcurrentHashMapTest {
    public static void main(String[] args) {
 		fail1();
    }


    /**
     * 使用HashMap作为容器返回结果,失败,原因是HashMap本身线程不安全,并且,map.get() 和 map.put()两个操作不是原子操作
     */
    private static void fail1() {
        demo(
                //创建map集合
                //创建concurrentHashMap 对不对?
                //使用HashMap失败
                ()->new HashMap<String,Integer>(),
                (map,words)->{
                    for(String word : words){
                        synchronized (map){
                            Integer counter = map.get(word);
                            int newValue = counter == null ? 1 : counter + 1;
                            map.put(word,newValue);
                        }
                    }
                }
        );
    }



    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);
    }

    /**
     *
     * @param i
     * @return
     */
    private 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 (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

输出结果:

{a=197, b=196, c=199, d=200, e=200, f=200, g=199, h=200, i=196, j=197, k=198, l=200, m=196, n=198, o=199, p=194, q=197, r=197, s=197, t=199, u=198, v=198, w=200, x=200, y=200, z=199}

有结果可以看出,使用HashMap线程不安全,并且,map.get() 和 map.put()两个操作不是原子操作,所以导致统计错误。

解决方法:

加锁synchorinized

使用concurrentHashMap

/**
 * @author jiangl
 * @version 1.0
 * @date 2021/4/15 14:57
 */
public class ConcurrentHashMapTest {
    public static void main(String[] args) {
        fail2();
    }

    private static void fail2() {
        demo(
                //创建map集合
                //创建concurrentHashMap 对不对?
                //使用ConcurrentHashMap 也是失败的,由于map.get() 和 map.put()两个操作分开执行了不是原子操作
                () -> 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);
                    }
                }
        );
    }



    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);
    }

    /**
     *
     * @param i
     * @return
     */
    private 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 (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

测试结果:

{a=193, b=194, c=193, d=197, e=197, f=198, g=198, h=195, i=195, j=193, k=195, l=194, m=193, n=197, o=196, p=197, q=198, r=193, s=197, t=192, u=193, v=195, w=198, x=192, y=194, z=195}

使用ConcurrentHashMap依旧无法正常统计,原因是ConcurrentHashMap的put(),get()方法本身是线程安全的,但是两个操作一起操作的时候却不是原子操作,例如代码段:

                        Integer counter = map.get(word);
                        int newValue = counter == null ? 1 : counter + 1;
                        map.put(word, newValue);

所以还是无法获取正确结果。

解决办法1.加锁

2.解决办法使用concurrentHashMap + LongAdder 调用map.computeIfAbsent 成功

/**
 * @author jiangl
 * @version 1.0
 * @date 2021/4/15 14:57
 */
public class ConcurrentHashMapTest {
    public static void main(String[] args) {
        success2();
    }

    private static void success2() {
        demo(
                //使用concurrentHashMap + LongAdder 调用map.computeIfAbsent 成功
                ()->new ConcurrentHashMap<String, LongAdder>(),
                (map,words)->{
                    for(String word : words){
                        //如果缺少一个key,则计算生成一个value,然后将 key value放入map;
                        LongAdder integer = map.computeIfAbsent(word, (key) -> new LongAdder());
                        //执行累加
                        integer.increment();
                    }
                }
        );
    }



    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);
    }

    /**
     *
     * @param i
     * @return
     */
    private 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 (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

结果:

{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}

由此获得到了正确的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值