主要参考《实战java高并发程序设计》、《并发编程的艺术》,感觉学习这13个原子类,还是要自己动手写一些,试一试,才能理解它们的用法和不同之处。
1.原子类的引入以及AtomicInteger
首先看一个例子:
public class AtomicNoIntegerDemo {
static int i = 0;
public static class AddThread implements Runnable{
@Override
public void run(){
for(int k=0;k<10000;k++)
i++;
}
}
public static void main(String[] args) throws InterruptedException{
Thread[] ts = new Thread[10];
for(int k=0;k<10;k++){
ts[k] = new Thread(new AddThread());
}
for(int k=0;k<10;k++) {
ts[k].start();
}
for(int k=0;k<10;k++){
ts[k].join();
}
System.out.println(i);
}
}
一个普通的int变量,以上面的代码相加,最后的值可能不是100000,因为10个线程共同对i做加法,但是i++的过程不是原子操作,会出现线程不安全的可能。可以按照下面的方法修改,将i++加锁,每次只能有一个线程进行i++,这样就能保证最终i的值为100000。
public void run(){
for(int k=0;k<10000;k++)
synchronized(AtomicNoIntegerDemo.class) {
i++;
}
}
这样做是使用加锁的方式,这样做效率会比较低,而无锁是一种乐观的策略,它使用CAS(Compare And Swap)策略来鉴别线程冲突,一旦检测到冲突产生,就重试当前操作直到没有冲突为止。
CAS优势:
CAS:算法过程:
而原子类就是使用CAS来实现的,JDK有一个并发包atomic,里面实现了一些直接使用CAS操作的线程安全的类型。上面那个例子可以使用AtomicTnteger来保证线程安全。
代码如下,很好理解
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicNoIntegerDemo {
static AtomicInteger i = new AtomicInteger();
public static class AddThread implements Runnable{
@Override
public void run(){
for(int k=0;k<10000;k++)
i.incrementAndGet();
}
}
public static void main(String[] args) throws InterruptedException{
Thread[] ts = new Thread[10];
for(int k=0;k<10;k++){
ts[k] = new Thread(new AddThread());
}
for(int k=0;k<10;k++) {
ts[k].start();
}
for(int k=0;k<10;k++){
ts[k].join();
}
System.out.println(i);
}
}
2.无锁的对象引用:AtomicReference
上面讲述了AtomicInteger的用法,但如果一个对象,比如Student类的一个对象student,想要原子的更新等,那么就不能用AtomicInteger了,这时候可以使用AtomicReference,利用泛型,实现各种类对象的原子操作,保证线程安全。一个例子如下所示。
public class JustTry {
public static void main(String[] args) throws InterruptedException {
//可以在定义student1时传入一个Student对象作为参数,也可以在下面的set时设置student1的值
AtomicReference<Student> student1 = new AtomicReference<>();
Student s1 = new Student("Tom",19);
Student s2 = new Student("Tony",20);
student1.set(s1);
while (true){
if(student1.compareAndSet(s1,s2)) break;
}
System.out.println(student1.get().getName());
//可以看到student1的改变没有影响s1
System.out.println(s1.getName());
}
public static class Student{
private int age;
private String name;
Student(String name,int age){
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
AtomicReference有一个问题,如果在CAS过程中,其他线程改变了这个变量,又改回去了,那么CAS会照常进行,没有反映,这在一些情况下没有影响,但在一些情况下可能会造成很大的问题,所以需要一回人标志位判断这个变量是否改变过,这时AtomicStampedReference可以排上用场,注意,AtomicStampedReference定义时需要传入泛型类的对象来初始化,CAS时要判断标志位是否变化,其他和AtomicReference区别不大。一个例子:
import java.util.concurrent.atomic.AtomicStampedReference;
public class AtomicStampedReferenceDemo {
static AtomicStampedReference<Integer> money = new AtomicStampedReference<Integer>(19, 0);
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
final int timestamp = money.getStamp();
new Thread() {
public void run() {
while (true) {
while (true) {
Integer m = money.getReference();
if (m < 20) {
if (money.compareAndSet(m, m + 20, timestamp, timestamp + 1)) {
System.out.println("余额小于20元,充值成功,余额:" + money.getReference() + "元");
break;
}
} else {
// System.out.println("余额大于20元,无需充值");
break;
}
}
}
}
}.start();
}
new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
while (true) {
int timestamp = money.getStamp();
Integer m = money.getReference();
if (m > 10) {
System.out.println("大于10元");
if (money.compareAndSet(m, m - 10, timestamp, timestamp + 1)) {
System.out.println("成功消费10元,余额:" + money.getReference());
break;
}
} else {
System.out.println("没有足够的金额");
break;
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
3.无锁的数组 AtomicIntegerArray
无锁的数组可以使用AtomicIntegerArray,可以使用这个类提供的方法,线程安全地对数组中的元素进行操作。
mport java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArrayDemo {
static AtomicIntegerArray arr = new AtomicIntegerArray(10);
public static class AddThread implements Runnable {
public void run() {
for (int k = 0; k < 10000; k++)
arr.getAndIncrement(k % arr.length());
}
}
public static void main(String[] args) throws InterruptedException {
Thread[] ts = new Thread[10];
for (int k = 0; k < 10; k++) {
ts[k] = new Thread(new AddThread());
}
for (int k = 0; k < 10; k++) {
ts[k].start();
}
for (int k = 0; k < 10; k++) {
ts[k].join();
}
System.out.println(arr);
}
}
4. 对象中某个字段的原子操作:AtomicIntegerFieldUpdater
上面的AtomicReference只能保证原子地操作对象本身,而AtomicIntegerFieldUpdater可以原子地操作对象中的字段。下面代码中Candidate,score和allSore总是相等,说明AtomicIntegerFieldUpdater保证了原子操作和线程安全。
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public class AtomicIntegerFieldUpdaterDemo {
public static class Candidate {
int id;
AtomicInteger score = new AtomicInteger(0);
}
public final static AtomicIntegerFieldUpdater<Candidate> scoreUpdater =
AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");
public static AtomicInteger allscore = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
final Candidate stu = new Candidate();
Thread[] t = new Thread[10000];
for (int i = 0; i < 10000; i++) {
t[i] = new Thread() {
public void run() {
if (Math.random() > 0.4) {
scoreUpdater.incrementAndGet(stu);
allscore.incrementAndGet();
}
}
};
t[i].start();
}
for (int i = 0; i < 10000; i++) {
t[i].join();
}
System.out.println("score=" + stu.score);
System.out.println("allscore=" + allscore);
}
}
注意点:
: