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;">





  • 相关阅读:
    SQL Server, Timeout expired.all pooled connections were in use and max pool size was reached
    javascript 事件调用顺序
    Best Practices for Speeding Up Your Web Site
    C语言程序设计 使用VC6绿色版
    破解SQL Prompt 3.9的几步操作
    Master page Path (MasterPage 路径)
    几个小型数据库的比较
    CSS+DIV 完美实现垂直居中的方法
    由Response.Redirect引发的"Thread was being aborted. "异常的处理方法
    Adsutil.vbs 在脚本攻击中的妙用
  • 原文地址:https://www.cnblogs.com/ziyue7575/p/12213729.html
Copyright © 2011-2022 走看看