【小家java】原子操作你还在用Synchronized?Atomic、LongAdder你真有必要了解一下了(下)

简介: 【小家java】原子操作你还在用Synchronized?Atomic、LongAdder你真有必要了解一下了(下)

原子更新基本类型


这个使用案例就略了,相信大家再使用他们已经0阻碍了


原子更新数组


当你操作的共享是个数组的话,就可以用这个很方便解决问题了


    public static void main(String[] args) {
        AtomicIntegerArray atomicArray = new AtomicIntegerArray(5);
        // 设置指定索引位的数值
        atomicArray.set(0, 5);
        // 也可以通过以下方法设置 (实际上默认值为0,这里加了5)
        // atomicArray.addAndGet(0, 5);
        // -- 0表示角标
        int current = atomicArray.decrementAndGet(0);
        System.out.println("current = " + current);
    }


   get(int i):获取数组指定位置的值,并不会改变原来的值


   set(int i, int newValue):为数组指定索引位设置一个新值


    getAndSet(int i, int newValue):获取数组指定位置的原始值后,用newValue这个新值进行覆盖。


   getAndAdd(int i, int delta):获取数组指定索引位的原始值后,为数组指定索引位的值增加delta。那么还有个类似的操作为:addAndGet。


   incrementAndGet、decrementAndGet


原子更新引用


使用场景:上面ABA问题有一个非常经典例子,请参加上面


若有类似的使用场景,用对应来存储数据,那么使用这个会非常的方便。例子其实非常简单,这里就不贴出来了,主要介绍一些几个常用的API方法吧:


   get()


   compareAndSet(V expect, V update):如果当前值与给定的expect相等,(注意是引用相等而不是equals()相等),更新为指定的update值。


   .getAndSet(V newValue):原子地设为给定值并返回旧值。


   set(V newValue):不管三七二十一,直接把内存里值设置为此值。


原子更新字段


这个可以算是原子更新引用更新引用的一个很好补充。上面根性我们只能全量更新,并且对象的地址都完全变化了。比如我们要更新一个学生的成绩,如果你new一个带有新成绩的Student进来,那就相当于Student对象都变了,显然是不符合我们要求的。


因此java提供了我们针对字段的跟新的原子操作,可谓是一个很好的补充。

当然啦,它使用起来还是稍微有点麻烦的,它是基于反射实现,该字段还不能是private的,且必须被volatile 修饰。


这个在业务上几乎涉及不到,但是在我们框架设计行,还是有可能被适用到的。比如我们内部定义一颗树,可以设计为:


private volatile Node left, right;


因为使用极少,因此有兴趣的朋友可以自己去玩玩,这里就略过吧


JDK8新增


DoubleAccumulator、LongAccumulator、DoubleAdder、LongAdder


今天看到阿里巴巴的手册里面说 ,如果你使用的JDK8和以上版本,建议使用LongAdder代替AotmicLong


受限于文章篇幅,关于他们的使用以及和LongAdder和AotmicLong的性能测试对比,请移步这篇博文专门讲解:【小家java】AtomicLong可以抛弃了,请使用LongAdder代替(或使用LongAccumulator)


悲观锁和乐观锁(Java都提供了对应支持)


为了更好的理解上面的一些操作原理,本文有必要稍带讲解一些悲观锁和乐观锁的概念以及区别


在本文讲解悲观锁和乐观锁,主要代表是synchronized和CAS的区别


悲观锁


悲观锁是一种独占锁,它假设的前提是“冲突一定会发生”,所以处理某段可能出现数据冲突的代码时,这个代码段就要被某个线程独占。而独占意味着“其它即将执行这段代码的其他线程”都将进入“阻塞”/“挂起”状态。


synchronized关键字就是java对于悲观锁的实现。


由于悲观锁的影响下,其他线程都将进入 阻塞/挂起 状态。而我们知道,CPU执行线程状态切换是要耗费相当资源的,这主要涉及到CPU寄存器的操作。所以悲观锁在性能上不会有太多惊艳的表现(但是一般也不至于成为性能瓶颈,所以各位也不要一棒子打死)


乐观锁


乐观锁假定“冲突不一定会出现”,如果出现冲突则进行重试,直到冲突消失。 由于乐观锁的假定条件,所以乐观锁不会独占资源,性能自然在**多数情况下**就会好于悲观锁。


AtomicInteger是一个标准的乐观锁实现,sun.misc.Unsafe是JDK提供的乐观锁的支持。

    public final long getAndAddLong(Object var1, long var2, long var4) {
        long var6;
        do {
            var6 = this.getLongVolatile(var1, var2);
        } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
        return var6;
    }

为什么是多数情况呢?因为一旦多线程对某个资源的抢占频度达到了某种规模,就会导致乐观锁内部出现多次更新失败的情况,最终造成乐观锁内部进入一种“活锁”状态。这时乐观锁的性能反而没有悲观锁好。


如果我们很好的理解了乐观锁,并且能很熟练的应用的话,我们可以把它运用到我们项目了,帮助改善性能,比一遇到并发问题就去使用悲观锁的选手,显得更加的NB轰轰了有木有

相关文章
|
5天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
30 4
|
28天前
|
算法 Java 程序员
Java中的Synchronized,你了解多少?
Java中的Synchronized,你了解多少?
|
26天前
|
Java
让星星⭐月亮告诉你,Java synchronized(*.class) synchronized 方法 synchronized(this)分析
本文通过Java代码示例,介绍了`synchronized`关键字在类和实例方法上的使用。总结了三种情况:1) 类级别的锁,多个实例对象在同一时刻只能有一个获取锁;2) 实例方法级别的锁,多个实例对象可以同时执行;3) 同一实例对象的多个线程,同一时刻只能有一个线程执行同步方法。
15 1
|
29天前
|
Java 开发者
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选
【10月更文挑战第6天】在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选。相比 `synchronized`,Lock 提供了更灵活强大的线程同步机制,包括可中断等待、超时等待、重入锁及读写锁等高级特性,极大提升了多线程应用的性能和可靠性。通过示例对比,可以看出 Lock 接口通过 `lock()` 和 `unlock()` 明确管理锁的获取和释放,避免死锁风险,并支持公平锁选择和条件变量,使其在高并发场景下更具优势。掌握 Lock 接口将助力开发者构建更高效、可靠的多线程应用。
23 2
|
25天前
|
安全 Java 开发者
java的synchronized有几种加锁方式
Java的 `synchronized`通过上述三种加锁方式,为开发者提供了从粗粒度到细粒度的并发控制能力,满足了不同场景下的线程安全需求。合理选择加锁方式对于提升程序的并发性能和正确性至关重要,开发者应根据实际应用场景的特性和性能要求来决定使用哪种加锁策略。
13 0
|
2月前
|
存储 安全 Java
Java并发编程之深入理解Synchronized关键字
在Java的并发编程领域,synchronized关键字扮演着守护者的角色。它确保了多个线程访问共享资源时的同步性和安全性。本文将通过浅显易懂的语言和实例,带你一步步了解synchronized的神秘面纱,从基本使用到底层原理,再到它的优化技巧,让你在编写高效安全的多线程代码时更加得心应手。
|
2月前
|
缓存 Java 编译器
JAVA并发编程synchronized全能王的原理
本文详细介绍了Java并发编程中的三大特性:原子性、可见性和有序性,并探讨了多线程环境下可能出现的安全问题。文章通过示例解释了指令重排、可见性及原子性问题,并介绍了`synchronized`如何全面解决这些问题。最后,通过一个多窗口售票示例展示了`synchronized`的具体应用。
|
3月前
|
Java 开发者
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选。相比 `synchronized`,Lock 提供了更灵活强大的线程同步机制,包括可中断等待、超时等待、重入锁及读写锁等高级特性,极大提升了多线程应用的性能和可靠性。通过示例对比,可以看出 Lock 接口通过 `lock()` 和 `unlock()` 明确管理锁的获取和释放,避免死锁风险,并支持公平锁选择和条件变量,使其在高并发场景下更具优势。掌握 Lock 接口将助力开发者构建更高效、可靠的多线程应用。
28 2
|
3月前
|
传感器 C# 监控
硬件交互新体验:WPF与传感器的完美结合——从初始化串行端口到读取温度数据,一步步教你打造实时监控的智能应用
【8月更文挑战第31天】本文通过详细教程,指导Windows Presentation Foundation (WPF) 开发者如何读取并处理温度传感器数据,增强应用程序的功能性和用户体验。首先,通过`.NET Framework`的`Serial Port`类实现与传感器的串行通信;接着,创建WPF界面显示实时数据;最后,提供示例代码说明如何初始化串行端口及读取数据。无论哪种传感器,只要支持串行通信,均可采用类似方法集成到WPF应用中。适合希望掌握硬件交互技术的WPF开发者参考。
67 0
|
3月前
|
安全 Java
Java并发编程实战:使用synchronized和ReentrantLock实现线程安全
【8月更文挑战第31天】在Java并发编程中,保证线程安全是至关重要的。本文将通过对比synchronized和ReentrantLock两种锁机制,深入探讨它们在实现线程安全方面的优缺点,并通过代码示例展示如何使用这两种锁来保护共享资源。