原子类
在Java Util Concurrent(JUC)库中,原子类是一组线程安全的、基于CAS(Compare and Swap)算法实现的类,
它们可以保证多线程环境下对一个共享变量的操作不会出现竞态条件(race condition)。Java中的原子类提供了一种非常高效的方式来进行同步和并发编程。
以下是JUC中常用的几个原子类:
AtomicBoolean:原子化地更新一个布尔类型的值。
AtomicInteger:原子化地更新一个整型的值。
AtomicLong:原子化地更新一个长整型的值。
AtomicReference:原子化地更新一个引用类型的值。
AtomicStampedReference:AtomicReference的增强版,可以解决ABA问题。
AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater:原子化地更新指定类的字段值。
使用这些原子类,可以避免使用synchronized关键字等传统的同步方式,从而提高应用程序的并发性能,并且保证数据的一致性。
基本类型原子类
Java中提供了以下几种基本类型的原子类,它们都是线程安全的,并且可以保证对共享变量的操作不会出现竞态条件:
AtomicBoolean:原子化地更新一个布尔类型的值。
AtomicInteger:原子化地更新一个整型的值。
AtomicLong:原子化地更新一个长整型的值。
这些基本类型的原子类都提供了一些常用的方法,如get、set、compareAndSet等。
其中,compareAndSet方法是最重要的方法,因为它使用CAS(Compare and Swap)算法来实现原子性操作。下面是一些常用的方法:
get():返回当前值。
set(int newValue):设置当前值为给定的newValue。
compareAndSet(int expect, int update):如果当前值等于expect,则将该值设为update并返回true,否则返回false。
getAndIncrement():获取当前值并自增1。
incrementAndGet():自增1并获取当前值。
getAndAdd(int delta):获取当前值并加上给定的delta值。
addAndGet(int delta):加上给定的delta值并获取当前值。
这些基本类型的原子类可以在高并发环境下安全地进行操作,避免了传统的同步方式带来的锁竞争和死锁问题。
数组类型原子类
在Java中,除了基本类型的原子类,还提供了一些数组类型的原子类用于操作数组。以下是JUC中常用的几个数组类型原子类:
AtomicIntegerArray:一个int类型的数组,支持原子化地更新某个下标的值。
AtomicLongArray:一个long类型的数组,支持原子化地更新某个下标的值。
AtomicReferenceArray<T>:一个T类型的数组,支持原子化地更新某个下标的值。
这些数组类型的原子类都实现了AtomicArray接口,并提供了一些常用的方法,比如get、set、compareAndSet等方法。
这些方法可以保证在多线程环境下对数组元素进行原子化的读取和更新,从而确保数据的一致性。
另外,这些数组类型的原子类还提供了一些批量操作的方法,如addAndGet、compareAndSet等方法,
这些方法可以同时对多个数组元素进行操作,从而进一步提高并发性能。
需要注意的是,使用数组类型的原子类时要注意保证数组的长度不变,否则可能会引起数组越界异常。
同时,由于数组类型的原子类只保证对单个元素进行原子化操作,因此在对整个数组进行遍历、排序等操作时仍然需要考虑如何保证线程安全。
引用类型原子类
除了基本类型和数组类型的原子类,Java中还提供了一些引用类型的原子类,用于原子化地操作对象和对象属性。以下是JUC中常用的几个引用类型原子类:
AtomicReference:一个泛型类,可以原子化地更新一个引用类型的变量。
AtomicStampedReference:对AtomicReference的增强版,可以解决ABA问题。
AtomicMarkableReference:标记型的AtomicReference,可以原子化地更新一个标记位和引用值。
这些引用类型的原子类都实现了AtomicReference接口,并提供了一些常用的方法,如get、set、compareAndSet等。
使用这些原子类可以保证在多线程环境下对共享对象的读取和修改操作的原子性和线程安全性。
需要注意的是,虽然引用类型原子类可以避免线程安全问题,但它们并不能完全避免死锁问题和性能瓶颈。
因此,在实际应用中,应该根据具体场景选择合适的同步方式来保证程序的正确性和性能。
对象属性修改相关原子类
在Java中,除了引用类型原子类,还提供了一些对象属性修改相关的原子类,这些原子类可以原子化地修改对象的某个属性,而不是整个对象。以下是JUC中常用的几个对象属性修改相关的原子类:
AtomicIntegerFieldUpdater:一个工具类,可以原子化地更新指定类的int类型字段。
AtomicLongFieldUpdater:一个工具类,可以原子化地更新指定类的long类型字段。
AtomicReferenceFieldUpdater<T, V>:一个泛型工具类,可以原子化地更新指定类的引用类型字段。
这些对象属性修改相关的原子类都使用反射机制来实现对对象属性的原子化操作。
使用这些原子类时需要注意,被修改的字段不能是private、final等修饰符限定的字段,并且需要保证该字段的访问权限。
此外,由于这些原子类的实现方式比较特殊,因此性能可能会受到一定的影响。
需要注意的是,虽然对象属性修改相关的原子类可以避免线程安全问题,但它们并不能完全避免死锁问题和性能瓶颈。
因此,在实际应用中,应该根据具体场景选择合适的同步方式来保证程序的正确性和性能。
以下为常用原子类代码示例:
AtomicBoolean
package com.lfsun.highconcurrency000.atomic;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 使用AtomicBoolean的简单示例代码,它用来实现两个线程之间的通信,当一个线程完成某个任务后,通知另一个线程进行下一步操作
*/
public class MyAtomicBooleanDemo {
public static void main(String[] args) {
AtomicBoolean flag = new AtomicBoolean(false);
// 第一个线程,执行某个任务
Thread t1 = new Thread(() -> {
System.out.println("Thread 1 is running.");
// 完成任务后,将flag设置为true
flag.set(true);
});
// 第二个线程,等待第一个线程完成任务
Thread t2 = new Thread(() -> {
System.out.println("Thread 2 is running.");
while (!flag.get()) {`在这里插入代码片`
// 等待flag变为true
}
System.out.println("Thread 2 is done.");
});
// 启动两个线程
t1.start();
t2.start();
}
}
AtomicIntegerArray
package com.lfsun.highconcurrency000.atomic;
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
* AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray都是用来原子化地更新数组元素的类。
*
* 以AtomicIntegerArray为例,它是一个int类型的数组,支持原子化地更新某个下标的值。它的使用方法与AtomicInteger类似,只是需要指定数组下标。
*/
public class MyAtomicIntegerArrayDemo {
public static void main(String[] args) {
AtomicIntegerArray arr = new AtomicIntegerArray(new int[]{1, 2, 3, 4, 5});
System.out.println("Before update: " + arr);
arr.set(2, 100);
System.out.println("After update: " + arr);
}
}
AtomicInteger
package com.lfsun.highconcurrency000.atomic;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 使用AtomicInteger的简单示例代码,它用来实现多个线程对一个共享计数器进行原子化操作
*/
public class MyAtomicIntegerDemo {
private static AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter.getAndIncrement();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter.getAndIncrement();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Counter value: " + counter.get());
}
}
MyAtomicIntegerFieldUpdaterDemo
package com.lfsun.highconcurrency000.atomic;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater是用来原子化地更新指定类的字段值的类。
* 它们都是通过反射来实现的,因此要更新的字段必须是可访问的。
* 这三个类的使用方法类似,这里以AtomicIntegerFieldUpdater为例:
* 首先,我们需要定义一个Updater对象,这个对象将用来更新指定类的指定字段
*/
public class MyAtomicIntegerFieldUpdaterDemo {
static class DemoClass {
public volatile int field;
}
public static void main(String[] args) {
AtomicIntegerFieldUpdater<DemoClass> updater = AtomicIntegerFieldUpdater.newUpdater(DemoClass.class, "field");
DemoClass demo = new DemoClass();
demo.field = 1;
System.out.println("Before update: " + demo.field);
updater.compareAndSet(demo, 1, 2);
System.out.println("After update: " + demo.field);
}
}
AtomicReference
package com.lfsun.highconcurrency000.atomic;
import java.util.concurrent.atomic.AtomicReference;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
/**
* 使用AtomicReference的简单示例代码,它用来实现多个线程对一个共享的Person对象进行原子化操作
*/
public class MyAtomicReferenceDemo {
private static AtomicReference<Person> person = new AtomicReference<>(new Person("Alice", 25));
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
Person p = person.get();
p.setName("Bob");
p.setAge(30);
person.set(p);
});
Thread t2 = new Thread(() -> {
Person p = person.get();
p.setName("Charlie");
p.setAge(35);
person.set(p);
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(person.get().getName() + ", " + person.get().getAge());
}
}
AtomicStampedReference
package com.lfsun.highconcurrency000.atomic;
import java.util.concurrent.atomic.AtomicStampedReference;
class Account {
private int balance;
public Account(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
}
public class MyAtomicStampedReferenceDemo {
private static AtomicStampedReference<Account> account = new AtomicStampedReference<>(new Account(1000), 0);
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
int stamp;
Account a;
do {
stamp = account.getStamp();
a = account.getReference();
a.setBalance(a.getBalance() + 100);
} while (!account.compareAndSet(a, a, stamp, stamp + 1));
});
Thread t2 = new Thread(() -> {
int stamp;
Account a;
do {
stamp = account.getStamp();
a = account.getReference();
a.setBalance(a.getBalance() - 100);
} while (!account.compareAndSet(a, a, stamp, stamp + 1));
});
Thread t3 = new Thread(() -> {
int stamp;
Account a;
do {
stamp = account.getStamp();
a = account.getReference();
a.setBalance(a.getBalance() - 200);
} while (!account.compareAndSet(a, a, stamp, stamp + 1));
});
t1.start();
t2.start();
t3.start();
try {
t1.join();
t2.join();
t3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final balance: " + account.getReference().getBalance());
}
}