博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线程安全类错误使用示范
阅读量:2197 次
发布时间:2019-05-02

本文共 2242 字,大约阅读时间需要 7 分钟。

前言

线程安全是Java面试中的常客,而在Java中有一些类本身是线程安全的,这些类就是线程安全类,例如ConcurrentHashMap。但是有时候错误地使用线程安全类反而会出现线程不安全的情况。例如下面的例子

错误示范

/** * 关于线程安全类的错误使用示范 * @author RJH * create at 2018/12/10 */public class ThreadUnsafeDemo {    /**     * 累加线程类     */    private static class UnsafeThread implements Runnable{        /**         * 共享的数据         */        private Map
map; /** * 累计的key */ private String key; /** * 线程执行次数 */ private AtomicInteger times; public UnsafeThread(Map
map, String key,AtomicInteger times) { this.map = map; this.key = key; this.times=times; } @Override public void run() { if(map.containsKey(key)){//包含特定的key就累加 map.put(key,map.get(key)+1); }else{ map.put(key,1); } //累计执行次数 times.incrementAndGet(); } } /** * 线程安全类ConcurrentHashMap,表示共享的数据 */ private static Map
map=new ConcurrentHashMap<>(); /** * 线程执行次数 */ private static AtomicInteger times=new AtomicInteger(0); public static void main(String[] args) { String key="test"; //初始化线程池 ExecutorService executor=Executors.newFixedThreadPool(10); for(int i=0;i<100;i++){//执行100次 Runnable job=new UnsafeThread(map,key,times); executor.execute(job); } //线程终止 executor.shutdown(); //死循环等待线程池中所有任务执行完毕 while (!executor.isShutdown()){ } //数据累加后的值 System.out.println("total:"+map.get(key)); //执行次数 System.out.println("times:"+times.get()); }}

执行结果

total:93times:100

分析

出现这样的结果主要是因为在对map累加的过程中,出现了非原子性操作:

map.put(key,map.get(key)+1);

这段代码其实对map进行了3步操作:

  1. 读取map中为key的值
  2. 读取到的值自增(这里其实也发生了3步操作)
  3. 将自增后的值存储到map

而线程安全类的线程安全其实指的是类中的每一个方法是线程安全的。

解决办法

可以使用synchronized来确保这3步操作的原子性,只需要修改run()方法即可

@Override    public void run() {        synchronized (map){            if(map.containsKey(key)){//包含特定的key就累加                map.put(key,map.get(key)+1);            }else{                map.put(key,1);            }        }        //累计执行次数        times.incrementAndGet();    }

修改后执行结果

total:100times:100

转载地址:http://troub.baihongyu.com/

你可能感兴趣的文章
深入了解JVM虚拟机8:Java的编译期优化与运行期优化
查看>>
深入理解JVM虚拟机9:JVM监控工具与诊断实践
查看>>
深入理解JVM虚拟机10:JVM常用参数以及调优实践
查看>>
深入理解JVM虚拟机11:Java内存异常原理与实践
查看>>
深入理解JVM虚拟机12:JVM性能管理神器VisualVM介绍与实战
查看>>
深入理解JVM虚拟机13:再谈四种引用及GC实践
查看>>
Spring源码剖析1:Spring概述
查看>>
Spring源码剖析2:初探Spring IOC核心流程
查看>>
Spring源码剖析3:Spring IOC容器的加载过程
查看>>
Spring源码剖析4:懒加载的单例Bean获取过程分析
查看>>
Spring源码剖析5:JDK和cglib动态代理原理详解
查看>>
Spring源码剖析6:Spring AOP概述
查看>>
Spring源码剖析7:AOP实现原理详解
查看>>
Spring源码剖析8:Spring事务概述
查看>>
Spring源码剖析9:Spring事务源码剖析
查看>>
重新学习Mysql数据库1:无废话MySQL入门
查看>>
探索Redis设计与实现2:Redis内部数据结构详解——dict
查看>>
探索Redis设计与实现3:Redis内部数据结构详解——sds
查看>>
探索Redis设计与实现4:Redis内部数据结构详解——ziplist
查看>>
探索Redis设计与实现6:Redis内部数据结构详解——skiplist
查看>>