Java并发编程入门(二十四)Java原子类

217 阅读5分钟

banner窄.png

铿然架构  |  作者  /  铿然一叶 这是铿然架构的第 89 篇原创文章

相关阅读:

Java并发编程(一)知识地图
Java并发编程(二)原子性
Java并发编程(三)可见性
Java并发编程(四)有序性
Java并发编程(五)创建线程方式概览
Java并发编程入门(六)synchronized用法
Java并发编程入门(七)轻松理解wait和notify以及使用场景
Java并发编程入门(八)线程生命周期
Java并发编程入门(九)死锁和死锁定位
Java并发编程入门(十)锁优化
Java并发编程入门(十一)限流场景和Spring限流器实现
Java并发编程入门(十二)生产者和消费者模式-代码模板
Java并发编程入门(十三)读写锁和缓存模板
Java并发编程入门(十四)CountDownLatch应用场景
Java并发编程入门(十五)CyclicBarrier应用场景
Java并发编程入门(十六)秒懂线程池差别
Java并发编程入门(十七)一图掌握线程常用类和接口
Java并发编程入门(十九)异步任务调度工具CompleteFeature
Java并发编程入门(二十)常见加锁场景和加锁工具
Java并发编程入门(二十一)volatile关键字
Java并发编程入门(二十二)ThreadLocal变量
Java并发编程入门(二十三)守护线程


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操作是原子操作从而做到线程安全。


<--阅过留痕,左边点赞!