- ConcurrentHashMap
ConcurrentHashMap的线程安全是保证单次 *** 作的原子性,仅仅使用ConcurrentHashMap不能保证线程安全,如果在多线程的情况下进行 *** 作依旧要对map进行加锁
public String right() throws InterruptedException { ConcurrentHashMapconcurrentHashMap = getData(ITEM_COUNT - 100); log.info("init size:{}", concurrentHashMap.size()); ForkJoinPool forkJoinPool = new ForkJoinPool(THREAD_COUNT); forkJoinPool.execute(() -> IntStream.rangeClosed(1, 10).parallel().forEach(i -> { //下面的这段复合逻辑需要锁一下这个ConcurrentHashMap synchronized (concurrentHashMap) { int gap = ITEM_COUNT - concurrentHashMap.size(); log.info("gap size:{}", gap); concurrentHashMap.putAll(getData(gap)); } })); forkJoinPool.shutdown(); forkJoinPool.awaitTermination(1, TimeUnit.HOURS); log.info("finish size:{}", concurrentHashMap.size()); return "OK"; }
提供了一些原子性的简单复合逻辑的 *** 作,比如computeIfAbsent
//利用computeIfAbsent()方法来实例化LongAdder,然后利用LongAdder来进行线程安全计数 ConcurrentHashMap freqs = new ConcurrentHashMap<>(ITEM_COUNT); freqs.computeIfAbsent(key, k -> new LongAdder()).increment();
额外的:computeIfAbsent和putIfAbsent的区别
除了最基础的参数不同以外,putIfAbsent是如果不存在参数一的key,将参数一和参数二作为键值对put到map当中,同时,原本存在该方法会返回key对应的value的值,原本不存在则会返回null,computeIfAbsent如果key不存在则将参数二的计算结果作为value值put到map当中
ThreadLocal
ThreadLocal在进行存储的时候是每个线程需要独立的进行保存信息,用作保存每个线程独享的对象,为每个线程都创建一个副本,这样每个线程都可以修改自己所拥有的副本, 而不会影响其他线程的副本,确保了线程安全,比如:当用户信息获取比较昂贵,如从数据库中获取,可以使用ThreadLocal进行存储用户信息,但是在利用ThreadLocal存储用户信息容易导致用户信息错乱,这点存在误区,我们在没有手动开启多线程的情况下,我们的web容器本身是并发的,会进行线程重用,当进行线程重用的时候,当前用户可能获取到的是上一个用户的信息,可以及时对ThreadLocal的信息进行清理,可以避免这类问题的出现。
CopyOnWrite导致的性能问题
在 Java 中,CopyonWriteArrayList 虽然是一个线程安全的 ArrayList,但因为其实现方式是,每次修改数据时都会复制一份数据出来,所以有明显的适用场景,即读多写少或者说希望无锁读的场景。当在有频繁的写 *** 作的时候CopyOnWrite的性能会十分糟糕,每次 add 时,都会用 Arrays.copyOf 创建一个新数组,频繁 add 时内存的申请释放消耗会很大。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)