Java 同步相关概念(synchronized、volatile)(悲观锁、乐观锁)(可见性、原子性和有序性)

锁的目的让一系列操作能被当作“原子”,能够正确执行。锁分为悲观锁和乐观锁,悲观锁就是假设一个线程在执行某些操作时(比如读写数据)别的线程也一定会一定会执行这些操作,叫并发冲突,所以在执行前先上锁,只能当前线程先操作,别的线程要操作时发现有锁先阻塞。乐观锁就是,假设不会有并发冲突,线程该执行什么操作就执行什么操作,只有更新的时候,才会检查有没有冲突,如果有冲突,可以让用户决定怎么处理,或者重试。乐观锁是一种实现思想。

java中的synchronized就是悲观锁,CAS机制就是乐观锁,比如AtomicInteger。

还有一点需要说明一下,锁只是保证了原子操作,其它和这个锁没有关系的线程该执行还是执行,不受影响。

在多线程编程中有一个同步的概念,比如多个线程都需要读写一个变量,这时就需要注意了,此时就会出现线程不安全的问题,导致意想不到的结果。java内存模型有可见性、原子性和有序性三性,同步就是围绕这三特性展开的。

我们声明一个变量,比如int a; 对这个进行自增操作 a++。此时的a,既没有可见性,也没有原子性和有序性。有两个线程A和B,分别对a,自增,我们假设这两个线程分别由两个CPU执行。每个CPU都有自己高速缓存,这样的话A和B线程都会缓存a,对a操作完再写回到主内存,如果A线程修改了a,可是线程B是不知道的,它缓存的a变量还是旧的。这就说明了A线程对a的修改对B线程是不可见的,这样就会出现问题。我们可以使用synchronized、volatile和final关键字让a有可见性,volatile可见性具体原理看参考的文章。我决定synchronized实现可见性很直接,就是上锁后,只能这个线程读a,所以不会出现上面说的问题。

volatile int a; 这样a就是具有了可见性,可是还是没有原子性(volatile只具有可见性和有序性),在A和B线程中分别执行a++,还是会有问题,那么我们就需要用乐观锁来保证原子性了。看看AtomicInteger的源码,就是volatile加乐观锁实现的。

synchronized由于加锁的原因,本身就保证了可见性、原子性和有序性。

Volatile关键字

 volatile是一种稍弱的同步机制,它实现了可见性和有序性,可见性是通过在给volatile修饰的变量赋值的时候,插入一条指令,让后通过cpu的EMSI协议和嗅探机制实现的;有序性是通过插入内存屏障指令实现的,也就是编译器或者执行的时候禁止对指令重排序优化。

总结一下:

synchronized是悲观锁,实现了可见性、原子性和有序性,可以实现多线程环境的同步。

volatile实现了可见性和有序性,需要和乐观锁思想配合使用,才能实现完整的同步机制。

Java中Volatile关键字详解

volatile

【Java 并发笔记】volatile 相关整理

java中的乐观锁和悲观锁

Java中CAS原理

Java AtomicInteger的用法

Java中的原子操作

发布了189 篇原创文章 · 获赞 25 · 访问量 22万+

猜你喜欢

转载自blog.csdn.net/lizhongyisailang/article/details/104413031