JAVA多线程之——并发包JUC——Atomic
2017-03-26 23:02
351 查看
前面学习了基础的多线程知识。今天开始学习JAVA的并发包java.util.concurrent。java并发包包括 java.util.concurrent、java.util.concurrent.atomic、java.util.concurrent.locks包。今天开始学习atomic包下的内容’
概念
java从jdk1.5开始引入了并发包。其中java.util.concurrent.atomic包,方便在无锁的情况下,进行原子操作。原子变量的底层使用的是CPU的原子指令,但是不同的CPU有不同的架构,指令也就不同。所以也有可能需要提供某种内部的锁机制。所以不能保证线程绝对不会堵塞。
Atomic分类
atomic包下面总共有12个类。根据起作用可以分为四类
1.原子更新基本类型
AtomicBoolean 原子更新布尔类型
AtomicInteger 原子更新整型
AtomincLong 原子更新长整型
原子更新整型的常用方法:
int get() 获取当前值
void set(int newValue) 设置为给定值。
int getAndAdd(int delta) 以原子方式将给定值与当前值相加。
int decrementAndGet() 以原子方式将当前值减 1。
int incrementAndGet() 以原子方式将当前值加 1。
void lazySet(int newValue) 最后设置为给定值。
方法可以通过API查找。我们主要是了解,为什么原子类型的操作就是线程安全的?底层又是如何进行原子操作的呢?
对JVM有过一定了解的应该都知道CAS操作。CAS操作有3个操作数,内存值M,预期值E,新值U,如果M==E,则将内存值修改为B,否则啥都不做。就是当且仅当内存值与当前值一致,才进行值的更新。否则不更新。这样就保证了atomic包下面的操作的原子行。那它是如何实现的呢?看一看源码:
这是AtomicInteger中的一段源码。我们可以看到其中有一个Unsafe类,一个volatile 修饰的value. 这个value就是先保证了AtomicInteger操作的可见性。然后Unsafe保证对这个value值的操作都是CAS操作。这样就保证了其原子性。Unsafe源码的一部分:
Unsafe中就是通过这样的方式来实现CAS操作。同理AtomicBoolean 和AtomicLong也是这样的方式。但是java中对long类型和double类型的操作是不具有原子性的。这个在今后学习JVM中会详细学习。还有CAS概念。
2.原子更新数组类型
理解了原子更新基本类型,对于其它类型就比较好理解了。
通过原子的方式更新数组里的某个元素,Atomic包提供了以下三个类:
AtomicIntegerArray 原子更新整型数组
AtomicLongArray 原子更新长整型数组
AtomicReferenceArray 原子更新引用数组
AtomicIntegerArray常用的方法:
int get(int i) 获取位置 i 的当前值。
void set(int i, int newValue) 将位置 i 的元素设置为给定值。
int length() 返回该数组的长度。
int getAndAdd(int i, int delta) 以原子方式将给定值与索引 i 的元素相加。
int getAndDecrement(int i) 以原子方式将索引 i 的元素减 1。
int getAndIncrement(int i) 以原子方式将索引 i 的元素加 1。
AtomicIntegerArray(int length) 创建给定长度的新 AtomicIntegerArray。
AtomicIntegerArray(int[] array) 创建与给定数组具有相同长度的新 AtomicIntegerArray,并从给定数组复制其所有素。
先来看一下AtomicIntegerArray的部分源码:
通过观察源码,其实AtomicIntegerArray类,跟AtomicInteger的原理相似。其自身拥有一个 int[]的数组array。如果我们调用构造器构造的时候没有传入数组,则直接初始化自身数组,否则,对我们的数组进行克隆。也就是说,它操作的都是自身的数组,对我们传入的数组是不会有任何操作。而对数组中的数据的操作也是通过Unsafe类来实现其原子性的操作。
3.原子更新引用类型
原子更新基本类型,每次只能更新一个变量,而当需要更新多个变量,比如一个对象的多个属性时候,我们就必须用到原子更新引用类型类——AtomicReference. 其常用的方法有:
V get() 获取当前值。
void set(V newValue) 设置为给定值。
V getAndSet(V newValue) 以原子方式设置为给定值,并返回旧值。
boolean compareAndSet(V expect, V update) 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
void lazySet(V newValue) 最终设置为给定值。
先看一个示例:
运行结果:
看来是没什么问题。再看一个示例:
运行结果:
这个结果通过前面学习我们可以知道原因。因为没有同步操作。OK,那可能就会想,我们今天学习了AtomicReferece。刚好是更新一个对象。那我们可以用上。示例:
结果:
我们发现,结果并没有对真个对象进行我们预期的“原子操作”。为什么? 看源码:
我们看到 有一个变量 V value。这意味着,我们所有的原子操作都是在操作这整个对象。如果对这个对象单独的属性进行操作,那必然会导致这个对象的原子性不一致。同理,如果在AtomicInteger类,加入我们连续的多次调用自增1的方法,那也是不能保证原子性的。
4.原子更新字段
如果我们只需要某个类里的某个字段,那么就需要使用原子更新字段类,Atomic包提供了以下三个类:
AtomicIntegerFieldUpdater 原子更新整型的字段的更新器
AtomicLongFieldUpdater 原子更新长整型的字段更新器
AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更数据和数据的版本号,可以解决使用CAS进行原子更新时,可能出现的ABA问题。
原子更新字段类都是抽象类,每次使用都时候必须使用静态方法newUpdater创建一个更新器。原子更新类的字段的必须使用public volatile修饰符。AtomicIntegerFieldUpdater的示例:
运行结果:
这里需要注意的是
1.字段必须用volatile修饰 2.字段不能修饰为private。源码:
其中调用了 sun.reflect.misc.ReflectUtil.ensureMemberAccess(
caller, tclass, null, modifiers);这个方法。一直跟踪这个方法:
在这个方法中如果是私有的就会返回false。于是就会抛出异常。
至于为什么不能声明私有,为何要抛出这个异常,目前对于sun.reflect.misc.ReflectUtil还没有深入研究。所以暂时做个笔记。
概念
java从jdk1.5开始引入了并发包。其中java.util.concurrent.atomic包,方便在无锁的情况下,进行原子操作。原子变量的底层使用的是CPU的原子指令,但是不同的CPU有不同的架构,指令也就不同。所以也有可能需要提供某种内部的锁机制。所以不能保证线程绝对不会堵塞。
Atomic分类
atomic包下面总共有12个类。根据起作用可以分为四类
1.原子更新基本类型
AtomicBoolean 原子更新布尔类型
AtomicInteger 原子更新整型
AtomincLong 原子更新长整型
原子更新整型的常用方法:
int get() 获取当前值
void set(int newValue) 设置为给定值。
int getAndAdd(int delta) 以原子方式将给定值与当前值相加。
int decrementAndGet() 以原子方式将当前值减 1。
int incrementAndGet() 以原子方式将当前值加 1。
void lazySet(int newValue) 最后设置为给定值。
方法可以通过API查找。我们主要是了解,为什么原子类型的操作就是线程安全的?底层又是如何进行原子操作的呢?
对JVM有过一定了解的应该都知道CAS操作。CAS操作有3个操作数,内存值M,预期值E,新值U,如果M==E,则将内存值修改为B,否则啥都不做。就是当且仅当内存值与当前值一致,才进行值的更新。否则不更新。这样就保证了atomic包下面的操作的原子行。那它是如何实现的呢?看一看源码:
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; /** * Creates a new AtomicInteger with the given initial value. * * @param initialValue the initial value */ public AtomicInteger(int initialValue) { value = initialValue; } }
这是AtomicInteger中的一段源码。我们可以看到其中有一个Unsafe类,一个volatile 修饰的value. 这个value就是先保证了AtomicInteger操作的可见性。然后Unsafe保证对这个value值的操作都是CAS操作。这样就保证了其原子性。Unsafe源码的一部分:
/** * 比较obj的offset处内存位置中的值和期望的值,如果相同则更新。此更新是不可中断的。 * * @param obj 需要更新的对象 * @param offset obj中整型field的偏移量 * @param expect 希望field中存在的值 * @param update 如果期望值expect与field的当前值相同,设置filed的值为这个新值 * @return 如果field的值被更改返回true */ public native boolean compareAndSwapInt(Object obj, long offset, int expect, int update);
Unsafe中就是通过这样的方式来实现CAS操作。同理AtomicBoolean 和AtomicLong也是这样的方式。但是java中对long类型和double类型的操作是不具有原子性的。这个在今后学习JVM中会详细学习。还有CAS概念。
2.原子更新数组类型
理解了原子更新基本类型,对于其它类型就比较好理解了。
通过原子的方式更新数组里的某个元素,Atomic包提供了以下三个类:
AtomicIntegerArray 原子更新整型数组
AtomicLongArray 原子更新长整型数组
AtomicReferenceArray 原子更新引用数组
AtomicIntegerArray常用的方法:
int get(int i) 获取位置 i 的当前值。
void set(int i, int newValue) 将位置 i 的元素设置为给定值。
int length() 返回该数组的长度。
int getAndAdd(int i, int delta) 以原子方式将给定值与索引 i 的元素相加。
int getAndDecrement(int i) 以原子方式将索引 i 的元素减 1。
int getAndIncrement(int i) 以原子方式将索引 i 的元素加 1。
AtomicIntegerArray(int length) 创建给定长度的新 AtomicIntegerArray。
AtomicIntegerArray(int[] array) 创建与给定数组具有相同长度的新 AtomicIntegerArray,并从给定数组复制其所有素。
先来看一下AtomicIntegerArray的部分源码:
private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final int base = unsafe.arrayBaseOffset(int[].class); private static final int shift; private final int[] array; public AtomicIntegerArray(int length) { array = new int[length]; } public AtomicIntegerArray(int[] array) { // Visibility guaranteed by final field guarantees this.ar 4000 ray = array.clone(); } public final int getAndAdd(int i, int delta) { long offset = checkedByteOffset(i); while (true) { int current = getRaw(offset); if (compareAndSetRaw(offset, current, current + delta)) return current; } }
通过观察源码,其实AtomicIntegerArray类,跟AtomicInteger的原理相似。其自身拥有一个 int[]的数组array。如果我们调用构造器构造的时候没有传入数组,则直接初始化自身数组,否则,对我们的数组进行克隆。也就是说,它操作的都是自身的数组,对我们传入的数组是不会有任何操作。而对数组中的数据的操作也是通过Unsafe类来实现其原子性的操作。
3.原子更新引用类型
原子更新基本类型,每次只能更新一个变量,而当需要更新多个变量,比如一个对象的多个属性时候,我们就必须用到原子更新引用类型类——AtomicReference. 其常用的方法有:
V get() 获取当前值。
void set(V newValue) 设置为给定值。
V getAndSet(V newValue) 以原子方式设置为给定值,并返回旧值。
boolean compareAndSet(V expect, V update) 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
void lazySet(V newValue) 最终设置为给定值。
先看一个示例:
public class AtomicTest3 { public static void main(String[] args) { final AtomicReference<Student> reference = new AtomicReference<Student>(); final Student s1 = new Student("张三", 18); Student s2 = new Student("李四", 20); reference.set(s1); reference.compareAndSet(s1, s2); System.out.println(reference.get()); } static class Student{ private String name; private int age; public Student(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; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } } }
运行结果:
Student [name=李四, age=20]
看来是没什么问题。再看一个示例:
public class AtomicTest { public static void main(String[] args) { AtomicReference<Student> reference = new AtomicReference<Student>(); final Student s1 = new Student("张三", 18); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { s1.setName("李四"); Thread.currentThread().sleep(2000); s1.setAge(20); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { System.out.println(s1.toString()); } }); t1.start(); t2.start(); } static class Student{ private String name; private int age; public Student(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; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } } }
运行结果:
Student [name=李四, age=18]
这个结果通过前面学习我们可以知道原因。因为没有同步操作。OK,那可能就会想,我们今天学习了AtomicReferece。刚好是更新一个对象。那我们可以用上。示例:
public class AtomicTest2 { public static void main(String[] args) { final AtomicReference<Student> reference = new AtomicReference<Student>(); final Student s1 = new Student("张三", 18); reference.set(s1); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { reference.get().setName("李四"); Thread.currentThread().sleep(2000); reference.get().setAge(20); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { System.out.println(reference.get().toString()); } }); t1.start(); t2.start(); } static class Student{ private String name; private int age; public Student(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; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } } }
结果:
Student [name=李四, age=18]
我们发现,结果并没有对真个对象进行我们预期的“原子操作”。为什么? 看源码:
private static final long serialVersionUID = -1848883965231344442L; private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicReference.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile V value;
我们看到 有一个变量 V value。这意味着,我们所有的原子操作都是在操作这整个对象。如果对这个对象单独的属性进行操作,那必然会导致这个对象的原子性不一致。同理,如果在AtomicInteger类,加入我们连续的多次调用自增1的方法,那也是不能保证原子性的。
4.原子更新字段
如果我们只需要某个类里的某个字段,那么就需要使用原子更新字段类,Atomic包提供了以下三个类:
AtomicIntegerFieldUpdater 原子更新整型的字段的更新器
AtomicLongFieldUpdater 原子更新长整型的字段更新器
AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更数据和数据的版本号,可以解决使用CAS进行原子更新时,可能出现的ABA问题。
原子更新字段类都是抽象类,每次使用都时候必须使用静态方法newUpdater创建一个更新器。原子更新类的字段的必须使用public volatile修饰符。AtomicIntegerFieldUpdater的示例:
public static void main(String[] args) { Student s1 = new Student("张三", 18); AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(Student.class, Integer.class, "age"); System.out.println(updater.get(s1)); updater.set(s1, 20); System.out.println(updater.get(s1)); System.out.println(s1); } static class Stud f867 ent{ private String name; volatile Integer age; public Student(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; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } } }
运行结果:
18 20 Student [name=张三, age=20]
这里需要注意的是
1.字段必须用volatile修饰 2.字段不能修饰为private。源码:
AtomicReferenceFieldUpdaterImpl(Class<T> tclass, Class<V> vclass, String fieldName, Class<?> caller) { Field field = null; Class fieldClass = null; int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); modifiers = field.getModifiers(); sun.reflect.misc.ReflectUtil.ensureMemberAccess( caller, tclass, null, modifiers); sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass); fieldClass = field.getType(); } catch (Exception ex) { throw new RuntimeException(ex); } if (vclass != fieldClass) throw new ClassCastException(); if (vclass.isPrimitive()) throw new IllegalArgumentException("Must be reference type"); if (!Modifier.isVolatile(modifiers)) throw new IllegalArgumentException("Must be volatile type"); this.cclass = (Modifier.isProtected(modifiers) && caller != tclass) ? caller : null; this.tclass = tclass; if (vclass == Object.class) this.vclass = null; else this.vclass = vclass; offset = unsafe.objectFieldOffset(field); } void targetCheck(T obj) { if (!tclass.isInstance(obj)) throw new ClassCastException(); if (cclass != null) ensureProtectedAccess(obj); } void updateCheck(T obj, V update) { if (!tclass.isInstance(obj) || (update != null && vclass != null && !vclass.isInstance(update))) throw new ClassCastException(); if (cclass != null) ensureProtectedAccess(obj); } public boolean compareAndSet(T obj, V expect, V update) { if (obj == null || obj.getClass() != tclass || cclass != null || (update != null && vclass != null && vclass != update.getClass())) updateCheck(obj, update); return unsafe.compareAndSwapObject(obj, offset, expect, update); } public boolean weakCompareAndSet(T obj, V expect, V update) { // same implementation as strong form for now if (obj == null || obj.getClass() != tclass || cclass != null || (update != null && vclass != null && vclass != update.getClass())) updateCheck(obj, update); return unsafe.compareAndSwapObject(obj, offset, expect, update); }
其中调用了 sun.reflect.misc.ReflectUtil.ensureMemberAccess(
caller, tclass, null, modifiers);这个方法。一直跟踪这个方法:
public static boolean More ...verifyMemberAccess(Class currentClass, 105 // Declaring class of field 106 // or method 107 Class memberClass, 108 // May be NULL in case of statics 109 Object target, 110 int modifiers) 111 { 112 // Verify that currentClass can access a field, method, or 113 // constructor of memberClass, where that member's access bits are 114 // "modifiers". 115 116 boolean gotIsSameClassPackage = false; 117 boolean isSameClassPackage = false; 118 119 if (currentClass == memberClass) { 120 // Always succeeds 121 return true; 122 } 123 124 if (!Modifier.isPublic(getClassAccessFlags(memberClass))) { 125 isSameClassPackage = isSameClassPackage(currentClass, memberClass); 126 gotIsSameClassPackage = true; 127 if (!isSameClassPackage) { 128 return false; 129 } 130 } 131 132 // At this point we know that currentClass can access memberClass. 133 134 if (Modifier.isPublic(modifiers)) { 135 return true; 136 } 137 138 boolean successSoFar = false; 139 140 if (Modifier.isProtected(modifiers)) { 141 // See if currentClass is a subclass of memberClass 142 if (isSubclassOf(currentClass, memberClass)) { 143 successSoFar = true; 144 } 145 } 146 147 if (!successSoFar && !Modifier.isPrivate(modifiers)) { 148 if (!gotIsSameClassPackage) { 149 isSameClassPackage = isSameClassPackage(currentClass, 150 memberClass); 151 gotIsSameClassPackage = true; 152 } 153 154 if (isSameClassPackage) { 155 successSoFar = true; 156 } 157 } 158 159 if (!successSoFar) { 160 return false; 161 } 162 163 if (Modifier.isProtected(modifiers)) { 164 // Additional test for protected members: JLS 6.6.2 165 Class targetClass = (target == null ? memberClass : target.getClass()); 166 if (targetClass != currentClass) { 167 if (!gotIsSameClassPackage) { 168 isSameClassPackage = isSameClassPackage(currentClass, memberClass); 169 gotIsSameClassPackage = true; 170 } 171 if (!isSameClassPackage) { 172 if (!isSubclassOf(targetClass, currentClass)) { 173 return false; 174 } 175 } 176 } 177 } 178 179 return true; 180 }
在这个方法中如果是私有的就会返回false。于是就会抛出异常。
if (!verifyMemberAccess(currentClass, memberClass, target, modifiers)) { 95 throw new IllegalAccessException("Class " + currentClass.getName() + 96 " can not access a member of class " + 97 memberClass.getName() + 98 " with modifiers \"" + 99 Modifier.toString(modifiers) + 100 "\""); 101 }
至于为什么不能声明私有,为何要抛出这个异常,目前对于sun.reflect.misc.ReflectUtil还没有深入研究。所以暂时做个笔记。
相关文章推荐
- JAVA多线程之——并发包JUC——Atomic
- JAVA多线程之——并发包JUC——Atomic
- JAVA多线程之——并发包JUC——Atomic
- JAVA多线程之——并发包JUC——Atomic
- JAVA多线程之——并发包JUC——Atomic
- JAVA多线程之——并发包JUC——Atomic
- JAVA多线程之——并发包JUC——Atomic
- JAVA多线程之——并发包JUC——Atomic
- JAVA多线程之——并发包JUC——Atomic
- JAVA多线程之——并发包JUC——Atomic
- JAVA多线程之——并发包JUC——Atomic
- Java多线程之原子操作atomic的使用CAS(七)
- Java多线程系列--【JUC集合03】- CopyOnWriteArraySet
- java.util.concurrent(JUC)的研究--》atomic原子操作--》happens-before法则
- JUC原子类-04之 AtomicReference原子类
- Java多线程系列--“JUC锁”03之 公平锁(一)
- java.util.concurrent(JUC)的研究--》atomic原子操作--》CAS操作
- Java JUC之Atomic系列12大类实例讲解和原理分解
- Java多线程系列--“JUC集合”05之 ConcurrentSkipListMap
- JUC学习笔记--Atomic原子类