10年 Java程序员,硬核人生!勇往直前,永不退缩!

欢迎围观我的git:https://github.com/R1310328554/spring_security_learn 寻找志同道合的有志于研究技术的朋友,关注本人微信公众号: 觉醒的码农,或Q群 165874185

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

ConcurrentHashMap 在 iterator 遍历时候的是线程安全 的,Collections.synchronizedSortedMap 不是;

 

package test.lk;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.SneakyThrows;
import org.apache.tomcat.util.modeler.ManagedBean;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * redis工具类
 *  
 * @version [版本号, 2016年7月27日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public final class TestColl {


    private SortedMap<String, Object> misMatchRptMap = Collections.synchronizedSortedMap(new TreeMap<>());

    public static void main(String[] args) {
        test1();
    }


    @SneakyThrows
    public static void main2(String[] args) {
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(5)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .build(new CacheLoader<String, String>() {
                    @SneakyThrows
                    @Override
                    public String load(String id) {
                        // 加载时,睡眠一秒
                        Thread.sleep(1000);
                        return id + System.currentTimeMillis();
                    }
                });

        // 异步线程加载
        new Thread(() -> {
            try {
                System.out.println("执行get");
                cache.get("key");
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }).start();

        // 异步线程移除
        new Thread(() -> {
            // 睡眠,让这个线程后执行
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("执行invalidate");
            cache.invalidate("key");
        }).start();

        // 按程序逻辑来说,我们应该拿到的结果是空map
        Thread.sleep(1200);
        System.out.println(cache.asMap());
    }


    private static void test1() {

        // TreeMap Concurrent 线程安全..
        SortedMap<String, ManagedBean> misMatchRptMap = Collections.synchronizedSortedMap(new TreeMap<>());
//
Map<String, ManagedBean> misMatchRptMap = Collections.synchronizedMap(new HashMap<>()); 也是一样的
//        Map<String, ManagedBean> misMatchRptMap = new ConcurrentHashMap<>();
        misMatchRptMap.put("aa", new ManagedBean()); //  类型是 Collections$SynchronizedSortedMap
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
                    misMatchRptMap.put("aa" + i, new ManagedBean());
                    try {
                        Thread.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        }.start();

        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {

                // 虽然entries 是线程安全的,但是
                // 这里的iterator 不是Concurrent, 而是TreeMap 的 而是而是TreeMap的内部类: TreeMap.iterator
                Set<Map.Entry<String, ManagedBean>> entries = misMatchRptMap.entrySet(); // 虽然TreeMap 的entrySet 是线程安全的Collections$SynchronizedSet , 但是entrySet 的iterator 并不是
                for (Iterator<Map.Entry<String, ManagedBean>> iterator = entries.iterator(); iterator.hasNext(); ) {
                    Map.Entry<String, ManagedBean> next =  iterator.next();// TreeMap$EntryIterator; 因为 Collections$SynchronizedSet 没有重写iterator 方法,没有提供线程安全的iterator , 这里会报错
                    System.out.println("next = " + next);
                    iterator.remove();
                }
            }
        }, 100, 5);
    }

    private static void test13() {
        List<String> list = new CopyOnWriteArrayList<>();//  "Timer-0" java.lang.UnsupportedOperationException   at java.util.concurrent.CopyOnWriteArrayList$COWIterator.remove(CopyOnWriteArrayList.java:1176)
        list.add("111");
        list.add("222");
        list.add("333");

        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
                    list.add(" ele " + i);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        }.start();

        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                for(Iterator<String> iterator = list.iterator(); iterator.hasNext();){
                    String ele=iterator.next();
                    System.out.println("ele = " + ele);
                    iterator.remove();
                }
                System.out.println(list);
            }
        }, 100, 1000);
    }

    private static void test2() {
        List<String> list = new ArrayList<>();
        list.add("111");
        list.add("222");
        list.add("333");

        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
                    list.add(" ele " + i);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        }.start();

        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                for(Iterator<String> iterator = list.iterator(); iterator.hasNext();){
                    String ele=iterator.next();
                    System.out.println("ele = " + ele);
                    iterator.remove();
                }
                System.out.println(list);
            }
        }, 100, 1000);
    }

}

 

 为什么呢?

一般的经验是,我使用iterator.remove(); 就可以保证不会报错了吧。

但是呢,iterator.remove(); 方法却并不能保证线程安全! 没有谁说 iterator.remove() 在多线程并发执行的情况下一定不报错! 千万不能记混哦!

 

 

其实,

虽然Collections.synchronizedSortedMap(new TreeMap<>()); 之后, TreeMap 的entrySet 是线程安全的Collections$SynchronizedSet , 但是entrySet 的iterator 并不是。

而 ConcurrentHashMap 里面存在大量子类, 重写了大部分的 集合相关的接口、类,确保了所有的集合操作、遍历都是线程安全的!

 

posted on 2021-09-29 19:42  CanntBelieve  阅读(331)  评论(0编辑  收藏  举报