java多线程学习之 11 atomic类型变量(极简同步技巧)

极简同步技巧:核心思想是尽最大程度缩小同步代码的范围

硬件知识:
寄存器:CPU中的临时存储器
主存储器:就是内存
_____________________________________________________________________________
thread之间绝不会共享保存在寄存器中的数据,使用volatile关键字能够确保用该关键字声明的变量不会保持在寄存器中!即用volatile声明的变量会被完整完全的保存在主存储器中(内存中)。
_____________________________________________________________________________
 

  • 变量原子性:在多线程并发的条件下,对于变量的操作是线程安全的,不会受到其他线程的干扰

the use of atomic classes. This set of classes, introduced in J2SE 5.0, allows certain operations on certain types of data to be defined atomically. These classes provide a nice data abstraction for the operations while preventing the race conditions that would otherwise be associated with the operation. These classes are also interesting because they take a different approach to synchronization: rather than explicitly synchronizing access to the data, they use an approach that allows race conditions to occur but ensures that the race conditions are all benign. Therefore, these classes automatically avoid explicit synchronization.

原子类的使用。J2SE 5.0中引入的这组类允许原子地定义对特定类型数据的特定操作。这些类为操作提供了很好的数据抽象,同时防止了与操作相关的竞争条件。这些类也很有趣,因为它们采用了不同的同步方法:它们使用的方法不是显式同步对数据的访问,而是允许竞争条件发生,但确保竞争条件都是良性的。因此,这些类会自动避免显式同步。(自带隐式同步属性,且在变量级别的实现隐式同步,大大缩小了代码的同步范围区间)

切记:争用锁,肯定是要耗费性能的,在获得竞争锁之前,其当前持有者必须将其释放。想要获取争用锁的线程必须始终等待锁被释放,才有机会获得锁 ,盲目同步是要耗费性能的,这种情况导致程序员试图限制程序中的同步。这是个好主意;你当然不想在程序中进行不必要的同步,就像你不想进行不必要的计算一样。但是,有时你可以完全避免同步吗?答案是:必要时还得同步,不必要时不要同步!

 In all other cases, if multiple threads access the same set of data, you must explicitly synchronize all access to that data in order to prevent various race conditions

在所有其他情况下,如果多个线程访问同一组数据,则必须显式同步对该数据的所有访问,以防止出现各种争用情况

Contended and Uncontended Locks

The terms contended and uncontended refer to how many threads are operating on a particular lock. A lock that is not held by any thread is an uncontended lock: the first thread that attempts to acquire it immediately succeeds.

When a thread attempts to acquire a lock that is already held by another thread, the lock becomes a contended lock. A contended lock has at least one thread waiting for it; it may have many more. Note that a contended lock becomes an uncontended one when threads are no longer waiting to acquire it.

java中通过atomic关键字实现变量的原子性,原子性变量又是间接通过volatile类型变量来实现完成的

volatile不是线程安全的,

atomic类型变量是线程安全的. 

比如增量操作(x++)看上去类似一个单独操作,实际上它是一个由[读取-修改-写入]操作序列组成的组合操作,必须以原子方式执行,而volatile不能提供必须的原子特性。实现正确的操作,应该使x的值在操作期间保持线程安全,而volatile变量无法实现这点

然而,Java提供了java.util.concurrent.atomic.*包下的变量或引用,让变量或对象的操作具有原子性,在高并发的情况下,依然能保持获取到最新修改的值,常见的有

AtomicBoolean、AtomicReference,    AtomicInteger  ,    AtomicLong等

 

  • volatile原理:对于值的操作,会立即更新到主存中,当其他线程获取最新值时会从主存中获取
  • atomic原理:对于值的操作,是基于底层硬件处理器提供的原子指令,保证并发时线程的安全

Atomic

Atomic的包名为java.util.concurrent.atomic。这个包里面提供了一组原子变量的操作类,这些类可以保证在多线程环境下,当某个线程在执行atomic的方法时,不会被其他线程打断,而别的线程就像自旋锁(乐观锁)一样,不断重试一直等到该方法执行完成,才由JVM从等待队列中选择一个线程执行
 

三、CAS

1、CAS简介

CAS指的是现代CPU广泛支持的一种对内存中的共享数据进行操作的一种特殊指令。这个指令会对内存中的共享数据做原子的读写操作。在Java并发应用中通常指CompareAndSwap或CompareAndSet,即比较并交换,是实现并发算法时常用到的一种技术。java.util.concurrent包中借助CAS实现了区别于synchronized同步锁的一种乐观锁。乐观锁就是每次去取数据的时候都乐观的认为数据不会被修改,因此这个过程不会上锁,但是在更新的时候会判断一下在此期间的数据有没有更新(比较值是否变化)
 

、CAS思想 compare and set

CAS有三个参数,当前内存值V、旧的预期值A、即将更新的值B,当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做,并返回false

3、CAS优缺点

系统在硬件层面保证了CAS操作的原子性,不会锁住当前线程,它的效率是很高的。但是在并发越高的条件下,失败的次数会越多,CAS如果长时间不成功,会极大的增加CPU的开销,因此CAS不适合竞争十分频繁的场景
CAS只能保证一个共享变量的原子操作,对多个共享变量操作时,无法保证操作的原子性,这时就可以用锁,或者把多个共享变量合并成一个共享变量来操作。JDK提供了AtomicReference类来保证引用对象的原子性,可以把多个变量放在一个对象里来进行CAS操作
 

ABA问题

CAS在操作值的时候检查值是否已经变化,没有变化的情况下才会进行更新。但是如果一个值原来是A,变成B,又变成A,那么CAS进行检查时会认为这个值没有变化,但是实际上却变化了。ABA问题的解决方法是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A就变成1A-2B-3A。JDK提供了AtomicStampedReference来解决ABA问题
 

简单的看下AtomicInteger提供的方法

方法名    方法作用
get()    直接返回值
set(int)    设置数据(注意这里是没有原子性操作的)
getAndIncrement()    以原子方式将当前值加1,相当于线程安全的i++操作
incrementAndGet()    以原子方式将当前值加1,相当于线程安全的++i操作
getAndDecrement()    以原子方式将当前值减1,相当于线程安全的i–操作
decrementAndGet()    以原子方式将当前值减1,相当于线程安全的–i操作
getAndSet(int)    设置指定的数据,返回设置前的数据
addAndGet(int)    增加指定的数据,返回增加后的数据
getAndAdd(int)    增加指定的数据,返回变化前的数据
lazySet(int)    仅仅当get时才会set
compareAndSet(int, int)    比较源数据和期望数据(参数一),若一致,则设置新数据(参数二)到源数据中并返回true,否则返回false

 

 

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * @author zhaoyong
 * @Date 2022/5/6
 * @Description
 */
public class TestAtomic {
    private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater
            .newUpdater(User.class, "old");

    public static void main(String[] args) {
        User user = new User("Hensen", 20);
        System.out.println(a.getAndIncrement(user));
        System.out.println(a.get(user));
    }

    public static class User {
        private String name;
        public volatile int old;//注意需要用volatile修饰

        public User(String name, int old) {
            this.name = name;
            this.old = old;
        }

        public String getName() {
            return name;
        }

        public int getOld() {
            return old;
        }
    }

}

输出

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值