美文网首页
谈谈synchronized、volatile、AtomicIn

谈谈synchronized、volatile、AtomicIn

作者: salix_ | 来源:发表于2020-02-03 16:51 被阅读0次

内容

Synchronized
一:sychronized锁的是对象
二:可重入
三: 当synchronized遇到异常
四:锁的是对象,不是那个引用变量名
五:Synchronized得底层实现
六:Synchronized的锁升级步骤
volatile
一:由问题引入Volatile,耐心看代码
二:重排序问题
AtomicInteger

synchronized

一:sychronized锁的是对象

1:非静态方法,锁this对象

下面两种情况都是一样的, 注意第二种synchronized锁的也是this对象,不是锁一个函数,也不是锁一段代码块。


image.png
image.png
2:静态方法,锁class对象(反射机制)

对于静态方法,就不能锁this对象了,因为即使你不new这个类的对象,也可以调用静态方法, 所以这个时候锁的是类的class对象(反射知识)


image.png
image.png

二:可重入

1:啥是可重入锁?

https://www.cnblogs.com/incognitor/p/9894604.html

2:情景一:一个线程的一个同步方法调用另一个同步方法
image.png
image.png
3:情景二:一个线程的子类同步方法调用父类的同步方法,也是可重入的。(下面这段代码不会产生死锁)
image.png

三:当synchronized遇到异常:

synchronized遇到异常之后,锁资源会释放,所以用synchronized锁的时候,一定要处理好异常。例:a线程对资源进行访问,修改了一半,出现了异常,锁释放。b线程继续访问,a线程没有回滚,也没有完成整个操作,可能会出问题。
最好的方法就是try-catch处理异常,可以在catch里面回滚异常。

四:锁的是对象,不是那个引用变量名,下面s1,s2是同一把锁。

image.png

五:Synchronized得底层实现

汇编级别实现语句:lock cmpxchg
cmpxchg操作系统级别得指令 本质就是和内存中得值 比较在修改
lock就是加了一个锁
硬件是锁一个北桥信号

1. 每个对象new出来的时候,在jvm里的格局
image.png
2. synchronized其实主要是修改了对象头markword,通过修改对象头来实现偏向锁、自旋锁、重量级锁的转换。
3.怎样修改呢?其实对象头是一个8位,32b大小的01串

6.Synchronized的锁升级步骤

首先是从申请到偏向锁,如果偏向锁不够了就升级到自旋锁。(竞争加剧,比如有线程超过10次自旋)这个时候就升级到重量级锁()

volatile(使得变量在线程之间可见)

一:

1:下面这段代码居然会陷入死循环?在main这个线程里面讲running变成false居然没有改变创建线程的死循环!
image.png
2:加上一个volatile修饰running就可以了
image.png
3:为啥会这样?

涉及到java的内存模型(JMM)
贴一个链接https://www.jianshu.com/p/8a58d8335270
可以去学习一下。
大概意思就是JMM里有一个主内存,running这些个变量都存在于主内存。然而每一个线程都有一块自己的内存空间,创建线程A执行m()函数,会讲running变量拷贝一份到A的自己的内存空间。主线程(main)也会拷贝 一份running到自己的区域,设置running=false之后就把running写回主内存。但是A线程不会及时看主内存变了没有(并不是说不会主动更新主内存的变量到该线程内存区,比如下面图代码加个sleep就也可以正常结束。)所以导致了错误,具体的俺也不太清楚,应该是根据设置的算法有关系。而volaile的作用就是当变量改变的时候通知使用该变量的线程再刷新一下。

image.png
4:volatile只是保证了可见性,没有保证原子性。(就是不能代替synchronized)(据说面试会问到!!!

因为volatile比synchronized更轻量级,所以能用volatile就不用synchronized。

5:volatile和synchronized的区别:
  • volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
  • volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
  • volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
  • volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
  • volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

二:重排序问题(自己百度一下把)

new一个没有加载进入内存的class的时候

  1. 首先会让其静态变量初始化(int 类型的就是0 对象默认就是null)
  2. 然后让静态变量赋值(static int count=3,那这一步就是count=3)
  3. 让变量指针指向这块内存(相当于这个变量指针不为空了)
    指针冲排序就是让2、3步骤反转。执行顺序是1、3、2。如果我们1、3执行完毕之后,这个时候指针默认不为空,会出现问题。可以看下面这篇文章,https://www.jianshu.com/p/840475e3c440

AtomicInteger

里面的操作可以保证原子性,incrementAndGet利用的就是CAS技术,相当于底层一条原子指令,这个过程中并没有上锁!只是用原子指令保证了线程安全。可以百度学习一下CAS。jdk1.8中ConcurrentHashMap内部利用的也是CAS技术(JDK1.7中ConcurrentHashMap利用的是分段锁)。实现用的是非常底层的技术,所以效率更高。
举个例子,创建10个线程每个线程执行count++ 1000,最后能成功输出10000。避免了不可重复读问题。(count是AtomicInteger类型的)


image.png

相关文章

网友评论

      本文标题:谈谈synchronized、volatile、AtomicIn

      本文链接:https://www.haomeiwen.com/subject/kxuhxhtx.html