i++为什么是线程不安全的

分析:

如果是全局变量,i++是不安全的,因为java在操作i++的时候,是分步骤做的,可以理解为:
temp = i;
temp2 = i+1;
i=temp2;
如果线程1在执行第一条代码的时候,线程2访问i变量,这个时候,i的值还没有变化,还是原来的值,所以是不安全的。

从更底层的角度讲,主要是因为i++这个操作不是原子性的,这个会编译成i= i +1; 所以会出现多线程访问冲突问题。volatile虽然可以保证多线程对修改可见,但代码中用到了
i++, 主要是i++不是原子性操作,这个会编译成i = i +1,其实是做了3个步骤,一个是读取,修改,写入 。所以会出现多线程访问冲突问题

解决方案:

在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而java.util.concurrent.AtomicInteger是一个提供原子操作的Integer类,其提供了线程安全且高效的原子操作,是线程安全的。

AtomicInteger类的底层实现原理是利用处理器的CAS操作(Compare And Swap,比较与交换,一种有名的无锁算法)来检测栈中的值是否被其他线程改变,如果被改变则CAS操作失败。这种实现方法在CPU指令级别实现了原子操作,因此,其比使用synchronized来实现同步效率更高。

CAS操作过程都包含三个运算符:内存地址V、期望值E、新值N。当操作的时候,如果地址V上存放的值等于期望值E,则将地址V上的值赋为新值N,否则,不做任何操作,但是要返回原值是多少。这就保证比较和设置这两个动作是原子操作。系统主要利用JNI(Java Native Interface,Java本地接口)来保证这个原子操作,它利用CPU硬件支持来完成,使用硬件提供swap和test_and_set指令,但CPU下同一指令的多个指令周期不可中断,SMP(Symmetric Multi-Processing)中通过锁总线支持这两个指令的原子性

示例代码:

  

package com.tree.blog.pool.cas;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicTest {

     volatile  int initialValue=1;
     //Atomic包下的是基于CAS实现的,是线程安全的,不需要加锁
     AtomicInteger initialAtomicValue=new AtomicInteger(1);

    /**
     * i++ 是线程不安去的
     *   这个语句在执行的时候实际分为了三部
     *   1、在栈中给一个 tp=i
     *   2、执行i+1,    tp2=i+1
     *   3、赋值 给i完成,i=tp2
     *   如果线程1在执行第一条代码的时候,线程2访问i变量,这个时候,i的值还没有变化,还是原来的值,所以是不安全的
     * @param args
     */
    public static void main(String[] args)  {
        AtomicTest test = new AtomicTest();
        ExecutorService executorService = Executors.newFixedThreadPool(3000);
        //使用Callable+future可以接受返回值其它的不行
        Runnable run = test::increment;
        Runnable runLock = test::incrementWithLock;
        Runnable atomicRun = test::atomicIncrement;
        //线程不安全,会出现多个线程得到相同的值
        for (int i = 0; i <1000 ; i++) {
            executorService.submit(run);
        }
        //同步锁,不会出粗,并发性能低
        for (int i = 0; i <1000 ; i++) {
           executorService.submit(runLock);
        }
        //使用基于CAS实现的原子操作,不错出现重复
        for (int i = 0; i <1000 ; i++) {
            executorService.submit(atomicRun);
        }
        executorService.shutdown();
    }

    /**
     * 同步锁 执行自增
     * @return
     */
   public synchronized  int incrementWithLock(){
       System.out.println("线程名称:"+Thread.currentThread().getName()+"-当前值:"+initialValue);
        return initialValue++;
   }
    /**
     * 执行自增
     * @return
     */
    public  int increment(){
        System.out.println("线程名称:"+Thread.currentThread().getName()+"-当前值:"+initialValue);
        return initialValue++;
    }

    /**
     * 同步锁 执行自增
     * @return
     */
    public synchronized int atomicIncrement(){
        System.out.println("线程名称:"+Thread.currentThread().getName()+"-当前值:"+initialAtomicValue.get());
        return initialAtomicValue.getAndIncrement();
    }
}

  参考:

  https://www.jianshu.com/p/ae25eb3cfb5d

posted on 2020-04-18 21:30  滚动的蛋  阅读(1231)  评论(0编辑  收藏  举报

导航