java线程安全性-原子13个操作类

(一)原子类简介     
     当程序更新一个 ,如果多 线 程同 更新 量,可能得到期望之外的 ,比如 变量 i=1 A 线 程更新 i+1 B 线 程也更新 i+1 经过 两个 线 程操作之后可能 i 不等于 3 ,而是等于 2 。因 A B 线 程在更新 i 候拿到的 i 都是 1 就是 线 程不安全的更新操作,通常我 会使 synchronized 来解决 问题 synchronized 会保 线 程不会同 更新 i
      Java JDK 1.5 开始提供了 java.util.concurrent.atomic 包(以下 Atomic 包), 个包中 的原子操作 提供了一种用法 简单 、性能高效、 线 程安全地更新一个 量的方式。
      为变 量的 型有很多种,所以在 Atomic 包里一共提供了 13 ,属于 4 型的原子更 新方式,分 是原子更新基本 型、原子更新数 、原子更新引用和原子更新属性(字段)。 Atomic 包里的 基本都是使用 Unsafe 实 现 的包装
 
(二)原子更新基本类型类
 
1.atomic包下的所有类示意图
2.使用原子的方式更新基本 型, Atomic 包提供了以下 3 类。
  • AtomicBoolean:原子更新布
  • AtomicInteger:原子更新整型
  • AtomicLong:原子更新整型
以上 3 提供的方法几乎一模一 ,所以 AtomicInteger 解、 AtomicInteger 的常用方法如下:
  • int addAndGetint delta):以原子方式将入的数例中的AtomicInteger里的 value)相加,并返回果。
  • boolean compareAndSetint expectint update):如果入的数等于以原子方式将该值设为输入的
  • int getAndIncrement():以原子方式将当前1,注意,里返回的是自增前的,等价于i++。
  • int incrementAndGet():以原子方式将当前1,等价于++1

3.AtomicBoolean案例演示  参考:AtomicBoolean介绍与使用

@Slf4j
@ThreadSafe
public class AtomicBooleanTest {
    private static AtomicBoolean isHappened = new AtomicBoolean(false);
    // 请求总数
    public static int clientTotal = 5000;
    // 同时并发执行的线程数
    public static int threadTotal = 200;
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    test();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("isHappened:{}", isHappened.get());
    }

    private static void test() {
        //并发同步控制,保证只有一个线程执行
        if (isHappened.compareAndSet(false, true)) {
            log.info("execute");
        }
    }
}

4.AtomicInteger案例演示

        //结果:i = 10  j = 20
        AtomicInteger atomicInteger = new AtomicInteger();
        int i = atomicInteger.addAndGet(10);
        int j = atomicInteger.addAndGet(10);
        System.out.println("i = " + i);
        System.out.println("j = " + j);

        //等价于i++  reuslt1 = 0
        AtomicInteger atomicInteger1 = new AtomicInteger();
        int reuslt1 = atomicInteger1.getAndIncrement();
        System.out.println("reuslt1 = " + reuslt1);

        //等价于++i  reuslt2 = 1
        AtomicInteger atomicInteger2 = new AtomicInteger();
        int reuslt2 = atomicInteger2.incrementAndGet();
        System.out.println("reuslt2 = " + reuslt2);

        //result3 = 100
        AtomicInteger atomicInteger3 = new AtomicInteger(10);
        atomicInteger3.getAndSet(100);
        int result3 = atomicInteger3.get();
        System.out.println("result3 = " + result3);

        //CAS乐观锁和数据库乐观锁类似 b1 = false  b2 = true
        AtomicInteger atomicInteger4 = new AtomicInteger(10);
        boolean b1 = atomicInteger4.compareAndSet(11, 100);
        boolean b2 = atomicInteger4.compareAndSet(10, 100);
        System.out.println("b1 = " + b1);
        System.out.println("b2 = " + b2);

5.使用原子类确保线程安全

@NotThreadSafe
public class UnsafeSequences {
  //多个线程并发访问共享变量n,线程不安全  
  private int n;
  private  int add(){
      return n++;
  }
    public static void main(String[] args) {
        Thread t ;
        UnsafeSequences unsafeSequences =new UnsafeSequences();
        for (int i = 0; i <10000 ; i++) {
           t= new Thread(new Runnable() {
                @Override
                public void run() {
                    int  reuslt = unsafeSequences.add();
                    System.out.println("reuslt = " + reuslt);
                }
            });
            t.start();
        }
    }
}
@ThreadSafe
public class SafeSequence {
 private static AtomicInteger atomicInteger =new AtomicInteger();
  private  int add(){
      return atomicInteger.getAndIncrement();
  }

    public static void main(String[] args) throws InterruptedException {
       Thread t ;
        for (int i = 0; i <10000 ; i++) {
           t= new Thread(new Runnable() {
                @Override
                public void run() {
                   new SafeSequence().add();
                }
            });
            t.start();
            t.join();
        }
        System.out.println(atomicInteger.get());
    }
}

6.getAndIncrement方法实现原子操作原理分析

 public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

var1:指的是AtomicInteger对象

var2:指的是valueOffset内存首地址偏移量--当前值

var4:指的是常量1

var5:内存中的值--期望值

当var2当前值和var5期望值相等的情况下,将值更新为var5+var4

(三)原子更新数组

1.通原子的方式更新数里的某个元素,Atomic包提供了以下4

  • AtomicIntegerArray:原子更新整型数里的元素
  • AtomicLongArray:原子更新整型数里的元素
  • AtomicReferenceArray:原子更新引用型数里的元素
AtomicIntegerArray 主要是提供原子的方式更新数 里的整型,其常用方法如下:
  • ·int addAndGetint iint delta):以原子方式将与数中索引i的元素相加
  • boolean compareAndSetint iint expectint update):如果当前等于以原子
    方式将数 位置 i 的元素 置成 update

2.案例演示

@ThreadSafe
public class AtomicIntegerArrayDemo {
    public static void main(String[] args) {
        int[] a = {5,6,7,8,9};
        AtomicIntegerArray array = new AtomicIntegerArray(a);
        array.addAndGet(0,1);
        int result = array.get(0);
        //result = 6
        System.out.println("result = " + result);
        //array = [6, 6, 7, 8, 9]
        System.out.println("array = "+array.toString());

        //array = [5, 6, 7, 8, 9]
        array.compareAndSet(0,6,5);
        System.out.println("array = "+array.toString());

    }
}

(四) 原子更新引用类型

        原子更新基本 型的 AtomicInteger ,只能更新一个 量,如果要原子更新多个 量,就需 要使用 个原子更新引用 型提供的 Atomic 包提供了以下 3
  • AtomicReference:原子更新引用
  • AtomicReferenceFieldUpdater:原子更新引用型里的字段
  • AtomicMarkableReference:原子更新标记位的引用型。可以原子更新一个布型的标记位和引用型。构造方法是AtomicMarkableReferenceV initialRefboolean initialMark

1.AtomicReference案例演示

public class AtomicReferenceTest {
   private static AtomicReference atomicReference = new AtomicReference();
    public static void main(String[] args) {
        Person person1 = new Person();
        person1.setName("zhangsan");
        person1.setAge(18);
        atomicReference.set(person1);

        Person person2 = new Person();
        person2.setName("lisi");
        person2.setAge(22);

        atomicReference.compareAndSet(person1,person2);
        //结果:Person{name='lisi', age=22}
        System.out.println(atomicReference);
    }

    static  class Person{
        private String name;
        private int age;
    }
}

2.AtomicReferenceFieldUpdater案例演示

public class AtomicReferenceFieldUpdaterTest {
   private static AtomicReferenceFieldUpdater updater=AtomicReferenceFieldUpdater.newUpdater(Person.class,String.class,"name");
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("zhangsan");
        person.setAge(18);

        updater.compareAndSet(person, person.getName(),"lisi");
        //结果:name = lisi
        System.out.println("name = "+person.getName());

        //将lisi修改为wangwu
        updater.getAndSet(person,"wangwu");
        System.out.println("name = "+person.getName());
    }

    static class Person{

        volatile String name;
        volatile int age;
    }
}

(五)原子更新类字段

如果需原子地更新某个 里的某个字段 ,就需要使用原子更新字段 Atomic 包提供了以下 3 类进 行原子字段更新。
  • AtomicIntegerFieldUpdater:原子更新整型的字段的更新器
  • AtomicLongFieldUpdater:原子更新整型字段的更新器
  • AtomicStampedReference:原子更新有版本号的引用型。该类将整数与引用关
    来,可用于原子的更新数据和数据的版本号,可以解决使用 CAS 行原子更新 可能出
    ABA 问题
要想原子地更新字段 需要两步:
第一步,因 原子更新字段 都是抽象 ,每次使用的时 候必 使用静 方法 newUpdater() 建一个更新器,并且需要 置想要更新的 和属性。
二步,更新 的字段(属性)必 使用 public volatile 符。
 
AtomicIntegerFieldUpdater 案例演示
@Slf4j
public class AtomicIntegerFieldUpdaterTest {
    //第一步,因为原子更新字段类都是抽象类,每次使用的时候必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。
    private static AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> updater =
            AtomicIntegerFieldUpdater.newUpdater(AtomicIntegerFieldUpdaterTest.class, "count");

    //第二步,更新类的字段(属性)必须使用public volatile修饰符。
    @Getter
    public volatile int count = 100;

    public static void main(String[] args) {

        AtomicIntegerFieldUpdaterTest updaterTest = new AtomicIntegerFieldUpdaterTest();

        if (updater.compareAndSet(updaterTest, 100, 120)) {
            //update success 1, 120
            log.info("update success 1, {}", updaterTest.getCount());
        }

        if (updater.compareAndSet(updaterTest, 100, 120)) {
            log.info("update success 2, {}", updaterTest.getCount());
        } else {
            // update failed 3, 120
            log.info("update failed 3, {}", updaterTest.getCount());
        }
    }
}

(六)Atomic包下相关面试题目

 

 

 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值