Java原子变量 - 极悦

Java多线程编程

全部教程

×

Java原子变量

原子变量类基于CAS实现的, 当对共享变量进行read-modify-write更新操作时,通过原子变量类可以保障操作的原子性与可见性.对变量的read-modify-write更新操作是指当前操作不是一个简单的赋值,而是变量的新值依赖变量的旧值,如自增操作i++. 由于volatile只能保证可见性,无法保障原子性, 原子变量类内部就是借助一个Volatile变量,并且保障了该变量的read-modify-write操作的原子性, 有时把原子变量类看作增强的volatile变量. 原子变量类有12个,如:

               分组              原子变量类
基础数据型 AtomicInteger, AtomicLong, AtomicBoolean
数组型 AtomicIntegerArray, AtomicLongArray,AtomicReferenceArray
字段更新器 AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater
引用型 AtomicReference,AtomicStampedReference, AtomicMarkableReference

AtomicLong 

package com.wkcto.atomics.atomiclong;

import java.util.concurrent.atomic.AtomicLong;

/**
 * 使用原子变量类定义一个计数器
 * 该计数器,在整个程序中都能使用,并且所有的地方都使用这一个计数器,这个计数器可以设计为单例
 * 北京极悦老崔
 */
public class Indicator {
    //构造方法私有化
    private  Indicator(){}
    //定义一个私有的本类静态的对象
    private static  final  Indicator INSTANCE = new Indicator();
    //3)提供一个公共静态方法返回该类唯一实例
    public static  Indicator getInstance(){
        return INSTANCE;
    }

    //使用原子变量类保存请求总数,成功数,失败数
    private final AtomicLong requestCount = new AtomicLong(0);  //记录请求总数
    private final AtomicLong successCount = new AtomicLong(0);  //处理成功总数
    private final AtomicLong fialureCount = new AtomicLong(0);  //处理失败总数

    //有新的请求
    public void newRequestReceive(){
        requestCount.incrementAndGet();
    }
    //处理成功
    public void requestProcessSuccess(){
        successCount.incrementAndGet();
    }
    //处理失败
    public void  requestProcessFailure(){
        fialureCount.incrementAndGet();
    }

    //查看总数,成功数,失败数
    public long getRequestCount(){
        return requestCount.get();
    }
    public long getSuccessCount(){
        return successCount.get();
    }
    public long getFailureCount(){
        return fialureCount.get();
    }
}
package com.wkcto.atomics.atomiclong;

import java.util.Random;

/**
 * 模拟服务器的请求总数, 处理成功数,处理失败数
 * 北京极悦老崔
 */
public class Test {
    public static void main(String[] args) {
        //通过线程模拟请求,在实际应用中可以在ServletFilter中调用Indicator计数器的相关方法
        for (int i = 0; i < 10000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //每个线程就是一个请求,请求总数要加1
                    Indicator.getInstance().newRequestReceive();
                    int num = new Random().nextInt();
                    if ( num % 2 == 0 ){        //偶数模拟成功
                        Indicator.getInstance().requestProcessSuccess();
                    }else {     //处理失败
                        Indicator.getInstance().requestProcessFailure();
                    }
                }
            }).start();
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //打印结果
        System.out.println( Indicator.getInstance().getRequestCount());     //总的请求数
        System.out.println( Indicator.getInstance().getSuccessCount());     //成功数
        System.out.println( Indicator.getInstance().getFailureCount());     //失败数
    }
}

AtomicIntegerArray

原子更新数组

package com.wkcto.atomics.atomicarray;

import java.util.concurrent.atomic.AtomicIntegerArray;

/**
 * AtomicIntegerArray的基本操作
 * 原子更新数组
 * 北京极悦老崔
 */
public class Test {
    public static void main(String[] args) {
        //1)创建一个指定长度的原子数组
        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);
        System.out.println( atomicIntegerArray );   //[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        //2)返回指定位置的元素
        System.out.println( atomicIntegerArray.get(0));     //0
        System.out.println( atomicIntegerArray.get(1));     //0
        //3)设置指定位置的元素
        atomicIntegerArray.set(0, 10);
        //在设置数组元素的新值时, 同时返回数组元素的旧值
        System.out.println( atomicIntegerArray.getAndSet(1, 11) );  //0
        System.out.println( atomicIntegerArray );   //[10, 11, 0, 0, 0, 0, 0, 0, 0, 0]
        //4)修改数组元素的值,把数组元素加上某个值
        System.out.println( atomicIntegerArray.addAndGet(0, 22) );  //32
        System.out.println( atomicIntegerArray.getAndAdd(1, 33));   //11
        System.out.println( atomicIntegerArray );   //[32, 44, 0, 0, 0, 0, 0, 0, 0, 0]
        //5)CAS操作
        //如果数组中索引值为0的元素的值是32 , 就修改为222
        System.out.println( atomicIntegerArray.compareAndSet(0, 32, 222));  //true
        System.out.println( atomicIntegerArray );   //[222, 44, 0, 0, 0, 0, 0, 0, 0, 0]
        System.out.println( atomicIntegerArray.compareAndSet(1, 11, 333));  //false
        System.out.println(atomicIntegerArray);
        //6)自增/自减
        System.out.println( atomicIntegerArray.incrementAndGet(0) );    //223, 相当于前缀
        System.out.println( atomicIntegerArray.getAndIncrement(1));     //44, 相当于后缀
        System.out.println( atomicIntegerArray );   //[223, 45, 0, 0, 0, 0, 0, 0, 0, 0]
        System.out.println( atomicIntegerArray.decrementAndGet(2));     //-1
        System.out.println( atomicIntegerArray);    //[223, 45, -1, 0, 0, 0, 0, 0, 0, 0]
        System.out.println( atomicIntegerArray.getAndDecrement(3));     //0
        System.out.println( atomicIntegerArray );   //[223, 45, -1, -1, 0, 0, 0, 0, 0, 0]
    }
}
package com.wkcto.atomics.atomicarray;

import java.util.concurrent.atomic.AtomicIntegerArray;

/**
 * 在多线程中使用AtomicIntegerArray原子数组
 * 北京极悦老崔
 */
public class Test02 {
    //定义原子数组
    static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);

    public static void main(String[] args) {
        //定义线程数组
        Thread[] threads = new Thread[10];
        //给线程数组元素赋值
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new AddThread();
        }
        //开启子线程
        for (Thread thread : threads) {
            thread.start();
        }
        //在主线程中查看自增完以后原子数组中的各个元素的值,在主线程中需要在所有子线程都执行完后再查看
        //把所有的子线程合并到当前主线程中
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println( atomicIntegerArray );
    }

    //定义一个线程类,在线程类中修改原子数组
    static class AddThread extends Thread{
        @Override
        public void run() {
            //把原子数组的每个元素自增1000次
            for (int j = 0; j < 100000; j++) {
                for (int i = 0; i < atomicIntegerArray.length(); i++) {
                    atomicIntegerArray.getAndIncrement(i % atomicIntegerArray.length());
                }
            }
           /* for (int i = 0; i < 10000; i++) {
                atomicIntegerArray.getAndIncrement(i % atomicIntegerArray.length());
            }*/
        }
    }
}

AtomicIntegerFieldUpdater

AtomicIntegerFieldUpdater可以对原子整数字段进行更新,要求:
● 字符必须使用volatile修饰,使线程之间可见。
● 只能是实例变量,不能是静态变量,也不能使用final修饰。

package com.wkcto.atomics.atominintegerfiled;

/**
 * 使用AtomicIntegerFieldUpdater更新的字段必须使用volatile修饰
 * 北京极悦老崔
 */
public class User {
    int id;
    volatile  int age;

    public User(int id, int age) {
        this.id = id;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", age=" + age +
                '}';
    }
}
package com.wkcto.atomics.atominintegerfiled;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * 线程类,
 * 北京极悦老崔
 */
public class SubThread extends Thread {
    private  User user;         //要更新的User对象
    //创建AtomicIntegerFieldUpdater更新器
    private AtomicIntegerFieldUpdater<User> updater = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");

    public SubThread(User user) {
        this.user = user;
    }

    @Override
    public void run() {
        //在子线程中对user对象的age字段自增10次
        for (int i = 0; i < 10; i++) {
            System.out.println( updater.getAndIncrement(user));
        }
    }
}
package com.wkcto.atomics.atominintegerfiled;

/**
 * 北京极悦老崔
 */
public class Test {
    public static void main(String[] args) {
        User user = new User(1234, 10);
        //开启10个线程
        for (int i = 0; i < 10; i++) {
            new SubThread(user).start();
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println( user );
    }
}

AtomicReference

可以原子读写一个对象

package com.wkcto.atomics.atomicreference;

import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 使用AtomicReference原子读写一个对象
 * 北京极悦老崔
 */
public class Test01 {
    //创建一个AtomicReference对象
    static AtomicReference<String> atomicReference = new AtomicReference<>("abc");

    public static void main(String[] args) throws InterruptedException {
        //创建100个线程修改字符串
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(new Random().nextInt(20));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (atomicReference.compareAndSet("abc","def")){
                        System.out.println(Thread.currentThread().getName() + "把字符串abc更改为def");
                    }
                }
            }).start();
        }
        //再创建100个线程
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(new Random().nextInt(20));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (atomicReference.compareAndSet("def","abc")){
                        System.out.println(Thread.currentThread().getName() + "把字符串还原为abc");
                    }
                }
            }).start();
        }

        Thread.sleep(1000);
        System.out.println(atomicReference.get());
    }
}
package com.wkcto.atomics.atomicreference;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 演示AtomicReference可能会出现CAS的ABA问题
 * 北京极悦老崔
 */
public class Test02 {
    private static AtomicReference<String> atomicReference = new AtomicReference<>("abc");
    public static void main(String[] args) throws InterruptedException {
        //创建第一个线程,先把abc字符串改为"def",再把字符串还原为abc
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                atomicReference.compareAndSet("abc", "def");
                System.out.println(Thread.currentThread().getName() + "--" + atomicReference.get());
                atomicReference.compareAndSet("def", "abc");
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println( atomicReference.compareAndSet("abc", "ghg"));
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println( atomicReference.get());
    }
}
package com.wkcto.atomics.atomicreference;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * AtomicStampedReference原子类可以解决CAS中的ABA问题
 * 在AtomicStampedReference原子类中有一个整数标记值stamp, 每次执行CAS操作时,需要对比它的版本,即比较stamp的值
 * 北京极悦老崔
 */
public class Test03 {
//    private static AtomicReference<String> atomicReference = new AtomicReference<>("abc");
    //定义AtomicStampedReference引用操作"abc"字符串,指定初始化版本号为0
    private static AtomicStampedReference<String> stampedReference = new AtomicStampedReference<>("abc", 0);
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                stampedReference.compareAndSet("abc", "def", stampedReference.getStamp(), stampedReference.getStamp()+1);
                System.out.println(Thread.currentThread().getName() + "--" +stampedReference.getReference());
                stampedReference.compareAndSet("def", "abc", stampedReference.getStamp(), stampedReference.getStamp()+1);
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                int stamp = stampedReference.getStamp();        //获得版本号
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println( stampedReference.compareAndSet("abc", "ggg", stamp, stamp+1));
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println( stampedReference.getReference() );
    }
}

 

技术文档推荐

更多>>

视频教程推荐

更多>>