zoukankan      html  css  js  c++  java
  • 原子类

    1.大纲

       作用

      6类原子类

      Atomic*基本类型的原子类

      Atomic*Array数组类型原子类

      Atomic*Reference引用类型原子类

      普通变量升级为原子类

      Adder累加器

      Accumulator累加器

    一:作用

    1.说明

      一个操作是不可中断的,即使是多线程的情况下也可以保证

      为了保证并发下的线程安全

    2.主要特点

      粒度更细,原子变量可以把竞争范围缩小到变量级别

      效率更高,使用原子类比使用锁的效率更高,除了高度竞争的情况

      

    二:六类原子类

    1.

      

    三:基本型原子类

    1.AtomicInteger常用方法

      get:获取当前的值

      getAndSet:获取当前的值,并设置新的值

      getAndIncrement:获取当前的值,并自增

      getAndDecremrnt:获取当前值,并自减

      getAndAdd(n):获取当前的值,并加上预期的值

      boolean compareAndSet(sepect,update):如果输入的等于预期值,则以原子方式将该值设置为输入值

    2.举例

    package com.jun.juc.lock.atomic;
    
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class AtomicIntegerDemo implements Runnable{
        private static final AtomicInteger atomicInteger = new AtomicInteger();
        private static volatile int basicCount = 0;
    
        public void invrementAtomic(){
            atomicInteger.getAndIncrement();
        }
    
        public void incrementBasic(){
            basicCount ++ ;
        }
    
    
        @Override
        public void run() {
            for (int i=0;i<10000;i++){
                invrementAtomic();
                incrementBasic();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            AtomicIntegerDemo demo = new AtomicIntegerDemo();
            Thread th1 = new Thread(demo);
            Thread th2 = new Thread(demo);
            th1.start();
            th2.start();
            th1.join();
            th2.join();
            System.out.println("原子类的结果是"+atomicInteger.get());
            System.out.println(basicCount);
        }
    }
    

      效果:

    Disconnected from the target VM, address: '127.0.0.1:59679', transport: 'socket'
    原子类的结果是20000
    19097
    
    Process finished with exit code 0
    

      

    四:AtomicArray

    1.示例

    package com.jun.juc.lock.atomic;
    
    import java.util.concurrent.atomic.AtomicIntegerArray;
    
    public class AtomicArrayDemo {
        public static void main(String[] args) {
            AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(1000);
            // 初始化
            Thread[] threadsIn = new Thread[100];
            Thread[] threadsDe = new Thread[100];
            //
            Decrement decrement = new Decrement(atomicIntegerArray);
            Increment increment = new Increment(atomicIntegerArray);
            for (int i=0;i<100;i++){
                threadsDe[i] = new Thread(decrement);
                threadsIn[i] = new Thread(increment);
                threadsDe[i].start();
                threadsIn[i].start();
            }
    
            for (int i=0;i<100;i++){
                try {
                    threadsDe[i].join();
                    threadsIn[i].join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            for (int i=0; i<atomicIntegerArray.length();i++){
                if(atomicIntegerArray.get(i)!=0){
                    System.out.println("有问题"+i);
                }
            }
            System.out.println("结束");
        }
    }
    
    class Decrement implements Runnable{
        private AtomicIntegerArray atomicIntegerArray;
    
        public Decrement(AtomicIntegerArray atomicIntegerArray){
            this.atomicIntegerArray = atomicIntegerArray;
        }
        @Override
        public void run() {
            for (int i=0;i<atomicIntegerArray.length();i++){
                atomicIntegerArray.getAndDecrement(i);
            }
        }
    }
    
    class Increment implements Runnable{
        private AtomicIntegerArray atomicIntegerArray;
    
        public Increment(AtomicIntegerArray atomicIntegerArray){
            this.atomicIntegerArray = atomicIntegerArray;
        }
        @Override
        public void run() {
            for (int i=0;i<atomicIntegerArray.length();i++){
                atomicIntegerArray.getAndIncrement(i);
            }
        }
    }
    

      效果:

    Connected to the target VM, address: '127.0.0.1:55387', transport: 'socket'
    Disconnected from the target VM, address: '127.0.0.1:55387', transport: 'socket'
    结束
    
    Process finished with exit code 0
    

      

    五:引用类型的原子类

    1.说明

      可以让一个对象保证原子性

    2.示例

      以前写过的自旋锁,可以保证对象的安全性

    package com.jun.juc.lock.spinlock;
    
    import java.util.concurrent.atomic.AtomicReference;
    
    /**
     * 自旋锁
     */
    public class SpinLock {
        private AtomicReference<Thread> sign = new AtomicReference<Thread>();
    
        public void lock(){
            Thread current = Thread.currentThread();
            while (!sign.compareAndSet(null, current)){
                System.out.println(Thread.currentThread().getName() + "获取失败,再次尝试");
            }
        }
    
        public void unlock(){
            Thread current = Thread.currentThread();
            sign.compareAndSet(current, null);
        }
    
        public static void main(String[] args) {
            SpinLock spinLock = new SpinLock();
    
            Runnable runnable = new Runnable(){
                public void run(){
                    System.out.println(Thread.currentThread().getName() + "尝试获取自旋锁");
                    spinLock.lock();
                    System.out.println(Thread.currentThread().getName() +"获取到了自旋锁");
                    try{
                        Thread.sleep(300);
                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {
                        System.out.println(Thread.currentThread().getName() +"释放到了自旋锁");
                        spinLock.unlock();
                    }
                }
            };
    
            Thread thread1 = new Thread(runnable);
            Thread thread2 = new Thread(runnable);
            thread1.start();
            thread2.start();
    
        }
    }
    

      

    六:原子类

    1.Adder累加器

      是java8新引入的

      并发下,LongAdder比ATomICLong的效果更高,是空间换时间

      在竞争激烈的情况下,LongAdder把不同的线程对象到不同的Cell上进行修改,降低了冲突的概率,是多段锁的理念,提高了并发

    2.AtomicLong

      这里使用了shutdown方法

    package com.jun.juc.lock.atomic;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.atomic.AtomicLong;
    
    public class AtomicLongDemo {
        public static void main(String[] args) {
            AtomicLong atomicLong = new AtomicLong(0);
            ExecutorService executorService = Executors.newFixedThreadPool(20);
            long start = System.currentTimeMillis();
            for (int i=0; i<10000;i++){
                executorService.submit(new Task(atomicLong));
            }
    
            executorService.shutdown();
            while (!executorService.isTerminated()){
                // 没有完成,则进行等待
            }
            long end = System.currentTimeMillis();
            System.out.println(end-start);
        }
    
        private static class Task implements Runnable{
            private AtomicLong counter;
            public  Task(AtomicLong counter){
                this.counter = counter;
            }
            @Override
            public void run() {
                for (int i=0;i<10000;i++){
                    counter.incrementAndGet();
                }
            }
        }
    }
    

      效果:

    Connected to the target VM, address: '127.0.0.1:56230', transport: 'socket'
    Disconnected from the target VM, address: '127.0.0.1:56230', transport: 'socket'
    1915
    
    Process finished with exit code 0
    

      

    3.LongAdder的使用

    package com.jun.juc.lock.atomic;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.atomic.AtomicLong;
    import java.util.concurrent.atomic.LongAdder;
    
    public class LongAdderDemo {
        public static void main(String[] args) {
            LongAdder longAdder = new LongAdder();
            ExecutorService executorService = Executors.newFixedThreadPool(20);
            long start = System.currentTimeMillis();
            for (int i=0; i<10000;i++){
                executorService.submit(new Task(longAdder));
            }
    
            executorService.shutdown();
            while (!executorService.isTerminated()){
                // 没有完成,则进行等待
            }
            long end = System.currentTimeMillis();
            System.out.println(end-start);
        }
    
        private static class Task implements Runnable{
            private LongAdder counter;
            public  Task(LongAdder counter){
                this.counter = counter;
            }
            @Override
            public void run() {
                for (int i=0;i<10000;i++){
                    counter.increment();
                }
            }
        }
    }
    

      效果:

    Connected to the target VM, address: '127.0.0.1:56388', transport: 'socket'
    Disconnected from the target VM, address: '127.0.0.1:56388', transport: 'socket'
    207
    
    Process finished with exit code 0
    

      

    4.原因

      AtomicLong每次都要flush和refesh,每一次加法都要进行同步,所以在高并发的时候导致冲突比较多,就降低了效率

      LongAdder,在每个线程中都有自己的一个计数器,仅用来在自己的线程内计数,这样一来就不会和其他线程计数器进行干扰

      要咋汇总呢?

      LongAdder引入了分段累加的概念,内部有一个base、变量和Cell【】数组共同参与计数:

      base变量:竞争不激烈,直接累加到该变量上

      Cell:竞争激烈,各个线程分散累加到自己的槽Cell[i]中

      

    5.sum源码

        public long sum() {
            Cell[] as = cells; Cell a;
            long sum = base;
            if (as != null) {
                for (int i = 0; i < as.length; ++i) {
                    if ((a = as[i]) != null)
                        sum += a.value;
                }
            }
            return sum;
        }
    

      但是有点小问题:

      每个小cell可能都有变化,导致sum不准

    6.适用场合

      LongAdder适用的场景是统计求和,只提供了add方法

      AtomicLong还具有cas方法

    七:Accumulator累加器

    1.与Adder相比较

      更加的通用

    2.示例

      可以发现很灵活

    package com.jun.juc.lock.atomic;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.atomic.LongAccumulator;
    import java.util.stream.IntStream;
    
    public class LongAccumulatorDemo {
        public static void main(String[] args) {
            LongAccumulator accumulator = new LongAccumulator((x,y)-> x + y, 1);
            ExecutorService executorService = Executors.newFixedThreadPool(8);
            IntStream.range(1, 10).forEach(i -> executorService.submit(()-> accumulator.accumulate(i)));
            executorService.shutdown();
            while (!executorService.isTerminated()){
    
            }
            System.out.println(accumulator.getThenReset());
        }
    }
    

      效果:

    Connected to the target VM, address: '127.0.0.1:57694', transport: 'socket'
    Disconnected from the target VM, address: '127.0.0.1:57694', transport: 'socket'
    46
    
    Process finished with exit code 0
    

      

    3.另一个特征

      可以发现,上面是可以多线程的计算

      同时,对顺序有没要求

  • 相关阅读:
    过滤器
    JSTL自定义标签
    EL表达式自定义函数
    和 区别
    JSTL标签
    jsp内置对象
    Java堆、栈和常量池以及相关String的详细讲解(转)
    jsp和servlet学习总结
    JAVA多线程实现的两种方式
    redis示例
  • 原文地址:https://www.cnblogs.com/juncaoit/p/13056756.html
Copyright © 2011-2022 走看看