synchronized、volatile以及CAS的区别

在讲解synchronized、volatile、CAS的区别之前,需要弄明白什么共享资源、什么是线程安全、线程间的可见性问题以及Java操作的原子性。弄明白这些问题可以加深你对synchronized、volatile、CAS的理解。下面我会先对这些问题进行简单的说明,

 

什么是资源共享?

同一个资源被多个线程所持有 或者说多个线程可以访问该资源,这样的资源称之为共享资源。

 

什么线程安全?

就是指多个线程同时读写同一个资源,并没有使用任何的同步措施,导致出现脏数据或者不可预见的结果。

注:如果多个线程只是访问共享资源,而不去修改的话,是不存在线程安全问题的。只有当线程修改共享资源的时候,才会出现线程安全问题。

 

线程间的可见性问题

java内存模型规定,所有的变量都存放在主内存中,当线程使用变量的时候,会把主内存中的变量赋值到自己的工作内存中,线程读写变量时,操作的是线程自己内存中变量。

进一步解读:

1、变量(也就是共享资源)存储在主内存中

2、线程使用变量的时候,会在自己的工作内存保存该变量的副本。

3、线程在工作内存中修改副本,并把修改后的变量副本同步到主内存中

4、主内存中的变量已经发生了改变,但不会将修改后的变量同步给其他线程,其他线程的工作空间中的副本变量的值还是修改前的值,也就造成了线程间资源的同步问题

这也就是线程间的不可见性

 

java操作的原子性

所谓的原子性操作,就是指执行一系列操作的时候,这些操作要么全部执行,要么全部不执行,不存在指执行一部分的情况。

在一些面试中,++i是原子性操作吗?通过一个示例简单的计数器进行说明,代码如下:

public class ThreadNotSafeCount {
    private int count;

    public int getCount() {
        return count;
    }

    public synchronized void  inc(){
        ++count;
    }
}

使用过javap  -c查看汇编代码,如下所示:

其中,简单的++count由2、5、6、7四步组成,也就是红框中所标识的汇编代码,其中第2步是获取当前count的值并放入栈顶,第5步是把常量1放入栈顶,第6步是把放入栈顶的两个值相加并把结果放入栈顶,第7步是把栈顶的结果赋值给count。由此可以发现++count,包含了count的读--改--写过程,所以++count不是一个原子性操作。

 

线程间不可见性的解决方案

synchronized锁机制解决线程间的可见性问题

线程synchronized加锁时,其他线程会进入阻塞,而该线程会清空锁块内线程工作内存中的变量,在使用这些变量的时候,会从主内存中加载。synchronized在释放锁时,会将线程工作空间中的变量同步到主内存中。这样也就解决了线程间的可见性问题。

 

volatile关键字解决线程间的可见性问题

当变量使用volatile关键字修饰变量,线程在使用该变量的时候会去主内存中读取,修改后,会把值同步给主内存。这跟synchronized有点类似。

 

synchronized锁机制和volatile关键字解决线程间的可见性问题的区别

使用synchronized锁机制,当线程获得锁之后,其他线程的调用会被阻塞,同时也会增加线程上下文切换和线程重新调度的开销。

使用volatile关键字,是非阻塞式解决方案,不会增加线程的上下文切换以及线程重新调度的开销。

 

如何确保java的原子性操作?

sychronized锁机制,当线程进入synchronized代码块前,获得锁,其他线程就会被阻塞挂起。当该线程正常退出或者抛出异常或者调用wait()系列方法释放锁时,其他线程才有机会获得锁,执行该代码块。简而言之,sychronized锁机制通过阻塞来确保java操作的原子性。

volatile关键字并不会保证操作的原子性。

 

小小总结

synchronized能解决线程之间的可见性问题和确保java操作的原子性,但是会做成线程阻塞,增加线程上下文切换和线程重新调到的开销问题、

volatile能通过非阻塞式的方式解决了线程间的可见性问题,但不能保证java操作的原子性。也不会增加线程切换和线程重新调度的开销问题。

 

什么是CAS?

CAS就是Compare And Swap,是JDK提供给的非阻塞性原子性操作。它通过硬件保证了比较--更新的原子性操作。JDK中的Unsafe提供了一些列的CAS方法,Unsafe类中的方法都是native方法,它们通过使用JNI的方式访问本地的C++实现库。


下面就compareAndSwapLong()方法进行简单介绍

public final native boolean compareAndSwapLong(Object Obj, long valueOffset, long expect, long update);

其中compareAndSwap的意思就是比较交换,CAS有四个操作数,分别为:对象内位置,对象中的变量的偏移量,变量的预期值和新的值。其操作含义是,如果obj中的内存偏移量为valueOffset,则使用新的值update替换旧的值expect。这是处理器提供的一个原子性指令

 

参考:《java并发编程之美》

 

 

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
VolatileCAS都是Java中用于实现多线程并发编程的技术。 Volatile是一种轻量级的同步机制,用于保证可见性和禁止指令重排序。当一个变量被声明为volatile时,每次对该变量的读写操作都会直接操作主内存,而不是从缓存中读取或写入。这样可以确保不同线程之间对该变量的操作是可见的,从而避免了由于线程间内存可见性问题而带来的线程安全问题。 CAS(compare and swap)是一种无锁的原子操作,它通过比较预期值和实际值来判断是否需要更新主内存中的值。当多个线程同时执行CAS操作时,只有一个线程能够成功,其他线程需要重试直到成功为止。CAS操作是通过硬件的原子指令来实现的,因此具有很高的效率。CAS操作可以确保线程之间对共享变量的更新是原子性的,从而避免了由于线程竞争而引起的数据不一致的问题。 VolatileCAS可以结合使用,来实现线程安全的操作。Volatile保证了可见性,确保了对共享变量的读写操作是立即可见的;而CAS保证了原子性,通过比较交换的方式保证了多个线程对共享变量的更新是安全的。当多个线程同时访问共享变量时,可以使用Volatile来保证可见性,使用CAS来保证原子性,从而实现线程安全。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Java多线程(Synchronized+Volatile+JUC 并发工具原理+线程状态+CAS+线程池)](https://download.csdn.net/download/weixin_43516258/87937931)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [volatile + CAS](https://blog.csdn.net/weixin_51207423/article/details/123085930)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值