zoukankan      html  css  js  c++  java
  • AtomicInteger关键字

    validate 关键字可以保证多线程之间的可见性,但是不能保证原子操作。(需要了解java内存模型jmm)

    package com.cn.test.thread;
    
    
    public class VolatileAutomic extends Thread{
        private volatile static int count = 0;
        public  static void add() {
           for (int i = 0; i < 10000; i++) {
                 count ++;
            }
        }
        
        @Override
        public void run() {
            add();
        }
        
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new VolatileAutomic().start();
            }
            while (Thread.activeCount() > 1) {
                Thread.yield();
            }
        //    System.out.println(atomicCount.get());
            System.out.println("count=" + count);
        }
    }

     运行结果:

    count=61618

        上面例子中volatile关键字能保证可见性没有错,但是上面的程序错在没能保证原子性。可见性只能保证每次读取的是最新的值,但是volatile没办法保证对变量的操作的原子性。

    count++是一个非原子性的操作,它包括读取变量的原始值、进行加1操作、写入工作内存。那么就是说自增操作的三个子操作可能会分割开执行,就有可能导致的情况:

    假如某个时刻变量count的值为10,线程1对变量进行自增操作,线程1先读取了变量count的原始值,然后线程1被阻塞了;线程2也对变量进行自增操作,线程2先读取了count的原始值,线程1只是进行了读取操作,没有进行写的操作,所以不会导致线程2中的本地缓存无效,因此线程2进行++操作,在把结果刷新到主存中去,此时线程1在还是读取原来的10 的值在进行++操作,所以线程1和线程2对于count=10进行两次++操作,结果都为11.。

      上述问题解决方法:

                                1.采用add方法中加入sychnorized.,或者同步代码块

                                2.采用AtomicInteger

    package com.cn.test.thread;
    
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class VolatileAutomic extends Thread{
        static AtomicInteger atomicCount = new AtomicInteger(0);
        public  static void add() {
            for (int i = 0; i < 10000; i++) {
                atomicCount.incrementAndGet();
            }
        }
        
        @Override
        public void run() {
            add();
        }
        
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new VolatileAutomic().start();
            }
            while (Thread.activeCount() > 1) {
                Thread.yield();
            }
            System.out.println(atomicCount.get());
        }
    }

    运行结果:

    100000

    AtomicInteger是java.util.concurrent.atomic并发包下面一个类。

    AtomicInteger源码理解:

    static {
            try {
                valueOffset = unsafe.objectFieldOffset
                    (AtomicInteger.class.getDeclaredField("value"));
            } catch (Exception ex) { throw new Error(ex); }
        }
      /**
         * Creates a new AtomicInteger with the given initial value.
         *
         * @param initialValue the initial value
         */
        public AtomicInteger(int initialValue) {
            value = initialValue;
        }

    调用AtomicInteger的构造方法传入一个initialValue值,将initialValue复制给一个volatile 的value,

     /**
         * Atomically increments by one the current value.
         *
         * @return the updated value
         */
        public final int incrementAndGet() {
            return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
        }
    public final int getAndAddInt(Object var1, long var2, int var4) {
    		int var5;
    		do {
    			var5 = this.getIntVolatile(var1, var2);
    		} while (!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    
    		return var5;
    	}

    底层源码实现主要采用cas算法的思想,通过操作底层硬件的指令。cas锁是一种无锁的机制。cas的机制:v的值应该是A,如果是A则将值更新为B。当多个线程同时要更新V的值,

    只有其中一个线程更新成功,其他线程更新失败,更新失败的线程并不会挂起,而是在继续进行竞争资源,对V的值进行操作。

    cas锁的机制采用的是事物提交-- 失败--重试来保证原子性。

    cas锁的缺点:当并发量大的时候,多线程一直进行重试,这样会导致CPU的开销很大。 

    package com.cn.test.thread;
    
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * 设计4个线程,其中两个线程  每次对j增加1,另外两个线程对j每次减少1。写出程序。
     *
     */
    public class TestAtomicThread extends Thread {
        private static int j = 10;
        static AtomicInteger atomicInteger = new AtomicInteger(j);
        public TestAtomicThread(String string) {
            super(string);
        }
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
            switch(Thread.currentThread().getName()) {
               case "thread-1": 
               case "threa-2": 
                   atomicInteger.incrementAndGet(); 
                  // System.out.println("当前线程名称:" + Thread.currentThread().getName() + ":"  + atomicInteger.get());
                     break;
               case "thread-3":
               case "thread-4": atomicInteger.decrementAndGet();
                  //   System.out.println("当前线程名称:" + Thread.currentThread().getName() + ":"  + atomicInteger.get());
                     break;
               default : break;
            }
        }
    
        public static void main(String[] args) {
          TestAtomicThread t1 = new TestAtomicThread("thread-1");
          TestAtomicThread t2 = new TestAtomicThread("thread-2");
          TestAtomicThread t3 = new TestAtomicThread("thread-3");
          TestAtomicThread t4 = new TestAtomicThread("thread-4");
          t1.start();
          t2.start();
          t3.start();
          t4.start();
          if (Thread.activeCount() > 1) {
              Thread.yield();
          }
          System.out.println("j====" + atomicInteger.get());
          
    
        }
    
    }
  • 相关阅读:
    高斯消元模板
    hdu4210 Sudominoku (dfs)
    Linux 下eclipse cpp配置libvlc环境
    PaddleDetection 导出PPYOLO 类型模型时报错AssertionError: Bad argument number for Assign: 2, expecting 3 解决记录
    qt 记录调用setStyleSheet设置样式后不生效问题
    ubuntu 在docker中使用gpu,安装nvidiacontainerruntime
    Linux 下使用libvlc 播放视频 C++
    QT 通过installEventFilter实现监控控件鼠标移入移出效果
    qt 记录某些控件 debug样式正常, release 样式不正常问题
    QT 记录一次窗口构造函数中访问UI控件报错问题
  • 原文地址:https://www.cnblogs.com/startSeven/p/10223736.html
Copyright © 2011-2022 走看看