28. Java原子类
大约 4 分钟
1. Java原子类
Java原子类在java.util.concurrent.atomic包下,使用无锁机制实现操作原子性,从而做到线程安全。
无锁机制相比加锁机制性能更高。
2. AtomicInteger
下面通过例子比较原子类和原始类型不加锁操作的结果差异。
代码:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerDemo {
private volatile static int count = 0;
private static AtomicInteger atomicInteger = new AtomicInteger();
private static CountDownLatch countDownLatch;
private static int CYCLE_TIMES = 20000;
private static int THREAD_NUMS = 2;
private static class AtomicCounter implements Runnable {
@Override
public void run() {
for (int i = 0; i < CYCLE_TIMES; i++) {
atomicInteger.incrementAndGet(); // 这里是原子操作,线程安全
}
countDownLatch.countDown();
}
}
private static class PrimitiveTypeCounter implements Runnable {
@Override
public void run() {
for (int i = 0; i < CYCLE_TIMES; i++) {
count = count + 1; // 非原子操作,不是线程安全
}
countDownLatch.countDown();
}
}
private static class PrimitiveTypeSyncCounter implements Runnable {
@Override
public void run() {
for (int i = 0; i < CYCLE_TIMES; i++) {
synchronized (AtomicIntegerDemo.class) { // 加锁后线程安全
count = count + 1;
}
}
countDownLatch.countDown();
}
}
private static void execCount(Runnable runnable) throws InterruptedException {
count = 0;
countDownLatch = new CountDownLatch(THREAD_NUMS);
for (int i = 0; i < THREAD_NUMS; i++) {
new Thread(runnable).start();
}
countDownLatch.await();
}
public static void main(String[] args) throws InterruptedException {
execCount(new AtomicCounter());
System.out.println("atomicInteger: " + atomicInteger.get());
execCount(new PrimitiveTypeCounter());
System.out.println("primitive type: " + count);
execCount(new PrimitiveTypeSyncCounter());
System.out.println("primitive type sync: " + count);
}
}
输出:
atomicInteger: 40000
primitive type: 26664
primitive type sync: 40000
可以看到原子类不需要加锁也能保证结果正确, 而原始类型必须加锁才能保证结果正确。
3. AtomicReference例子
AtomicReference用于提供Java对象原子操作的能力。
代码:
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceDemo {
private static AtomicReference<Person> atomicReference = new AtomicReference();
private static CountDownLatch countDownLatch;
private static class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
name.equals(person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
private static class AtomicRunnable implements Runnable {
Person expect;
Person upate;
private AtomicRunnable(Person expect, Person upate) {
this.expect = expect;
this.upate = upate;
}
@Override
public void run() {
boolean isChanged = atomicReference.compareAndSet(expect, upate); // 多个线程并发执行也能做到线程安全
System.out.println("AtomicRunnable isChanged: " + isChanged + " " + atomicReference.get());
countDownLatch.countDown();
}
}
private static class NormalRunnable implements Runnable {
Person expect;
Person upate;
private NormalRunnable(Person expect, Person upate) {
this.expect = expect;
this.upate = upate;
}
@Override
public void run() {
if (expect.name.equals("leo") && expect.age == 20) { // 多个线程同时执行时都会满足这个条件,非线程安全
System.out.println("NormalRunnable isChanged: true " + upate);
} else {
System.out.println("NormalRunnable isChanged: false " + expect);
}
countDownLatch.countDown();
}
}
private static void exec(Runnable[] runnables) throws InterruptedException {
countDownLatch = new CountDownLatch(2);
for (int i = 0; i < 2; i++) {
new Thread(runnables[i]).start();
}
countDownLatch.await();
}
public static void main(String[] args) throws InterruptedException {
Person expect = new Person("leo", 20);
atomicReference.set(expect);
Person[] changePersons = new Person[]{
new Person("Jack", 30),
new Person("Mark", 40)
};
System.out.println("expect: " + expect);
exec(new AtomicRunnable[] {new AtomicRunnable(expect, changePersons[0]), new AtomicRunnable(expect, changePersons[1])});
exec(new NormalRunnable[] {new NormalRunnable(expect, changePersons[0]), new NormalRunnable(expect, changePersons[1])});
}
}
输出:
expect: Person{name='leo', age=20}
AtomicRunnable isChanged: true Person{name='Mark', age=40}
AtomicRunnable isChanged: false Person{name='Mark', age=40}
NormalRunnable isChanged: true Person{name='Mark', age=40}
NormalRunnable isChanged: true Person{name='Jack', age=30}
可以看到,当进行compareAndSet操作时AtomicReference能保证原子性,如果A线程修改了对象,则B线程立即就能看到A的修改结果,不会继续修改,而不使用AtomicReference时,两个线程都会修改对象。
4. AtomicReferenceFieldUpdater例子
AtomicReferenceFieldUpdater的作用和AtomicReference类似,从名字可以看出它和AtomicReference的区别是,它提供修改java对象字段的原子操作能力。
代码:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class AtomicReferenceFieldUpdaterDemo {
private static AtomicReferenceFieldUpdater<Person, String> atomicRefFieldUpdater =
AtomicReferenceFieldUpdater.newUpdater(Person.class, String.class, "name");
private static CountDownLatch countDownLatch;
private static class Person {
volatile String name; // 必须用volatile修饰,且不能是private变量
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
private static class AtomicRunnable implements Runnable {
Person person;
String expect;
String upate;
private AtomicRunnable(Person person, String expect, String upate) {
this.person = person;
this.expect = expect;
this.upate = upate;
}
@Override
public void run() {
boolean isChanged = atomicRefFieldUpdater.compareAndSet(person, expect, upate); // 多个线程并发执行也能做到线程安全
System.out.println("isChanged: " + isChanged + " " + person);
countDownLatch.countDown();
}
}
private static void exec(Runnable[] runnables) throws InterruptedException {
countDownLatch = new CountDownLatch(2);
for (int i = 0; i < 2; i++) {
new Thread(runnables[i]).start();
}
countDownLatch.await();
}
public static void main(String[] args) throws InterruptedException {
String expect = "loe";
Person person = new Person(expect, 20);
String[] changeNames = new String[] { "Jack", "Mark" };
System.out.println("expect: leo");
exec(new AtomicRunnable[] {new AtomicRunnable(person, expect, changeNames[0]), new AtomicRunnable(person, expect, changeNames[1])});
}
}
打印日志:
expect: leo
isChanged: true Person{name='Jack', age=20}
isChanged: false Person{name='Jack', age=20}
可以看到,如果A线程修改了对象字段,则B线程立即就能看到A的修改结果,不会继续修改。
4. 总结
1.Java原子类通过无锁机制提高性能,机制相同,只是使用场景有差异,有基于JAVA原始类型,JAVA对象,JAVA对象的某个字段等等。
2.无锁机制的核心是确保CAS操作是原子操作从而做到线程安全。