zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    javascript ext 闭包
    Hibernate HQL from superclass 问题
    sql查询按in顺序排序显示数据 oracle
    Hibernate createSQLquery()
    sql 分页
    javasript 闭包测试
    Excel 批量快速导入mySQL 解决方案~~
    C# 注册COM+组件步骤~
    QT错误集锦~
    QuartzNet Test~~
  • 原文地址:https://www.cnblogs.com/irobotzz/p/12728218.html
Copyright © 2011-2022 走看看