java线程安全——原子操作

竞态条件和临界区

多线程访问相同资源,对资源进行写操作时,对执行顺序有要求。

public class Demo {

    public int i = 0;
    public void incr() {
    
        i++;
    }
}

临界区:incr方法内部就是临界区域,关键部分代码的多线程并发执行,会对执行结果产生影响。

竞态条件:可能发生在临界区内的特殊田间。多线程执行incr方法中i++关键代码时,产生了竞态条件。

 

共享资源:

如果一段代码是线程安全的,则不包含竞态条件,只有当多个线程更新共享资源时,才会产生竞态条件。

栈封闭状态下,不会在线程之间进行数据共享,所以是线程安全的。

局部对象引用是不共享的,但是引用的对象是存放在堆内存当中的。如果对象引用是在方法内创建的,只在方法中进行传递,并且对其他线程是不可用的,那它也是线程安全的。

 

不可变对象:

如果创建的是不可变的共享对象,对象在线程间共享时不会被修改,不可变对象也是线程安全的。

 

java的原子性操作:

概念可参考数据库的原子性操作。

原子操作可以是一个步骤也可以是多个步骤,但是执行顺序不可改变,也不可以切割执行,而是将整个操作过程视为一个整体的操作。

竞态条件下,线程是不安全的,需要将操作转换为原子操作,才能保证线程安全。

经典案例:多线程循环执行i++;

方法:锁、循环CAS。

 

CAS机制:

Compare and Swap 比较和交换,处理器提供基本内存操作的原子性保证。

Cas操作需要两个数值,操作前的值和操作后的值,在操作期间,先比较原值是否发生改变,如果发生改变,则不替换,没有改变时,才将新值写入,替换原有旧值。

Java中,sun.misc.Unsafe类,提供了compareAndSwapInt等几个方法,实现CAS操作,保证了原子性。

compareAndSwapInt方法可能会失败。类似数据库的乐观锁操作。在真正的业务中,该方式并不常用,使用不方便。

简单的基本类型的操作也可以通过AtomicInteger等进行操作。

AtomicInteger内部就是反射,调用sun.misc.Unsafe的操作。

Cas的问题:

1、cas+循环:不适合多个属性的原子操作;

2、容易导致CPU占用100%;

3、aba问题,有线程不知道数据被修改了 ;

你可能感兴趣的:(java,Java-Web)