多线程 - Synchronized 篇
发布日期:2021-06-30 23:50:46 浏览次数:2 分类:技术文章

本文共 2177 字,大约阅读时间需要 7 分钟。

一、Synchronized 简介

Ps1:join() 方法作用:等待线程执行完后才会执行下面的接下来的语句。

Ps2:两个线程同时a++, 最后结果会比预计的少(读取a, a++, 将a写入内存),所以每一步执行完都有可能被打断,所以a值有可能没写进内存就执行另一个线程的a++操作了,so...

 

二、Synchronized的两种用法(对象锁和类锁)

1、对象锁

Ps:这里的 while 相当于 join 作用。

1.1、this

Ps:【两个线程同时访问一个对象的同步方法】。

 

1.2、lock(手动指定)

Ps:这里就体现出自定义锁lock与this的应用区别。Lock1和lock2是并行运行的,而单个lockN属于同步运行的。

Ps:如果两个块都为同一个lockN,那么与this的应用一样。

 

1.2、附:调试技巧(查看线程生命周期)

Ps:调试技巧1:

All:所有线程在这里都会停下来。

Thread:只针对当前该线程会停下来,其余线程继续执行。

Ps:调试技巧2:

查看此时此刻的属性、状态等。

步骤:

Debugger -> Frames -> 748,Thread -> Evaluate -> 输入表达式。

Ps:调试技巧3:

切换线程查看此时此刻的属性、状态等。

步骤:

Debugger -> Frames -> 748,Thread -> Evaluate -> 输入表达式。

 

1.3、普通方法锁

 

2、类锁

2.1、synchronized加在static方法上

Ps:不加 static,因为实例对象不一样,所以会导致并发运行。

Ps1:加 static,因为static代表类对象,所以把整个类对象锁住,所以只要是该类的实例对象,都会被锁住,实现同步运行【两个线程访问的是synchronized的静态方法】。

Ps2:有些人说可以加在run方法上吗?不行的。因为这里的run是接口的实现,所以通常做法是另外写一个静态方法,一举两得(达到类锁的作用也可以被执行在run方法里)。

 

2.2、synchronized(*.class)代码块

Ps:原因:因为实例对象不一样,所以可以并发运行,而且数据之间不会相互干扰。【两个线程访问的是两个对象的同步方法】。

 

三、多线程访问同步方法的7种具体情况

 

Ps:【同时访问同步方法与非同步方法】。

Ps:【访问同一个对象的不同的普通同步方法】。

Ps:不同对象针对这种情况,也是可以并发运行的,互不干扰。【同时访问静态synchronized和非静态synchronized方法】【不要看输出,没说服力,自己能理解就好】。

Ps:方法抛异常后,会释放锁。展示不抛出异常前和抛出异常后的对比:一旦抛出了异常,第二个线程会立刻进入同步方法,意味着锁已经释放。【方法抛异常后,会释放锁】

Ps:附加一种情况:sychronized 修饰的方法中调用没有sychronized修饰的方法 A,则 A 为不安全方法,可被多个方法调用哦!

 

四、Synchronized的性质

性质1:可重入

 

可重入:一个线程拿到了锁,这个线程可以再次使用该锁对其他方法,说明该锁是可以重入的。

不可重入:一个线程拿到锁了,如果需要再次使用该锁,必须先释放该锁才能再次获取。

Ps:情况1:证明同一个方法是可重入的

Ps:情况2:证明可重入不要求是同一个方法

Ps:情况3:证明可重入不要求是同一个类中的

总结:可重入锁,粒度是线程范围的,只要在一个线程中,如果需要的是同一把锁,那么就不需要重新释放锁,再获取锁。

 

性质2:不可中段

 

五、深入原理

1、加锁和释放锁的原理

Ps:这里说明了 synchronized 相当于是 lock 的一个封装,所以 lock 会比 synchronized 更细粒度控制。

Ps:反编译素材。

Ps:.class文件进行反编译命令:javap - verbose Demo.class

Ps1:反编译结果看到有两个关键的底层代码:monitorenter & monitorexit来控制加锁和释放锁。

Ps2:Monitorenter:线程第一次给对象加锁的时候,计数变为1。每当这个相同的线程在此对象上再次获得锁时,计数会递增

Ps3:Monitorexit:每当任务离开时,计数递减。当计数为0的时候,锁被完全释放。

 

2、可重入原理:加锁次数计数器

 

3、可见性原理:Java内存模型

Ps:三步骤:

(1)从内存中读取(复制)变量;

(2)修改此变量(副本);

(3)把这个变量副本再写入内容中去替换原先被复制的变量。

 

六、Synchronized的缺陷

Ps:Lock锁可以克服synchronized的缺陷

 

七、常见的面试问题

Ps:锁对象为空案例,因为锁是保存在对象头当中的,如果连对象都没有,那何来对象头。

Ps:死锁案例:

当A线程访问

锁1{

  锁2{

  }

}

当B线程访问

锁2{

  锁1{

  }

}

那么,很容易引发死锁问题。

最后,JAVA专家建议:一般情况下建议util包中的并发类(至少比我们自己的完善),接着使用 synchronized(减少编码,减少错误),如果要用到Lock独有的特性,最后再考虑使用Lock。

 

八、思考题

 

九、总结

 

转载地址:https://lux-sun.blog.csdn.net/article/details/85789201 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Nginx - 安装 & 配置(Linux)
下一篇:IDE - vsftpd 安装 & 配置(Windows)

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年04月28日 07时15分23秒