精华内容
下载资源
问答
  • 原子类

    2020-06-19 20:49:24
    原子类

    原子类

    1.概念
    原子是世界上的最小单位,具有不可分割性,原子类指的是这个类具有原子操作,这个操作不可分割,Java中的原子类在java.util.concurrent.atomic包下,这些类都是使用非阻塞的CAS算法实现的。

    具体的测试代码:原子类的操作
    2.原子更新基本类型
    使用原子的方式更新基本类型,Atomic包有以下几个类:

    • AtomicInteger (原子更新整形)
    • AtomicBoolean(原子更新布尔类型)
    • AtomicLong(原子更新长整形)
      以AtomicInteger为例,它的内部通过UnSafe来实现,具体代码如下:
     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();//获得Unsafe实例
        private static final long valueOffset;//存放value的偏移量
        static {
            try {
                valueOffset = unsafe.objectFieldOffset
                    (AtomicInteger.class.getDeclaredField("value"));
            } catch (Exception ex) { throw new Error(ex); }
        }
        private volatile int value;//实际变量值,用volatile保证了内存的可见性
         public AtomicInteger(int initialValue) {
            value = initialValue;//初始化value值
        }
        ......
    

    这里说明一下Unsafe类
    Unsafe类可以直接操作内存,所以在高并发条件下它的效率很高,但是因为可以直接操作内存,就无法通过jvm来回收,必须手动回收,并且Unsafe类会修改内存中对象的地址,使用不当会使程序崩溃。

    public final class Unsafe {  //声明这个类为final,表明不可继承
      private static final Unsafe theUnsafe;  //声明thUnSafe引用
      public static final int INVALID_FIELD_OFFSET = -1;
      public static final int ARRAY_BOOLEAN_BASE_OFFSET;//布尔数组的基本偏移量
     ...
      public static final int ARRAY_BOOLEAN_INDEX_SCALE;//布尔数组的比例因子
      ...
       public static final int ADDRESS_SIZE;
    
        private static native void registerNatives();//注册native方法,用native表明实现不是通过java而是通过c或者c++等其他语言
    
        private Unsafe() {
        }//构造方法设为私有,则无法直接new获得实例对象
    
        @CallerSensitive
        public static Unsafe getUnsafe() {  //通过反射机制来获得Unsafe实例
            Class var0 = Reflection.getCallerClass();
            if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
                throw new SecurityException("Unsafe");
            } else {
                return theUnsafe;
            }
        }
     }
    

    Unsafe初始化主要通过getUnsafe方法的单例模式实现的,通过Reflection类的getCallerClass判断当前的类是否是有主类加载器加载的,不是的话就抛出异常。

    CAS(compare and swap)即比较和交换,CAS是一种无锁算法,它有内存值V,旧的预期值A,新值B,只有当旧的预期值A和内存值V相等,才修改为B的值。

    Unsafe中的CAS机制的方法:

    public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
    public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
    

    来看AtomicInteger的几个主要函数:

        public final void set(int newValue) {
            value = newValue;
        }//设置值为给定值
        public final void lazySet(int newValue) {
            unsafe.putOrderedInt(this, valueOffset, newValue);
        }//设置值,但是不保证写完之后对其他线程可见
        public final int getAndIncrement() {
            return unsafe.getAndAddInt(this, valueOffset, 1);
        } //以原子方式将当前值加一,返回的是旧值
        public final int getAndDecrement() {
            return unsafe.getAndAddInt(this, valueOffset, -1);
        }//以原子方式将当前值减一,返回的是旧值
        public final int getAndAdd(int delta) {
            return unsafe.getAndAddInt(this, valueOffset, delta);
        }//以原子方式将当前值加给定值,返回的是旧值
        public final int incrementAndGet() {
            return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
        } //以原子方式将当前值加一,返回的是加一后的值
        public final int decrementAndGet() {
            return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
        }//以原子方式将当前值减一,返回的是减一后的值
        public final int addAndGet(int delta) {
            return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
        }//以原子方式将当前值加给定值,返回的是改变后的值
    

    3.原子更新数组

    • AtomicIntegerArray (原子更新整型数组里的元素)
    • AtomicReferenceArray (原子更新引用类型数组里的元素)
    • AtomicLongArray(原子更新长整型数组里的元素)

    以AtomicReferenceArray为例:

    public class AtomicReferenceArray<E> implements java.io.Serializable {
        private static final long serialVersionUID = -6209656149925076980L;
        private static final Unsafe unsafe; //声明unsafe引用
        private static final int base; //基础地址
        private static final int shift;  //偏移量
        private static final long arrayFieldOffset; //array数组的偏移量
        private final Object[] array;  //对象数组
    
        static {
            try {
                unsafe = Unsafe.getUnsafe();
                arrayFieldOffset = unsafe.objectFieldOffset
                    (AtomicReferenceArray.class.getDeclaredField("array"));
                base = unsafe.arrayBaseOffset(Object[].class);
                int scale = unsafe.arrayIndexScale(Object[].class);//获取数组元素占数组的大小
                if ((scale & (scale - 1)) != 0)//判断其是否为2的幂次方
                    throw new Error("data type scale not a power of two");
                shift = 31 - Integer.numberOfLeadingZeros(scale);
            } catch (Exception e) {
                throw new Error(e);
            }
        }
        private long checkedByteOffset(int i) {
            if (i < 0 || i >= array.length)
                throw new IndexOutOfBoundsException("index " + i);
            return byteOffset(i);
        }//先检查索引是否超出数组,是就抛出异常,不是的话查找下标为i的偏移量
        private static long byteOffset(int i) {
            return ((long) i << shift) + base;
        }//查找下标为i的偏移量
        public AtomicReferenceArray(int length) {
            array = new Object[length];
        }//构造方法,以指定长度实例化数组
        public AtomicReferenceArray(E[] array) {
            // Visibility guaranteed by final field guarantees
            this.array = Arrays.copyOf(array, array.length, Object[].class);
        }
    }
    

    常用方法:

    public final E get(int i) {
            return getRaw(checkedByteOffset(i));
    }//得到下标为i的元素
    public final void set(int i, E newValue) {
            unsafe.putObjectVolatile(array, checkedByteOffset(i), newValue);
    }//设置下标为i为给定值
    public final E getAndSet(int i, E newValue) {
            return (E)unsafe.getAndSetObject(array, checkedByteOffset(i), newValue);
    }//设置下标为i为给定值,并且返回旧值
    public final boolean compareAndSet(int i, E expect, E update) {
            return compareAndSetRaw(checkedByteOffset(i), expect, update);
    }//如果下标为i的值与期望值相同,就更新值,返回true,否则返回false
    

    4.原子更新引用类型

    • AtomicReference (原子更新引用类型)
    • AtomicReferenceFieldUpdater(原子更新引用类型的字段)
    • AtomicMarkableReferce(原子更新带有标记位的引用类型)

    这三个类和AtomicInteger等类似,只是数据类型换成引用数据类型。

    5.原子更新字段

    • AtomicIntegerFieldUpdater(原子更新整型的字段的更新器)
    • AtomicLongFieldUpdater(原子更新长整型的字段的更新器)
    • AtomicStampedReference(原子更新带有版本号的引用类型)
    • AtomicReferenceFieldUpdater(原子更新引用类型的字段的更新器)

    除了AtomicStampedReference,其他三个都为抽象类,所以它们需要使用newUpdater方法创建更新器,并且更新类的字段时需要使用volatile关键字修饰。

    6.jdk1.8出现的操作类

    • DoubleAccumulator
    • DoubleAdder
    • LongAccumulator
    • LongAdder

    以LongAdder为例,它与普通的AtomicLong的区别主要体现在在高并发时对单一变量的操作转换成cells数组中多个元素的CAS操作,取值时进行求和。在低并发的情况下与AtomicLong原理相同。

    使用场景:普通变量在高并发情况下,进行加减操作可能非线程安全的,可以通过原子类的操作保证线程安全,它比使用锁性能上更好。

    展开全文
  • AtomicInteger,AtomicLong,AtomicReference对boolean,Integer,long,reference定义的变量进行原子性的操作,简单点说就是它提供的方法都是原子性的,不会出现线程之间存在的问题二、原子类更新...AtomicLongFieldUpdater...

    一、基本类原子操作

    AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference对boolean,Integer,long,reference定义的变量进行原子性的操作,

    简单点说就是它提供的方法都是原子性的,不会出现线程之间存在的问题

    二、原子类更新

    AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUpdater

    (1)字段必须是volatile类型

    (2)调用者能够直接操作对象字段,那么就可以反射进行原子操作。但是对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段。

    (3)只能是实例变量,不能是类变量,也就是说不能加static关键字。

    (4)只能是可修改变量,不能使final变量,因为final的语义就是不可修改。

    (5)对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。

    如果要修改包装类型就需要使用AtomicReferenceFieldUpdater。

    static classDemoData {volatile int value1 = 1;volatile int value2 = 2;private volatile int value3 = 3;volatile int value4 = 4;

    }static AtomicIntegerFieldUpdatergetUpdater(String fieldName) {return AtomicIntegerFieldUpdater.newUpdater(DemoData.class, fieldName);

    }public static voidmain(String[] args) {

    DemoData data= newDemoData();//getAndSet() Atomically sets the field of the given object managed by this updater to the given value and returns the old value.

    System.out.println("1 ==> " + getUpdater("value1").getAndSet(data, 10));

    System.out.println("2 ==> " + getUpdater("value2").incrementAndGet(data));

    System.out.println("3 ==> " + getUpdater("value3").decrementAndGet(data)); //报错

    System.out.println("4 ==> " + getUpdater("value4").compareAndSet(data, 4, 5));

    }

    四、带有标记位的对象引用

    AtomicMarkableReference类描述的一个的对,可以原子的修改Object或者Boolean的值,这种数据结构在一些缓存或者状态描述中比较有用。

    这种结构在单个或者同时修改Object/Boolean的时候能够有效的提高吞吐量。 AtomicStampedReference类维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。

    对比AtomicMarkableReference类的,AtomicStampedReference维护的是一种类似的数据结构,其实就是对象(引用)的一个并发计数。

    但是与AtomicInteger不同的是,此数据结构可以携带一个对象引用(Object),并且能够对此对象和计数同时进行原子操作。

    四。使用场景

    展开全文
  • 目录一、Atomic 原子类介绍二、基本类型原子类1.基本类型原子类介绍2. AtomicInteger常见方法使用3.基本数据类型原子类的优势4. AtomicInteger线程安全原理简单分析三、数组类型原子类1.数组类型原子类介绍2. ...

    一、Atomic 原子类介绍

    Atomic翻译成中文是原子的意思。在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的。在我们这里 Atomic是指一个操作是不可中断的即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。

    所以,所谓原子类说简单点就是具有原子/原子操作特征的类。

    并发包java.util.concurrent的原子类都存放在java.util.concurrent.atomic下,如下图所示。

    根据操作的数据类型,可以将JUC包中的原子类分为4类

    基本类型

    使用原子的方式更新基本类型

    • AtomicInteger整型原子类
    • AtomicLong长整型原子类
    • AtomicBoolean布尔型原子类

    数组类型

    使用原子的方式更新数组里的某个元素

    • AtomicIntegerArray整型数组原子类
    • AtomicLongArray长整型数组原子类
    • AtomicReferenceArray引用类型数组原子类

    引用类型

    • AtomicReference引用类型原子类
    • AtomicMarkableReference原子更新带有标记的引用类型。该类将boolean标记与引用关联起来,也可以解决使用CAS进行原子更新时可能出现的ABA问题。
    • AtomicStampedReference原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用CAS进行原子更新时可能出现的ABA问题。

    对象的属性修改类型

    • AtomicIntegerFieldUpdater:原子更新整型字段的更新器
    • AtomicLongFieldUpdater原子更新长整型字段的更新器
    • AtomicReferenceFieldUpdater原子更新引用类型里的字段

    修正: AtomicMarkableReference不能解决ABA问题

    /**
    AtomicMarkableReference是将一个boolean值作是否有更改的标记,本质就是它的版本号只有两个, true和false, 
    修改的时候在这两个版本号之间来回切换,这样做并不能解决ABA的问题,只是会降低ABA问题发生的几率而已
    @author : mazh 
    @Date : 2020/12/9 14:41 
    */
    
    public class SolveABAByAtomicMarkableReference {
    		private static AtomicMarkableReference atomicMarkableReference = new 
    AtomicMarkableReference(100, false);
    		public static void main(String[] args) { 
    		
    			Thread refT1 = new Thread(() -> { 
    				try {TimeUnit.SECONDS.sleep(1); 
    				} catch (InterruptedException e) {
    					 e.printStackTrace(); 
    				}
    				atomicMarkableReference.compareAndSet(100, 101, 
    				atomicMarkableReference.isMarked(), !atomicMarkableReference.isMarked());
    				atomicMarkableReference.compareAndSet(101, 100, 
    				atomicMarkableReference.isMarked(), !atomicMarkableReference.isMarked());
    			});
    			Thread refT2 = new Thread(() -> { 
    				boolean marked = atomicMarkableReference.isMarked(); 
    				try {
    					TimeUnit.SECONDS.sleep(2); 
    				} catch (InterruptedException e) { 
    					e.printStackTrace(); 
    				}
    				boolean c3 = atomicMarkableReference.compareAndSet(100, 101, 
    marked, !marked);
    				System.out.println(c3); // 返回true,实际应该返回false 
    				}); 
    
    				refT1.start(); 
    				refT2.start(); 
    			} 
    		}
    

    CAS ABA 问题

    • 描述: 第一个线程取到了变量 x 的值 A,然后巴拉巴拉干别的事,总之就是只拿到了变量 x 的值A。这段时间内第二个线程也取到了变量 x 的值 A,然后把变量 x 的值改为 B,然后巴拉巴拉干别的事,最后又把变量 x 的值变为 A (相当于还原了)。在这之后第一个线程终于进行了变量 x 的操作,但是此时变量 x 的值还是 A,所以compareAndSet操作是成功。
    • 例子描述(可能不太合适,但好理解): 年初,现金为零,然后通过正常劳动赚了三百万,之后正常消费了(比如买房子)三百万。年末,虽然现金零收入(可能变成其他形式了),但是赚了钱是事实,还是得交税的!
    • 代码例子(以AtomicInteger为例)
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class AtomicIntegerDefectDemo { 
    		public static void main(String[] args) { 
    			defectOfABA(); 
    		}
    		
    		static void defectOfABA() { 
    			final AtomicInteger atomicInteger = new AtomicInteger(1);
    			
    			Thread coreThread = new Thread( 
    					() -> { 
    						final int currentValue = atomicInteger.get(); 
    						System.out.println(Thread.currentThread().getName() + " ---- 
    -- currentValue=" + currentValue);
    
    						// 这段目的:模拟处理其他业务花费的时间
    						try {
    							Thread.sleep(300); 
    						} catch (InterruptedException e) {
    						 	e.printStackTrace(); 
    						 }
    						 
    						 boolean casResult = atomicInteger.compareAndSet(1, 2);
    						 System.out.println(Thread.currentThread().getName() 
    						 		+ " ------ currentValue=" + currentValue 
    						 		+ ", finalValue=" + atomicInteger.get() 
    						 		+ ", compareAndSet Result=" + casResult); 
    				} 
    		);
    		coreThread.start();
    
    		// 这段目的:为了让 coreThread 线程先跑起来
    		try {
    			Thread.sleep(100); 
    		} catch (InterruptedException e) { 
    			e.printStackTrace(); 
    		}
    		
    		Thread amateurThread = new Thread( 
    				() -> { 
    					int currentValue = atomicInteger.get(); 
    					boolean casResult = atomicInteger.compareAndSet(1, 2); 
    					System.out.println(Thread.currentThread().getName() 
    							+ " ------ currentValue=" + currentValue 
    							+ ", finalValue=" + atomicInteger.get() 
    							+ ", compareAndSet Result=" + casResult);
    							 
    					currentValue = atomicInteger.get(); 
    					casResult = atomicInteger.compareAndSet(2, 1); 
    					System.out.println(Thread.currentThread().getName() 
    							+ " ------ currentValue=" + currentValue 
    							+ ", finalValue=" + atomicInteger.get() 
    							+ ", compareAndSet Result=" + casResult); 
    				} 
    		);
    		amateurThread.start();
    	} 
    }
    

    输出内容如下:

    Thread-0 ------ currentValue=1 
    Thread-1 ------ currentValue=1, finalValue=2, compareAndSet Result=true 
    Thread-1 ------ currentValue=2, finalValue=1, compareAndSet Result=true 
    Thread-0 ------ currentValue=1, finalValue=2, compareAndSet Result=true
    

    下面我们来详细介绍一下这些原子类。

    二、基本类型原子类

    1.基本类型原子类介绍

    使用原子的方式更新基本类型

    • AtomicInteger整型原子类
    • AtomicLong长整型原子类
    • AtomicBoolean布尔型原子类

    上面三个类提供的方法几乎相同,所以我们这里以AtomicInteger为例子来介绍。
    AtomicInteger 类常用方法

    public final int get() //获取当前的值 
    public final int getAndSet(int newValue)//获取当前的值,并设置新的值 
    public final int getAndIncrement()//获取当前的值,并自增 
    public final int getAndDecrement() //获取当前的值,并自减 
    public final int getAndAdd(int delta) //获取当前的值,并加上预期的值 
    boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式 将该值设置为输入值(update) 
    public final void lazySet(int newValue)//最终设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
    

    2. AtomicInteger常见方法使用

    import java.util.concurrent.atomic.AtomicInteger;
    
    public class AtomicIntegerTest {
    		public static void main(String[] args) { 
    			// TODO Auto-generated method stub 
    			int temvalue = 0; 
    			AtomicInteger i = new AtomicInteger(0); 
    			temvalue = i.getAndSet(3); 
    			System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:0; i:3 
    			temvalue = i.getAndIncrement(); 
    			System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:3; i:4 
    			temvalue = i.getAndAdd(5); 
    			System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:4; i:9 
    	}
    }
    

    3.基本数据类型原子类的优势

    通过一个简单例子带大家看一下基本数据类型原子类的优势

    ①多线程环境不使用原子类保证线程安全(基本数据类型)

    class Test { 
    		private volatile int count = 0; 
    		//若要线程安全执行执行count++,需要加锁 
    		public synchronized void increment() { 
    				count++; 
    		}
    		
    		public int getCount() { 
    				return count; 
    		} 
    }
    

    ②多线程环境使用原子类保证线程安全(基本数据类型)

    class Test2 { 
    		private AtomicInteger count = new AtomicInteger(); 
    		
    		public void increment() { 
    				count.incrementAndGet(); } 
    		//使用AtomicInteger之后,不需要加锁,也可以实现线程安全。 
    		public int getCount() { 
    				return count.get(); 
    		} 
    }
    

    4. AtomicInteger线程安全原理简单分析

    AtomicInteger类的部分源码:

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

    AtomicInteger主要利用CAS (compare and swap) + volatilenative方法来保证原子操作,从而避免synchronized的高开销,执行效率大为提升。

    CAS的原理是拿期望的值和原本的一个值作比较,如果相同则更新成新的值。 UnSafe类objectFieldOffset()方法是一个本地方法,这个方法是用来拿到“原来的值”的内存地址。另外value是一个volatile变量,在内存中可见,因此JVM可以保证任何时刻任何线程总能拿到该变量的最新值。

    三、数组类型原子类

    1.数组类型原子类介绍

    使用原子的方式更新数组里的某个元素

    • AtomicIntegerArray整形数组原子类
    • AtomicLongArray长整形数组原子类
    • AtomicReferenceArray引用类型数组原子类

    上面三个类提供的方法几乎相同,所以我们这里以AtomicIntegerArray为例子来介绍。

    AtomicIntegerArray类常用方法

    public final int get(int i) //获取 index=i 位置元素的值 
    public final int getAndSet(int i, int newValue)//返回index=i位置的当前的值,并将其设置为新值:newValue 
    public final int getAndIncrement(int i)//获取index=i位置元素的值,并让该位置的元素自增
    public final int getAndDecrement(int i) //获取index=i位置元素的值,并让该位置的元素自减
    public final int getAndAdd(int i, int delta) //获取 index=i 位置元素的值,并加上预期的值
    boolean compareAndSet(int i, int expect, int update) //如果输入的数值等于预期值,则以原子方式将index=i位置的元素值设置为输入值(update) 
    public final void lazySet(int i, int newValue)//最终将index=i位置的元素设置为 newValue,使用lazySet设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
    

    2. AtomicIntegerArray常见方法使用

    import java.util.concurrent.atomic.AtomicIntegerArray;
    
    public class AtomicIntegerArrayTest {
    	public static void main(String[] args) { 
    		// TODO Auto-generated method stub 
    		int temvalue = 0; 
    		int[] nums = { 1, 2, 3, 4, 5, 6 }; 
    		AtomicIntegerArray i = new AtomicIntegerArray(nums); 
    		for (int j = 0; j < nums.length; j++) { 
    			System.out.println(i.get(j)); 
    		}
    		temvalue = i.getAndSet(0, 2); 
    		System.out.println("temvalue:" + temvalue + "; i:" + i); 
    		temvalue = i.getAndIncrement(0); 
    		System.out.println("temvalue:" + temvalue + "; i:" + i); 
    		temvalue = i.getAndAdd(0, 5); 
    		System.out.println("temvalue:" + temvalue + "; i:" + i); 
    	} 
    }
    

    四、引用类型原子类

    1.引用类型原子类介绍

    基本类型原子类只能更新一个变量,如果需要原子更新多个变量,需要使用 引用类型原子类。

    • AtomicReference引用类型原子类
    • AtomicStampedReference原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用CAS进行原子更新时可能出现的ABA问题。
    • AtomicMarkableReference原子更新带有标记的引用类型。该类将boolean标记与引用关联起来,也可以解决使用CAS进行原子更新时可能出现的 ABA 问题。

    上面三个类提供的方法几乎相同,所以我们这里以AtomicReference为例子来介绍。

    2. AtomicReference 类使用示例

    import java.util.concurrent.atomic.AtomicReference;
    
    public class AtomicReferenceTest {
    
    		public static void main(String[] args) { 
    			AtomicReference<Person> ar = new AtomicReference<Person>();
    			Person person = new Person("SnailClimb", 22); 
    			ar.set(person); 
    			Person updatePerson = new Person("Daisy", 20); 
    			ar.compareAndSet(person, updatePerson); 
    			
    			System.out.println(ar.get().getName()); 
    			System.out.println(ar.get().getAge()); 
    	}
    }
    
    class Person { 
    	private String name; 
    	private int age; 
    	
    	public Person(String name, int age) { 
    		super(); 
    		this.name = name; 
    		this.age = age; 
    }
    
    public String getName() { 
    	return name; 
    }
    
    public void setName(String name) { 
    	this.name = name; 
    }
    
    public int getAge() { 
    	return age; 
    }
    
    public void setAge(int age) { 
    	this.age = age; 
    	}
    }
    

    上述代码首先创建了一个Person对象,然后把Person对象设置进AtomicReference对象中,然后调用compareAndSet方法,该方法就是通过CAS操作设置 ar。如果ar的值为person的话,则将其设置为updatePerson。实现原理与AtomicInteger类中的compareAndSet方法相同。运行上面的代码后的输出结果如下:

    Daisy 
    20
    

    3. AtomicStampedReference 类使用示例

    import java.util.concurrent.atomic.AtomicStampedReference;
    
    public class AtomicStampedReferenceDemo { 
    		public static void main(String[] args) { 
    			// 实例化、取当前值和 stamp 值 
    			final Integer initialRef = 0, initialStamp = 0; 
    			final AtomicStampedReference<Integer> asr = new AtomicStampedReference<> 
    (initialRef, initialStamp); 
    			System.out.println("currentValue=" + asr.getReference() + ", 
    currentStamp=" + asr.getStamp());
    			// compare and set 
    			final Integer newReference = 666, newStamp = 999; 
    			final boolean casResult = asr.compareAndSet(initialRef, newReference, 
    initialStamp, newStamp); 
    			System.out.println("currentValue=" + asr.getReference() 
    					+ ", currentStamp=" + asr.getStamp() 
    					+ ", casResult=" + casResult); 
    					
    			// 获取当前的值和当前的stamp值 
    			int[] arr = new int[1]; 
    			final Integer currentValue = asr.get(arr); 
    			final int currentStamp = arr[0]; 
    			System.out.println("currentValue=" + currentValue + ", currentStamp=" + currentStamp);
    			 
    			// 单独设置 stamp 值 
    			final boolean attemptStampResult = asr.attemptStamp(newReference, 88); 
    			System.out.println("currentValue=" + asr.getReference() 
    					+ ", currentStamp=" + asr.getStamp() 
    					+ ", attemptStampResult=" + attemptStampResult); 
    					
    			// 重新设置当前值和 stamp 值 
    			asr.set(initialRef, initialStamp); 
    			System.out.println("currentValue=" + asr.getReference() + ", 
    currentStamp=" + asr.getStamp()); 
    
    			// [不推荐使用,除非搞清楚注释的意思了] weak compare and set 
    			// 困惑!weakCompareAndSet 这个方法最终还是调用 compareAndSet 方法。[版本: jdk-8u191]
    			// 但是注释上写着 "May fail spuriously and does not provide ordering guarantees, 
    			// so is only rarely an appropriate alternative to compareAndSet."
    			// todo 感觉有可能是 jvm 通过方法名在 native 方法里面做了转发
    			final boolean wCasResult = asr.weakCompareAndSet(initialRef, 
    newReference, initialStamp, newStamp); 
    			System.out.println("currentValue=" + asr.getReference() 
    					+ ", currentStamp=" + asr.getStamp() 
    					+ ", wCasResult=" + wCasResult); 
    		}
    }
    

    输出结果如下:

    currentValue=0, currentStamp=0 
    currentValue=666, currentStamp=999, casResult=true 
    currentValue=666, currentStamp=999 
    currentValue=666, currentStamp=88, attemptStampResult=true 
    currentValue=0, currentStamp=0 
    currentValue=666, currentStamp=999, wCasResult=true
    

    4. AtomicMarkableReference类使用示例

    import java.util.concurrent.atomic.AtomicMarkableReference;
    
    public class AtomicMarkableReferenceDemo { 
    	public static void main(String[] args) { 
    		// 实例化、取当前值和 mark 值 
    		final Boolean initialRef = null, initialMark = false; 
    		final AtomicMarkableReference<Boolean> amr = new 
    AtomicMarkableReference<>(initialRef, initialMark); 
    		System.out.println("currentValue=" + amr.getReference() + ", 
    currentMark=" + amr.isMarked());
    
    		// compare and set 
    		final Boolean newReference1 = true, newMark1 = true; 
    		final boolean casResult = amr.compareAndSet(initialRef, newReference1, initialMark, newMark1); 
    		System.out.println("currentValue=" + amr.getReference() 
    				+ ", currentMark=" + amr.isMarked() 
    				+ ", casResult=" + casResult);
    	
    		// 获取当前的值和当前的 mark 值 
    		boolean[] arr = new boolean[1]; 
    		final Boolean currentValue = amr.get(arr); 
    		final boolean currentMark = arr[0]; 
    		System.out.println("currentValue=" + currentValue + ", currentMark=" + 
    currentMark);
    		// 单独设置 mark 值
    		final boolean attemptMarkResult = amr.attemptMark(newReference1, false); 
    		System.out.println("currentValue=" + amr.getReference() 
    				+ ", currentMark=" + amr.isMarked() 
    				+ ", attemptMarkResult=" + attemptMarkResult);
    	
    		// 重新设置当前值和 mark 值 
    		amr.set(initialRef, initialMark); 
    		System.out.println("currentValue=" + amr.getReference() + ",currentMark=" + amr.isMarked());
    
    		// [不推荐使用,除非搞清楚注释的意思了] weak compare and set 
    		// 困惑!weakCompareAndSet 这个方法最终还是调用 compareAndSet 方法。[版本: jdk-8u191]
    		// 但是注释上写着 "May fail spuriously and does not provide ordering guarantees, 
    		// so is only rarely an appropriate alternative to compareAndSet." 
    		// todo 感觉有可能是 jvm 通过方法名在 native 方法里面做了转发
    		final boolean wCasResult = amr.weakCompareAndSet(initialRef, 
    newReference1, initialMark, newMark1); 
    		System.out.println("currentValue=" + amr.getReference() 
    				+ ", currentMark=" + amr.isMarked() 
    				+ ", wCasResult=" + wCasResult);
    		} 
    }
    

    输出结果如下:

    currentValue=null, currentMark=false 
    currentValue=true, currentMark=true, casResult=true 
    currentValue=true, currentMark=true 
    currentValue=true, currentMark=false, attemptMarkResult=true 
    currentValue=null, currentMark=false 
    currentValue=true, currentMark=true, wCasResult=true
    

    五、对象的属性修改类型原子类

    1.对象的属性修改类型原子类介绍

    如果需要原子更新某个类里的某个字段时,需要用到对象的属性修改类型原子类。

    • AtomicIntegerFieldUpdater:原子更新整形字段的更新器
    • AtomicLongFieldUpdater原子更新长整形字段的更新器
    • AtomicReferenceFieldUpdater原子更新引用类型里的字段的更新器

    要想原子地更新对象的属性需要两步。第一步,因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。第二步,更新的对象属性必须使用public volatile修饰符。

    上面三个类提供的方法几乎相同,所以我们这里以AtomicIntegerFieldUpdater为例子来介绍。

    2. AtomicIntegerFieldUpdater类使用示例

    import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
    
    public class AtomicIntegerFieldUpdaterTest { 
    	public static void main(String[] args) { 
    		AtomicIntegerFieldUpdater<User> a = 
    AtomicIntegerFieldUpdater.newUpdater(User.class, "age"); 
    
    		User user = new User("Java", 22); 
    		System.out.println(a.getAndIncrement(user));// 22 
    		System.out.println(a.get(user));// 23 
    	}
    }
    
    class User {
    	private String name; 
    	public volatile int age; 
    	
    	public User(String name, int age) { 
    		super(); 
    		this.name = name; 
    		this.age = age; 
    }
    	public String getName() { 
    		return name; 
    }
    	public void setName(String name) { 
    		this.name = name; 
    }
    
    	public int getAge() { 
    		return age; 
    }
    	public void setAge(int age) { 
    		this.age = age; 
    	}
    }
    

    输出结果:

    22
    23
    

    参考资料:《Java中高级核心知识全面解析》
    文章持续更新!喜欢的同学记得一键三连关注博主!

    展开全文
  • 原子类的分类

    2021-03-19 15:13:47
    效率更高:除了高度竞争的情况,使用原子类的效率比锁更高。 原子类总览 基本类型:AtomicInteger,AtomicLong,AtomicBoolean 数组类型:AtomicIntegerArray,AtomicLongArray,AtomicBooleanArray 引用类型原子类...

    作用:保证并发安全,相比于锁,具有一定的优势:

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

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

    原子类总览

    基本类型:AtomicInteger,AtomicLong,AtomicBoolean

    数组类型:AtomicIntegerArray,AtomicLongArray,AtomicBooleanArray

    引用类型原子类:AtomicReference.AtomicStampedReference,AtomicMarkableReference

    升级型:AtomicIntegerfFeldupdater,AtomicLongFieldUpdater,AtomicReferenceFieldUpdater

    可以对普通变量进行升级

    使用场景:偶尔需要get和set操作

    注意点:

    • 属性可见
    • 不支持static变量

     

    Adder累加器:LongAdder ,DoubleAdder

    LongAdder效率比atomicLong要高。AtomicLong 每做一次加法运算都需要同步,,LongAdder进行分段累加,但是LongAdder只适合求和计数的场景

    Accumulator 累加器 LongAccumulator,DoubleAccumulator

    功能升级,不限于加操作

    线程的执行顺序不能影响执行结果

    AtomicInteger常用方法

    getAndIncrement

    getAndDecrement

    getAndAdd

    CompareAndSet

    CAS原理

    compare and swap

    应用场景

    乐观锁

    并发容器

    原子类

    缺点

    ABA问题 添加版本号

    自旋时间长

    展开全文
  • 并发安全工具类(6):原子类

    千次阅读 2020-01-26 23:02:31
    原子类
  • “Java 并发之Atomic 原子类介绍”1 Atomic 原子类介绍Atomic 指原子性的。在Java中 Atomic 是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。所以,所谓原子类...
  • 1、什么是原子类?什么情况下使用原子类?java 1.5引进原子类,具体在java.util.concurrent.atomic包下,atomic包里面一共提供了13个类,分为4种类型,分别是:原子更新基本类型,原子更新数组,原子更新引用,原子...
  • Atomic原子类

    2019-08-02 14:32:35
    Atomic原子类简介JUC包下的原子类分类基本类型数组类型引用类型对象的属性修改类型原子类总结基本类型原子类数组类型原子类引用类型原子类对象的属性修改类型原子类 简介 Atomic 翻译成中文是原子的意思。在化学上,...
  • 原子类类型JUC原子类基本类型数组类型引用类型对象的属性修改类型 JUC原子类 原子操作: 操作过程不会中断,保证数据操作是以原子方式进行的 JUC原子类是对数据进行原子操作 根据修改的数据类型,可以将JUC原子类分为4...
  • JAVA原子类

    2020-12-31 12:45:19
    原子类 在java.util.concurrent.atomic包下定义了一些对“变量”操作的“原子类”: 1).java.util.concurrent.atomic.AtomicInteger:对int变量操作的“原子类”; 2).java.util.concurrent.atomic.AtomicLong:对...
  • 原子类操作

    2020-04-15 15:12:31
    常用原子类 Java中的原子操作类大致可以分为4类:原子更新基本类型、原子更新数组类型、原子更新引用类型、原子更新属性类型。这些原子类中都是用了无锁的概念,有的地方直接使用CAS操作的线程安全的类型。 在JDK7...
  • Atomic 原子类

    2020-08-21 09:36:18
    1 Atomic 原子类介绍 Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的。在我们这里 Atomic 是指一个操作是不可中断的。即使是在多个线程一起执行的时候...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,419
精华内容 4,967
关键字:

原子类