zoukankan      html  css  js  c++  java
  • 多线程atomicInteger

    并发编程的3个重要概念

    1.原子性:

    一个操作或者多个操作,要么全部成功,要么全部失败 

    1.java中保证了基本数据类型的读取和赋值,保证了原子性,这些操作不可终端

    a=10  原子性
    b=a    不满足 1.read a; 2.assign b;
    c++    不满足  1.read c; 2 add 3.assige to c 
    c=c+1  不满足 1.read c 2.add; 3. assign to c;

    2.可见性:

    volatile 可以保证可见性.主要是把变量放在主存里

    多个线程访问这个变量,一个线程修改之后必须保证另一个线程可以看见。

    每一个线程都自己的缓存,有的变量在主存区,我们要保证变量的可见性

    4.顺序性:

    java中hapens-before 原则保证了有序性

    4.1 代码顺序(程序顺序规则): 单个线程中的每个操作    编写在前面的反生在编写在后面的

    4.2 lock原则(监视器锁规则) : unlock 必须发生在lock之后

    4.3 volatile变量规则: 对该变量的写操作必须发生在读操作之前.就是一个变量 写变量发生在读变量之前

    4.4 传递规则: a->b->c  那么a肯定在c之前

    4.5 start规则:   先start 线程在启动(个人认为废话)

    4.6 中断规则: 对线程的中断 先发生在 捕获interpetor异常之前(个人认为废话)

    4.7 对象的销毁规则: 一个对象的初始化.必须发生在finalize 之前(个人认为废话)

    4.8 线程的终结规则: 所有操作都发生在线程死亡之前 (个人认为废话)

    比如定义一个变量

    int i =0;
    
    boolean flag =false;
    
    int i =1;
    
    boolean flag =true;
    
    java中有重排序的过程 因为这几条代码没有关联性可能会变成
    
    int i =0;
    
    boolean flag =false;
    
    boolean flag =true;
    
    int i =1;

    重排序只保证最终一致性,重排序也是有依赖关系的,重排序对没有依赖关系的可以重排序 ,重排序不会影响单线程,但是会影响多线程

    举个例子

    --------thread-1 ==========

    obj =createObj();1
    flag = true; 2
    如果这里发生指令重排将  1 -2 顺序颠倒下面下面的代码就会报出空指针异常

    -----thread-2------
    while(!flag){
    }
    useObject()

    atomic 具有原子性

     主要有俩点 1.他的变量是volatile

           2.unsafe方法调用

        // setup to use Unsafe.compareAndSwapInt for updates
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        private static final long valueOffset;
    
        static {
            try {
                valueOffset = unsafe.objectFieldOffset
                    (AtomicInteger.class.getDeclaredField("value"));
            } catch (Exception ex) { throw new Error(ex); }
        }
    
        private volatile int value;

    什么时候需要使用atomic类对象

    当多线程操作一个变量的时候。如果使用传统的变量会出错,很多同学为了解决这个问题,加了votatile修饰。也并不能够解决问题。

    补充下votalite 的知识

    volatile具有可见性、有序性,不具备原子性。

    当value+=1时候我们需要做几个操作

    1.从主存区获取变量到写存区

    2.value+1

    3.value = 新的value

    4.讲变量写入主存区  

    这4步都是原子性的。但是在多线程环境中。可能你读取到的变量是别人修改过的变量造成线程不安全情况  

      private static Set<Integer> set = Collections.synchronizedSet(new HashSet<>());
    
        public static void main(String[] args) throws InterruptedException {
    
           Thread t1= new Thread(()->{
                int x=0;
                while (x<500){
                    set.add(value);
                    int temp =value;
                    System.out.println(Thread.currentThread().getName()+"   "+temp);
                    value +=1;
                    x++;
    
                }
            });
            Thread t2= new Thread(()->{
                int x=0;
                while (x<500){
                    set.add(value);
                    int temp =value;
                    System.out.println(Thread.currentThread().getName()+"   "+temp);
                    value +=1;
                    x++;
    
                }
            });
            Thread t3= new Thread(()->{
                int x=0;
                while (x<500){
                    set.add(value);
                    int temp =value;
                    System.out.println(Thread.currentThread().getName()+"   "+temp);
                    value +=1;
                    x++;
    
                }
            });
    
            t1.start();
            t2.start();
            t3.start();
           t1.join();
    
    
            t2.join();
    
            t3.join();
            System.out.println(set.size());
    」
    View Code

    当我们使用aomic类时。可以保证原子性

         AtomicInteger atoValue = new AtomicInteger();
            Thread t1= new Thread(()->{
                int x=0;
                while (x<500){
                    int i = atoValue.getAndIncrement();
                    set.add(i);
                    System.out.println(Thread.currentThread().getName()+"   "+i);
                    atoValue.getAndIncrement();
                    x++;
    
                }
            });
            Thread t3= new Thread(()->{
                int x=0;
                while (x<500){
                    int i =atoValue.getAndIncrement();
                    set.add(i);
                    System.out.println(Thread.currentThread().getName()+"   "+i);
                    atoValue.getAndIncrement();
                    x++;
    
                }
            });
            Thread t2= new Thread(()->{
                int x=0;
                while (x<500){
                    int i = atoValue.getAndIncrement();
                    set.add(i);
                    System.out.println(Thread.currentThread().getName()+"   "+i);
    
                    x++;
    
                }
            });
            t1.start();
            t2.start();
            t3.start();
            t1.join();
            t2.join();
            t3.join();
            System.out.println(set.size());

     下载jdk源码

    http://download.java.net/openjdk/jdk8/promoted/b132/openjdk-8-src-b132-03_mar_2014.zip

    源码查看

    /openjdk/jdk/src/share/classes/sun/misc/Unsafe.java 

    atomic保证原子性操作是因为有unsafe.java 

    很多人都说unsafe.java 是java留的一后门可以直接操作cpu级别的内存

    因为UnSafe提供了硬件级别的原子操作,提高了Java对底层操作的能力。Unsafe类使Java语言拥有了像C语言的指针一样的操作内存空间的能力

    一篇比较好的博客

    https://www.cnblogs.com/pkufork/p/java_unsafe.html

    如何保证原子性

    /**
         * Atomically adds the given value to the current value of a field
         * or array element within the given object <code>o</code>
         * at the given <code>offset</code>.
         *
         * @param o object/array to update the field/element in
         * @param offset field/element offset
         * @param delta the value to add
         * @return the previous value
         * @since 1.8
         */

    public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
    }
      1、需要修改的对象
    2、更改属性的内存偏移量

    4、要添加的 1
    public final int getAndAddInt(Object o, long offset, int delta) { 
    int v;

    do {
      v
    = getIntVolatile(o, offset);
    }

    while (!compareAndSwapInt(o, offset, v, v + delta)); return v;
    }
    其实主要原子性是通过compareAndSwapInt对比算法.传入你开始的值.和预期的值 如果不相同就一直执行下去直到相同为止,达到很多人说的无锁概念,其实我更倾向于自旋锁
    假设多线程调用getAndIncrement
    假设初始值是1
    compareAndWapInt(1,1+1)
    compareAndWapInt(1,1+1)

    1 2 比较false

    进行下一次 2==2

    2

    1 2比较 false

    2 3 比较false

    3

    这样就实现了free lock

    获取unsafe.实例利用java反射

       Field f = Unsafe.class.getDeclaredField("theUnsafe");
             f.setAccessible(true);
             Unsafe unsafe = (Unsafe) f.get(null);

    自己测试类

      public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    
             Field f1 = Unsafe.class.getDeclaredField("theUnsafe");
             f1.setAccessible(true);
             Unsafe unsafe = (Unsafe) f1.get(null);
            Class clazz = Target.class;
            Field[] fields = clazz.getDeclaredFields();
            for (Field f : fields) {
                // 获取属性偏移量,可以通过这个偏移量给属性设置
                System.out.println(f.getName() + ":" + unsafe.objectFieldOffset(f));
            }
            Target target = new Target();
            Field intFiled  =  clazz.getDeclaredField("intParam")  ;
    //        int a=(Integer)intFiled.get(target ) ;
    //        System.out.println("原始值是:"+a);
            //intParam的字段偏移是12 原始值是3 我们要改为10
            System.out.println(unsafe.compareAndSwapInt(target, 12, 3, 10));
            int b=(Integer)intFiled.get(target) ;
            System.out.println("改变之后的值是:"+b);
    //
    //        //这个时候已经改为10了,所以会返回false
            System.out.println(unsafe.compareAndSwapInt(target, 12, 3, 10));
    //
    //        System.out.println(unsafe.compareAndSwapObject(target, 24, null, "5"));
        }
    
    
    }
    class Target {
        int intParam=3;
        long longParam;
        String strParam;
        String strParam2;
    }
    
    
    
  • 相关阅读:
    luogu P3398 仓鼠找sugar
    关于lca
    luogu P3374 【模板】树状数组 1
    [NOIp2013普及组]车站分级
    [HDU1598]find the most comfortable road
    [NOI2015]程序自动分析
    [USACO08DEC]Secret Message
    [洛谷3375]【模板】KMP字符串匹配
    [ZJOI2010]网络扩容
    [SCOI2007]修车
  • 原文地址:https://www.cnblogs.com/bj-xiaodao/p/10790431.html
Copyright © 2011-2022 走看看