Java高并发关于synchronized的8锁讲解
先了解一个概念:synchronized 锁的是这个方法所在的资源类,就是这个对象,也就是同一时间段不可能有两个线程同时进到这个资源类,同一时间段,只允许有一个线程访问资源类里面的其中一个synchronized 方法!
T1:标准访问,请问先打印邮件还是短信?
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public synchronized void sendEmail() throws Exception {
System.out.println("----sendEmail");
}
public synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
}
结果:虽然先执行的sendEmail,但其实是不一定的,看cpu的调度,被 synchronized 修饰的方式,锁的对象是方法的调用者,因为这两个方法锁的是同一个对象,所以先调用的先执行。
T2:邮件方法暂停4s,请问先打印邮件还是短信?
代码如下:
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public synchronized void sendEmail() throws Exception {
Thread.sleep(4000);
System.out.println("----sendEmail");
}
public synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
}
结果:虽然先执行的(等待4s)sendEmail,然后接着sendSMS,但其实是不一定的,看cpu的调度,被 synchronized 修饰的方式,锁的对象是方法的调用者,因为这两个方法锁的是同一个对象,所以先调用的先执行,即先等待4s后sendEmail出现,接着sendSMS。相当于王小胖要拿手机发邮件,王二胖要拿手机发短息,王小胖先抢到手机了,它发邮件要4s的时间,那么就要等王小胖花4s发完邮件,王二胖才能发短信。
综合T1,T2,结论如下:
一个对象里面如果有多个synchronized 方法,某一时刻内,只要有一个线程去调用synchronized 方法了,那么其他的线程只能够等待,换句话说,某一时刻只能够有一个线程去访问这些synchronized方法,锁的是当前对象this,被锁定后,其他对象都不能进入到当前对象的其他synchronized 方法。
T3:新增一个普通方法hello(),请问先打印邮件还是hello?
代码如下:
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
// phone.sendSMS();
phone.hello();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public synchronized void sendEmail() throws Exception {
Thread.sleep(4000);
System.out.println("----sendEmail");
}
public synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
public void hello() {
System.out.println("----hello");
}
}
结果:结果是先打印hello,其次4s后再sendEmail,因为hello没有被synchronized 修饰。
结论:普通方法和同步锁无关。
T4:两部手机(两个资源实体),请问先打印邮件还是短信?
代码如下:
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
// phone.sendSMS();
// phone.hello();
phone1.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public synchronized void sendEmail() throws Exception {
Thread.sleep(4000);
System.out.println("----sendEmail");
}
public synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
public void hello() {
System.out.println("----hello");
}
}
结果:先打印的phone1的sendSMS,4s后再打印phone的sendemail,因为资源对象是不同的。
结论:两个对象,就不是同一把锁了。
T5:两个静态同步方法,同一部手机,请问先打印邮件还是短信?
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
phone.sendSMS();
// phone.hello();
// phone1.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public static synchronized void sendEmail() throws Exception {
Thread.sleep(4000);
System.out.println("----sendEmail");
}
public static synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
public void hello() {
System.out.println("----hello");
}
}
结果:4s过后先打印sendEmail,接着打印sendSMS。
T6:由上一题的一部手机换成两部手机,请问先打印邮件还是短信?
代码如下:
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
// phone.sendSMS();
// phone.hello();
phone1.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public static synchronized void sendEmail() throws Exception {
Thread.sleep(4000);
System.out.println("----sendEmail");
}
public static synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
public void hello() {
System.out.println("----hello");
}
}
结果:4s过后先打印sendEmail,接着打印sendSMS。
综合T5,T6:
结论:此时锁的不是对象了,而是类!即锁的是phone.class!锁的是模板,Phone phone=new Phone() 锁的就是这个Phone。
因为phone和phone1都是Phone的实例对象,而锁的是Phone,所以说,当phone正在执行的时候,phone1就得等着,因为锁的是模板。
T7:1个普通同步方法,1个静态同步方法,1部手机,请问先打印邮件还是短信?
代码如下:
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
// Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
phone.sendSMS();
// phone.hello();
// phone1.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public static synchronized void sendEmail() throws Exception {
Thread.sleep(4000);
System.out.println("----sendEmail");
}
public synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
public void hello() {
System.out.println("----hello");
}
}
结果:先打印的sendSMS,接着4s后打印的sendEmail。原因:因为sendEmail锁的是模板的(加了static),sendSMS锁的是类的。
锁模板与锁类两者是不冲突的。由于sendEmail睡了4s所以先打印sendSMS.
T8:1个普通同步方法,1个静态同步方法,2部手机,请问先打印邮件还是短信?
代码如下:
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
phone1.sendSMS();
// phone.hello();
// phone1.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public static synchronized void sendEmail() throws Exception {
Thread.sleep(4000);
System.out.println("----sendEmail");
}
public synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
public void hello() {
System.out.println("----hello");
}
}
结果:同T7一样,先打印的sendSMS,接着4s后打印的sendEmail。原因:因为sendEmail锁的是模板的(加了static),sendSMS锁的是模板的实例对象的,而且操作的是不同的对象,两者是不冲突的。
8种加锁情景总结:
synchronized实现同步基础:java中的每一个对象都可以作为锁。当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。
synchronized的具体的表现形式有下面的三种:
1:对于普通方法而言public static synchronized void method(){...},锁是当前实例对象。
也就是说当一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁。
如果别的实例对象的非静态同步方法和和该实例对象的非静态同步方法用的是不同的锁,那么就不用等待该实例对象已获取的锁的非静态同步方法释放锁,直接获取他们自己的锁。
2:对于静态同步方法public static synchronized void method(){...},锁是当前类的class对象。
对象锁和类锁(this/class)是两个不同的对象,所以静态同步方法和非静态同步方法之间是不会有竞争条件的。但一旦静态方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁。
3:对于同步方法块而言用 synchronized(object){代码内容},锁是synchronized括号里配置的对象。
Java高并发关于synchronized的8锁讲解的更多相关文章
- java高并发核心要点|系列2|锁的底层实现原理
上篇文章,我们主要讲了解决多线程之间共享数据的核心问题和解决方案,也讲了锁的简单分类. 那么,这把锁,我们应该怎么去实现呢?如果你是java语言设计者,你又会怎么去设计这个线程锁呢? 直觉告诉我们,我 ...
- java高并发核心要点|系列3|锁的底层实现原理|ABA问题
继续讲CAS算法,上篇文章我们知道,CAS算法底层实现,是通过CPU的原子指令来实现. 那么这里又有一个情景: 话说,有一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且 ...
- 【实战Java高并发程序设计 2】无锁的对象引用:AtomicReference
AtomicReference和AtomicInteger非常类似,不同之处就在于AtomicInteger是对整数的封装,而AtomicReference则对应普通的对象引用.也就是它可以保证你在修 ...
- 【实战Java高并发程序设计6】挑战无锁算法:无锁的Vector实现
[实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...
- 【实战Java高并发程序设计 7】让线程之间互相帮助--SynchronousQueue的实现
[实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...
- 【实战Java高并发程序设计 5】让普通变量也享受原子操作
[实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...
- 【实战Java高并发程序设计 3】带有时间戳的对象引用:AtomicStampedReference
[实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference AtomicReference无法解决上述问题的根 ...
- java高并发核心要点|系列文章
java高并发核心要点|系列1|开篇 java高并发核心要点|系列2|锁的底层实现原理 java高并发核心要点|系列3|锁的底层实现原理|ABA问题 java高并发核心要点|系列4|CPU内存指令重排 ...
- java高并发锁的3种实现
初级技巧 - 乐观锁 乐观锁适合这样的场景:读不会冲突,写会冲突.同时读的频率远大于写. 以下面的代码为例,悲观锁的实现: Java代码 public Object get(Object key) ...
- 构建高性能服务(二)java高并发锁的3种实现
构建高性能服务(二)java高并发锁的3种实现 来源:http://www.xymyeah.com/?p=46 提高系统并发吞吐能力是构建高性能服务的重点和难点.通常review代码时看到sync ...
随机推荐
- multi-GPU环境下的batch normalization需要特殊实现吗?
3年前曾经写过关于分布式环境下batch normalization是否需要特殊实现的讨论:batch normalization的multi-GPU版本该怎么实现? [Tensorflow 分布式P ...
- Centos7安装mysql8.0教程
1.背景 centos7安装mysql8.0 2.安装步骤 步骤一:安装准备工作 1.查看是否有安装过mysql rpm -qa | grep -i mysql 2.删除mysql yum -y re ...
- nginx实战教程
大纲 为了让大家更快的学会,该博客中的内容录制成了视频课程:马上在线学习 1.什么是nginx Nginx是一款高性能的http 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器. 由 ...
- Pytorch学习率更新
如需了解示例完整代码及其后续内容请访问: https://www.emperinter.info/2020/08/01/learning-rate-in-pytorch/ 缘由 自己在尝试了官方的代码 ...
- PHP 字符串大小写操作
PHP为我们提供了字符串中大小写字母转换的函数, strtoupper()将指定的字符全部转换为大写: strtolower()将北定的字符都转换成小写: ucwords()将指定字符串中每个单词的首 ...
- IP一致性论文
IP一致性:指的是给定输入的图像,要求保持图像中的ID不变,IP可能是Identity Property,要求能够识别出是同一个身份. 目前通过IP的一致性技术,可以用于短视频短剧上,是一个新兴的市场 ...
- 【牛客刷题】HJ6 质数因子
题目链接 这道题本身更多的是考察如何计算一个数的质数因子,更像是一道数学题,用到了循环的方法: package main import ( "fmt" "math&quo ...
- Win32 状态栏用法
WIN32 状态控件用法 1.创建控件 状态栏类名: STATUSCLASSNAME #define STATUSCLASSNAMEW L"msctls_statusbar32 ...
- 记一个文件过大导致git失败的修复方法
原因: 测试文件流时候弄了个安装包进去,结果太大了 解决方法: 参考蓝色行,先取到故障的文件名 然后 git filter-branch --tree-filter 'rm -f 文件名' --tag ...
- iPhone 打不开 Apple News 解决方法
想看 Apple News,但是在主屏幕找不到,在 App Store 搜索 Apple News 后打开时显示访问控制已启用,然而在设置中检查发现访问控制并没有启用. 经过一番摸索,发现这个访问控制 ...