Java中的原子类

主要参考《实战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);
    }
}

注意点:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章