对java多线程的线程安全性的一些总结

一直以来对 java 的线程安全都有点模糊,知道最近写程序时发现在处理大数据的时候,基本上都不可能离开多线程,而线程安全的忽略对数据的准确性产生比较严重的影响,在这里,对 JAVA 的线程安全做初步总结,以避免以后的代码中出现线程安全问题。

线程安全主要出现在两个方面:

1.      多线程,同时使用公共变量时,出现线程私自拷贝公共变量,导致公共变量出现不唯一的情况,线程为了提高效率,将某成员变量(A)拷贝了一份(如B),线程中对A的访问其实访问的是B。只在某些动作时才进行AB的同步。因此存在AB不一致的情况。对这种情况,需要在代码中对变量进行volatile关键字的修饰,这样,java的虚拟机将拒接线程私自拷贝公共变量的情况,使该变量全局唯一,避免出现线程安全。Java提供的AtomicInteger, AtomicLongAtomicBoolean就是对这方面的应用。AtomicInteger等类型实际上是对int等基础类型进行了封装,并使用了volatile对数据变量进行了修饰。查看源码可见: AtomicInteger里面定义了一个私有成员:private volatile int value;

2.      多线程时,每个线程对公共资源的使用存在多个步骤,而每个线程对这多个步骤的执行不具有原子性,容易被其他线程中途打断,造成数据的结果和预想的结果出现不一致的情形。比如ArrayList的增加元素的方法的源码实现:

public boolean add(E e) {

       ensureCapacityInternal(size + 1); // Increments modCount!!

       elementData[size++] = e;

       return true;

}

       方法中,添加元素的分成了三个步骤(1判断是否需要扩充容器容量ensureCapacityInternal(size + 1);2. 计数器自增size++ 3.对元素对应的位置进行赋值。),这种情况在多线程的情况下容易出现被其他线程打断,出现a线程执行addsize++后被b线程打断,b线程在a线程执行自增的基础上继续执行自增并设置值后a线程得到cpu后执行赋值语句,覆盖了b线程设置的值。而java提供的线程安全的顺序容器Vector的添加元素方法的源码如下:

    public synchronized boolean add(E e) {

       modCount++;

        ensureCapacityHelper(elementCount + 1);

        elementData[elementCount++] = e;

        return true;

}

Synchronized修饰了add方法,进行了加锁,使得多线程同时调用add时进行了排他性,等待已经获得锁的线程执行该方法完毕后才能进去执行该方法里面的代码。

常用的线程安全容器

Vector  ConcurrentHashMap  Collections.synchronizedSet  Collections.synchronizedList  Collections.synchronizedMap  BufferedWriter

如果不确定该容器是否是线程安全,可以直接查看源码,看操作方法里面的代码是否用到synchronized进行修饰。总而言之,Synchronized用来保证多语句执行的原子性。另外还有Lock也能用来保证多语句的原子性,其实原理都是用锁机制保障资源使用在同一时间的使用者只有一个的特性。一般使用Synchronized就能满足需求。

 

--回头看看之前写的代码,很多时候没有考虑线程的安全性,比如经常使用简单的int,long 基础类型和Listhashmap,hashset等容器在多线程程序中而不自知。实在汗颜。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值