Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 257|回复: 0

[Java线程学习]深入浅出 Java Concurency原子操作(1)

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-10-31 23:58:58 | 显示全部楼层 |阅读模式
    从相对简单的Atomic入手(java.util.concurrent是基于Queue的并发包,而Queue,很多情况下使用到了Atomic操作,因此首先从这里开始)。很多情况下我们只是需要一个简单的、高效的、线程安全的递增递减方案。注意,这里有三个条件:简单,意味着程序员尽可能少的操作底层或者实现起来要比较容易;高效意味着耗用资源要少,程序处理速度要快;线程安全也非常重要,这个在多线程下能保证数据的正确性。这三个条件看起来比较简单,但是实现起来却难以令人满意。     通常情况下,在Java里面,++i或者--i不是线程安全的,这里面有三个独立的操作:或者变量当前值,为该值+1/-1,然后写回新的值。在没有额外资源可以利用的情况下,只能使用加锁才能保证读-改-写这三个操作时“原子性”的。 Doug Lea在未将backport-util-concurrent合并到JSR 166里面来之前,是采用纯Java实现的,于是不可避免的采用了synchronized关键字。
      
       
       
         
       

         
       
      


    public final synchronized void set(int newValue);
    public final synchronized int getAndSet(int newValue);
    public final synchronized int incrementAndGet();
       同时在变量上使用了volatile (后面会具体来讲volatile到底是个什么东东)来保证get()的时候不用加锁。尽管synchronized的代价还是很高的,但是在没有JNI的手段下纯Java语言还是不能实现此操作的。
    JSR 166提上日程后,backport-util-concurrent就合并到JDK 5.0里面了,在这里面重复使用了现代CPU的特性来降低锁的消耗。后本章的最后小结中会谈到这些原理和特性。在此之前先看看API的使用。
    一切从java.util.concurrent.atomic.AtomicInteger开始。
    int addAndGet(int delta)
               以原子方式将给定值与当前值相加。 实际上就是等于线程安全版本的i =i+delta操作。
    boolean compareAndSet(int expect, int update)
               如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。 如果成功就返回true,否则返回false,并且不修改原值。
    int decrementAndGet()
               以原子方式将当前值减 1。 相当于线程安全版本的--i操作。
    int get()
               获取当前值。
    int getAndAdd(int delta)
               以原子方式将给定值与当前值相加。 相当于线程安全版本的t=i;i+=delta;return t;操作。
    int getAndDecrement()
               以原子方式将当前值减 1。 相当于线程安全版本的i--操作。
    int getAndIncrement()
               以原子方式将当前值加 1。 相当于线程安全版本的i++操作。
    int getAndSet(int newValue)
               以原子方式设置为给定值,并返回旧值。 相当于线程安全版本的t=i;i=newValue;return t;操作。
    int incrementAndGet()
               以原子方式将当前值加 1。 相当于线程安全版本的++i操作。  
    void lazySet(int newValue)
               最后设置为给定值。 延时设置变量值,这个等价于set()方法,但是由于字段是volatile类型的,因此次字段的修改会比普通字段(非volatile字段)有稍微的性能延时(尽管可以忽略),所以如果不是想立即读取设置的新值,允许在“后台”修改值,那么此方法就很有用。如果还是难以理解,这里就类似于启动一个后台线程如执行修改新值的任务,原线程就不等待修改结果立即返回(这种解释其实是不正确的,但是可以这么理解)。
    void set(int newValue)
               设置为给定值。 直接修改原始值,也就是i=newValue操作。
    boolean weakCompareAndSet(int expect, int update)
               如果当前值 == 预期值,则以原子方式将该设置为给定的更新值。JSR规范中说:以原子方式读取和有条件地写入变量但 创建任何 happen-before 排序,因此不提供与除 weakCompareAndSet 目标外任何变量以前或后续读取或写入操作有关的任何保证。大意就是说调用weakCompareAndSet时并不能保证不存在happen-before的发生(也就是可能存在指令重排序导致此操作失败)。但是从Java源码来看,其实此方法并没有实现JSR规范的要求,最后效果和compareAndSet是等效的,都调用了unsafe.compareAndSwapInt()完成操作。

    下面的代码是一个测试样例,为了省事就写在一个方法里面来了。



      package
       xylz.study.concurrency.atomic;


      import
       java.util.concurrent.atomic.AtomicInteger;


      import
       org.junit.Test;


      import
       
      static
       org.junit.Assert.
      *
      ;


      public
       
      class
       AtomicIntegerTest
      
      {

         @Test
        public void testAll() throws InterruptedException{
             final AtomicInteger value = new AtomicInteger(10);
             assertEquals(value.compareAndSet(1, 2), false);
             assertEquals(value.get(), 10);
             assertTrue(value.compareAndSet(10, 3));
             assertEquals(value.get(), 3);
             value.set(0);
             //
             assertEquals(value.incrementAndGet(), 1);
             assertEquals(value.getAndAdd(2),1);
             assertEquals(value.getAndSet(5),3);
             assertEquals(value.get(),5);
             //
             final int threadSize = 10;
             Thread[] ts = new Thread[threadSize];
            for (int i = 0; i < threadSize; i++) {
                ts = new Thread() {
                    public void run() {
                         value.incrementAndGet();
                     }
                 };
             }
             //
            for(Thread t:ts) {
                 t.start();
             }
            for(Thread t:ts) {
                 t.join();
             }
             //
             assertEquals(value.get(), 5+threadSize);
         }

    }
      

      

      由于这里例子比较简单,这里就不做过多介绍了。

    AtomicInteger和AtomicLong、AtomicBoolean、AtomicReference差不多,这里就不介绍了。在下一篇中就介绍下数组、字段等其他方面的原子操作。




      
      
       
       

         
       

         
       
      
    复制代码
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-25 19:33 , Processed in 0.312828 second(s), 36 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表