多线程:原子类
1.什么是原子类
原子类:一度认为原子是不可分割的最小单位,故原子类可以认为其操作都是不可分割。
对多线程访问同一个变量,我们需要加锁,而锁是比较消耗性能的,JDk1.5之后, 新增的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式, 这些类同样位于JUC包下的atomic包下,发展到JDk1.8,该包下共有17个类, 囊括了原子更新基本类型、原子更新数组、原子更新属性、原子更新引用。
1.8新增的原子类:DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder、Striped64
2.原子更新基本类型
大致可以归为3类:
AtomicBoolean、AtomicInteger、AtomicLong 元老级的原子更新,方法几乎一模一样。
DoubleAdder、LongAdder 对Double、Long的原子更新性能进行优化提升 。
DoubleAccumulator、LongAccumulator 支持自定义运算。
AtomicInteger使用
package com.ljessie.sinopsis.atomic;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 原子的Integer型
* @author zbw
*
*/
public class AtomicIntegerDemo {
private static AtomicInteger num = new AtomicInteger(0);
public static void increate() {
num.incrementAndGet();
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread() {
public void run() {
for (int j = 0; j < 10; j++) {
try {
increate();
System.out.println(num);
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
}
}
}
LongAccumulator 使用
package com.ljessie.sinopsis.atomic;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.function.LongBinaryOperator;
/**
* JDK1.8之后的原子Long型
* @author zbw
*
*/
public class LongAccumlatorDemo {
public static void main(String[] args) {
//输入一个数字,如果比上一个输入的大,则直接返回,如果输入的小,则返回上一个.
LongAccumulator la = new LongAccumulator(new LongBinaryOperator() {
@Override
public long applyAsLong(long left, long right) {
return left>right?left:right;
}
}, 5);
la.accumulate(3);
System.out.println(la.get());
la.accumulate(7);
System.out.println(la.get());
}
}
3.原子更新数组类型
AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray,使用方法类似
AtomicIntegerArray使用
package com.ljessie.sinopsis.atomic;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.function.IntBinaryOperator;
/**
* 原子的更新数组
* @author zbw
*
*/
public class AtomicIntegerArrayDemo {
public static void main(String[] args) {
int[] arr = {1,3,5,7,9};
AtomicIntegerArray array = new AtomicIntegerArray(arr);
int i = array.addAndGet(0, 3);
System.out.println(i);
int j = array.accumulateAndGet(4, 10, new IntBinaryOperator() {
@Override
public int applyAsInt(int left, int right) {
//将10和数组下标为4的值进行对比,a[4]是left,10是right
return left>right?left:right;
}
});
System.out.println(j);
}
}
4.原子更新属性
原子地更新某个类里的某个字段时,就需要使用原子更新字段类,Atomic包提供了以下4个类进行原子字段更新 AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference、AtomicReferenceFieldUpdater
package com.ljessie.sinopsis.atomic;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
/**
* 原子属性
* @author zbw
*
*/
public class AtomicLongFieldUpdaterDemo {
public static void main(String[] args) {
AtomicLongFieldUpdater<Student> updater = AtomicLongFieldUpdater.newUpdater(Student.class, "id");
Student student = new Student(1, "zbw");
updater.compareAndSet(student, 1, 1000);
System.out.println(student.getId());
AtomicReferenceFieldUpdater<Student , String> referenceUpdater =
AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
boolean isSuccess = referenceUpdater.compareAndSet(student, "zbw", "jessie");
if(isSuccess) {
System.out.println(student.getName());
}
}
}
class Student{
volatile long id;
volatile String name;
public Student(long id,String name){
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
使用上述类的时候,必须遵循以下原则
字段必须是volatile类型的,在线程之间共享变量时保证立即可见
字段的描述类型是与调用者与操作对象字段的关系一致。
也就是说调用者能够直接操作对象字段,那么就可以反射进行原子操作。
对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段。
只能是实例变量,不能是类变量,也就是说不能加static关键字。
只能是可修改变量,不能使final变量,因为final的语义就是不可修改。
对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。
如果要修改包装类型就需要使用AtomicReferenceFieldUpdater。
5.原子更新引用
AtomicReference的使用
package com.ljessie.sinopsis.atomic;
import java.util.concurrent.atomic.AtomicReference;
/**
* 原子的引用修改
* @author zbw
*
*/
public class AtomicReferenceDemo {
public static void main(String[] args) {
AtomicReference<Students> reference = new AtomicReference<Students>();
Students student1 = new Students(1, "zbw");
Students student2 = new Students(2, "jessie");
reference.set(student1);
reference.compareAndSet(student1, student2);
Students student3 = reference.get();
System.out.println(student3.getName());
}
}
class Students{
private long id;
private String name;
public Students(long id,String name){
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}