zoukankan      html  css  js  c++  java
  • AtomicInteger类的理解与使用

    TOC

    AtomicInteger类的理解与使用

    参考: 

    • 定义:AtomicInteger是一个提供原子操作的Integer类,通过线程安全的方式操作加减。
    • 使用场景 :适合高并发情况下的使用

      AtomicInteger是在使用非阻塞算法实现并发控制,在一些高并发程序中非常适合,但并不能每一种场景都适合,不同场景要使用使用不同的数值类。

      注意:高并发的情况下,i++无法保证原子性,往往会出现问题,所以引入AtomicInteger

    • 部分源码:
    public class AtomicInteger extends Number implements java.io.Serializable {
        private static final long serialVersionUID = 6214790243416807050L;
    
        // 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;

    里value使用了volatile关键字,volatile在这里可以做到的作用是使得多个线程可以共享变量,但是问题在于使用volatile将使得VM优化失去作用,导致效率较低,所以要在必要的时候使用,因此AtomicInteger类不要随意使用,要在使用场景(高并发/多线程)下使用。


    对于全局变量的数值类型操作(比如多线程) num++,若没有加synchronized关键字则是线程不安全的.

    例如:

    public class AtomicIntegerTest1 {
        public static int count=0;
    
        public static void main(String[] args) {
            for (int i = 0; i < 10000; i++) {
                new Thread(){
                    @Override
                    public void run() {
                        count++;
                    }
                }.start();
            }
            System.out.println("count: "+count);
        }
    }

    输出: count: 8947

    明显数值错误,线程不安全

    若是使用volatile

    public class AtomicIntegerTest1 {
        static volatile int count=0;
    
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i < 100; i++) {
                new Thread(){
                    @Override
                    public void run() {
                        count++;
                    }
                }.start();
            }
    //        Thread.sleep(1000);
            System.out.println("count: "+count);
        }
    }

    输出:count: 61

    volatile仅仅保证变量在线程间保持可见性,却依然不能保证非原子性的操作

    使用AtomicInteger

    public class AtomicIntegerTest1 {
        static AtomicInteger count=new AtomicInteger(0);
    
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i < 100; i++) {
                new Thread(){
                    @Override
                    public void run() {
                        count.getAndIncrement();
                    }
                }.start();
            }
    //        Thread.sleep(1000);
            System.out.println("count: "+count);
        }
    }

    输出:count: 59

    方法

    方法定义
    AtomicInteger() 创建一个新的AtomicInteger,初始值为 0 。
    AtomicInteger(int initialValue) 用给定的初始值创建一个新的AtomicInteger
    int get() 获取当前值
    set(int newValue) 设置指定值: set()会立刻修改旧值,别的线程可以立刻看到更新后的值
    void lazySet(int newValue) 设置指定值:lazySet不会立刻(但是最终会)修改旧值,别的线程看到新值的时间会延迟一些
    int getAndSet(int newValue) 设置指定值并返回原来的值
    boolean compareAndSet(int expect, int update) 如果当前值等于入参expect,则把值设为update,并返回ture,如果不等则返回false
    weakCompareAndSet(int expect, int update) jdk1.9之前,与compareAndSet完全一致;
    1.9中: weakCompareAndSet有可能不是原子的去更新值,这取决于虚拟机的实现(效率高)
    int getAndIncrement() i++
    int getAndDecrement() i--
    int getAndAdd(int delta) 当前值加上delta,返回以前的值
    int incrementAndGet() ++i
    int decrementAndGet() --i
    addAndGet(int delta) 当前值加上delta,返回新的值
    int getAndUpdate(IntUnaryOperator updateFunction) 使用IntBinaryOperator 对当前值进行计算,并更新当前值,返回计算前的旧值 (比如:Math::max)
    int updateAndGet(IntUnaryOperator updateFunction) 使用IntBinaryOperator 对当前值进行计算,并更新当前值,返回计算后的新值
    int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction) 使用IntBinaryOperator 对当前值和x进行计算,并更新当前值,返回计算前的旧值
    int accumulateAndGet(int x, IntBinaryOperator accumulatorFunction) 使用IntBinaryOperator 对当前值和x进行计算,并更新当前值,返回计算后的新值
    /**
    * 演示AtomicInteger中1.8新增方法的使用方法
    */
    @Test
    public void operatorTest(){
        AtomicInteger i = new AtomicInteger(0);
        //lambda表达式中参数operand表示AtomicInteger的当前值
        int andUpdate = i.getAndUpdate(operand -> ++operand);
        System.out.println(andUpdate); //result: 0
        System.out.println(i.get()); //result: 1
    
        int i1 = i.updateAndGet(operand -> operand-2 );
        System.out.println(i1); //result: -1
        System.out.println(i.get()); //result: -1
    
        //lambda表达式中参数left表示AtomicInteger的当前值、right表示前面那个参数5
        int andAccumulate = i.getAndAccumulate(5, (left, right) -> left + right);
        System.out.println(andAccumulate); //result: -1
        System.out.println(i.get()); //result: 4
    
        int i2 = i.accumulateAndGet(4, (left, right) -> left + right);
        System.out.println(i2); //result: 8
        System.out.println(i.get()); //result: 8
    }

    案例

    • 使用案例1:
    public class AtomicTest {
    
        static long randomTime() {
            return (long) (Math.random() * 1000);
        }
    
        public static void main(String[] args) {
            // 阻塞队列,能容纳100个文件
            final BlockingQueue<Filequeue = new LinkedBlockingQueue<File>(100);
            // 线程池
            final ExecutorService exec = Executors.newFixedThreadPool(5);
            final File root = new File("D:\ISO");
            // 完成标志
            final File exitFile = new File("");
            // 原子整型,读个数
            // AtomicInteger可以在并发情况下达到原子化更新,避免使用了synchronized,而且性能非常高。
            final AtomicInteger rc = new AtomicInteger();
            // 原子整型,写个数
            final AtomicInteger wc = new AtomicInteger();
            // 读线程
            Runnable read = new Runnable() {
                public void run() {
                    scanFile(root);
                    scanFile(exitFile);
                }
    
                public void scanFile(File file) {
                    if (file.isDirectory()) {
                        File[] files = file.listFiles(new FileFilter() {
                            public boolean accept(File pathname) {
                                return pathname.isDirectory() || pathname.getPath().endsWith(".iso");
                            }
                        });
                        for (File one : files)
                            scanFile(one);
                    } else {
                        try {
                            // 原子整型的incrementAndGet方法,以原子方式将当前值加 1,返回更新的值
                            int index = rc.incrementAndGet();
                            System.out.println("Read0: " + index + " " + file.getPath());
                            // 添加到阻塞队列中
                            queue.put(file);
                        } catch (InterruptedException e) {
    
                        }
                    }
                }
            };
            // submit方法提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
            exec.submit(read);
    
            // 四个写线程
            for (int index = 0; index < 4; index++) {
                // write thread
                final int num = index;
                Runnable write = new Runnable() {
                    String threadName = "Write" + num;
    
                    public void run() {
                        while (true) {
                            try {
                                Thread.sleep(randomTime());
                                // 原子整型的incrementAndGet方法,以原子方式将当前值加 1,返回更新的值
                                int index = wc.incrementAndGet();
                                // 获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。
                                File file = queue.take();
                                // 队列已经无对象
                                if (file == exitFile) {
                                    // 再次添加"标志",以让其他线程正常退出
                                    queue.put(exitFile);
                                    break;
                                }
                                System.out.println(threadName + ": " + index + " " + file.getPath());
                            } catch (InterruptedException e) {
                            }
                        }
                    }
    
                };
                exec.submit(write);
            }
            exec.shutdown();
        }
    
    }
    • 使用案例2:
    public class TestAtomicInteger {
        private static final int THREADS_COUNT = 2;
    
        public static int count = 0;
        public static volatile int countVolatile = 0;
        public static AtomicInteger atomicInteger = new AtomicInteger(0);
        public static CountDownLatch countDownLatch = new CountDownLatch(2);
    
        public static void increase() {
            count++;
            countVolatile++;
            atomicInteger.incrementAndGet();
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread[] threads = new Thread[THREADS_COUNT];
            for(int i = 0; i< threads.length; i++) {
                threads[i] = new Thread(() -> {
                    for(int i1 = 0; i1 < 1000; i1++) {
                        increase();
                    }
                    countDownLatch.countDown();
                });
                threads[i].start();
            }
    
            countDownLatch.await();
    
            System.out.println(count);
            System.out.println(countVolatile);
            System.out.println(atomicInteger.get());
        }
    }

    <wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">





  • 相关阅读:
    学习笔记CSS
    悲剧,当用cywin 写Linux脚本
    .net中控件的命名规则和一些词语的简称(转)(I)
    PyMining开源中文文本数据挖掘平台 Ver 0.2发布
    TCP和UDP的区别(转)
    发一道我今天遇到C面试题(求完美解)
    C#三种定时器的实现转载
    window 拷贝 linux 远程
    datepicker 日月年
    Oracle PL/SQL练习题总目录 hl3292
  • 原文地址:https://www.cnblogs.com/ziyue7575/p/12213729.html
Copyright © 2011-2022 走看看