你真的懂synchronized锁?
1. 前言
synchronized在我们的程序中非常的常见,主要是为了解决多个线程抢占同一个资源。那么我们知道synchronized有多种用法,以下从实践出发,题目由简入深,看你能答对几道题目?
2. 问题
调用代码如下
public static void main(String[] args) throws Exception {
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1024));
SyncTest st = new SyncTest();
// 注意是不同方法
executor.submit(() -> st.sync1_1());
executor.submit(() -> st.sync1_2());
executor.shutdown();
}
问题2.1
锁lock全局对象,会输出什么?
public static final Object LOCK = new Object();
public void sync1_1() {
// 锁住lock对象
synchronized (LOCK) {
sleep("sync1_1");
}
}
public void sync1_2() {
// 锁住lock对象
synchronized (LOCK) {
sleep("sync1_2");
}
}
public static void sleep(String name) {
try {
log.info(name + " get lock");
TimeUnit.SECONDS.sleep(1);
log.info(name + " release lock");
} catch (Exception e) {}
}
点击查看答案(请思考后在点击查看)
[INFO 2023-04-14 15:12:46.639] [pool-2-thread-1] [] - [SyncTest.java.sleep:86] [sync1_1 get lock]
[INFO 2023-04-14 15:12:47.652] [pool-2-thread-1] [] - [SyncTest.java.sleep:88] [sync1_1 release lock]
[INFO 2023-04-14 15:12:47.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:86] [sync1_2 get lock]
[INFO 2023-04-14 15:12:48.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:88] [sync1_2 release lock]
等待线程A执行完成后,线程B才能执行,否则阻塞。符合我们预期。锁的资源就是我们所谓的LOCK对象
问题2.2
锁this对象,会输出什么?this是代表什么?
public void sync2_1() {
synchronized (this) {
sleep("sync2_1");
}
}
public void sync2_2() {
synchronized (this) {
sleep("sync2_2");
}
}
点击查看答案(请思考后在点击查看)
[INFO 2023-04-14 15:12:46.639] [pool-2-thread-1] [] - [SyncTest.java.sleep:86] [sync2_1 get lock]
[INFO 2023-04-14 15:12:47.652] [pool-2-thread-1] [] - [SyncTest.java.sleep:88] [sync2_1 release lock]
[INFO 2023-04-14 15:12:47.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:86] [sync2_2 get lock]
[INFO 2023-04-14 15:12:48.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:88] [sync2_2 release lock]
等待线程A执行完成后,线程B才能执行,否则阻塞。符合我们预期。锁的是调用方,SyncTest st = new SyncTest(); 中的st对象。 st是SyncTest类的一个对象。也就是锁的这个this资源
问题2.3
锁方法,会输出什么? 锁的又是什么资源?
public synchronized void sync3_1() {
sleep("sync3_1");
}
public synchronized void sync3_2() {
sleep("sync3_2");
}
点击查看答案(请思考后在点击查看)
结论同 问题2.2
问题2.4
锁static方法,会输出什么? 锁的又是什么资源?
public static synchronized void sync4_1() {
sleep("sync4_1");
}
public static synchronized void sync4_2() {
sleep("sync4_2");
}
点击查看答案(请思考后在点击查看)
[INFO 2023-04-14 15:12:46.639] [pool-2-thread-1] [] - [SyncTest.java.sleep:86] [sync4_1 get lock]
[INFO 2023-04-14 15:12:47.652] [pool-2-thread-1] [] - [SyncTest.java.sleep:88] [sync4_1 release lock]
[INFO 2023-04-14 15:12:47.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:86] [sync4_2 get lock]
[INFO 2023-04-14 15:12:48.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:88] [sync4_2 release lock]
等待线程A执行完成后,线程B才能执行,否则阻塞。符合我们预期。因为是在static上面加锁,而static方法即是类方法,因此他锁的是这个类,也就是对SyncTest这个this class加的锁
问题2.5
锁类的class,会输出什么? 锁的又是什么资源?
public void sync5_1() {
synchronized (SyncTest.class) {
sleep("sync5_1");
}
}
public void sync5_2() {
synchronized (SyncTest.class) {
sleep("sync5_2");
}
}
点击查看答案(请思考后在点击查看)
结论同问题2.4
3. 问题(修改调用方式)
其他全部代码不改变,仅修改调用方式。具体代码如下
public static void main(String[] args) throws Exception {
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1024));
// 这个是new了一个st1
SyncTest st1 = new SyncTest();
executor.submit(() -> st1.sync1_1());
// 这里new了一个st2
SyncTest st2 = new SyncTest();
executor.submit(() -> st2.sync1_2());
executor.shutdown();
}
其他全部不变,问题从2.1 - 2.5重新全部调用一遍。结果又是否相同。
注意一下:调用方为 new 了两个st1以及st2, 如果你完全理解上述问题,这个问题就非常简单了。我们以问题1以及问题2为例:
问题2.1
[INFO 2023-04-14 15:12:46.639] [pool-2-thread-1] [] - [SyncTest.java.sleep:86] [sync1_1 get lock]
[INFO 2023-04-14 15:12:47.652] [pool-2-thread-1] [] - [SyncTest.java.sleep:88] [sync1_1 release lock]
[INFO 2023-04-14 15:12:47.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:86] [sync1_2 get lock]
[INFO 2023-04-14 15:12:48.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:88] [sync1_2 release lock]
这是由于无论new几个st,锁的永远是唯一资源lock,因此结论不变
问题2.2
[INFO 2023-04-14 15:26:31.676] [pool-2-thread-2] [] - [SyncTest.java.sleep:86] [sync2_1 get lock]
[INFO 2023-04-14 15:26:31.676] [pool-2-thread-1] [] - [SyncTest.java.sleep:86] [sync2_1 get lock]
[INFO 2023-04-14 15:26:32.688] [pool-2-thread-1] [] - [SyncTest.java.sleep:88] [sync2_1 release lock]
[INFO 2023-04-14 15:26:32.688] [pool-2-thread-2] [] - [SyncTest.java.sleep:88] [sync2_1 release lock]
这个结果就非常有意思了。注意看,线程B并没有阻塞,而是直接获取到了这个资源。也就是synchronized失效了。我们来分析一下为什么synchronized失效了?
我们知道在问题2中,synchronized锁的是this对象,而这个this对象分别为st1, 以及st2。那么是不是就是两个资源了。如果是两个资源,就不存在互斥的作用了,也就是不会相互争夺资源。
请各位读者自己分析问题2.3 - 2.5的第二种代码调用方式的结果。
4. 结论
synchronized是我们工程中常用的一个方法。但对于其用法,如果深究,还是有非常多意外的惊喜。如果小伙伴有其他问题,随时欢迎交流讨论
你真的懂synchronized锁?的更多相关文章
- Synchronized锁在Spring事务管理下,为啥还线程不安全?
前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 大年初二,朋友问了我一个技术的问题(朋友实在是好学, ...
- Java线程同步:synchronized锁住的是代码还是对象
所以我们在用synchronized关键字的时候,能缩小代码段的范围就尽量缩小,能在代码段上加同步就不要再整个方法上加同步.这叫减小锁的粒度,使代码更大程度的并发.原因是基于以上的思想,锁的代码段太长 ...
- java并发笔记之证明 synchronized锁 是否真实存在
警告⚠️:本文耗时很长,先做好心理准备 证明:偏向锁.轻量级锁.重量级锁真实存在 由[java并发笔记之java线程模型]链接: https://www.cnblogs.com/yuhangwang/ ...
- 详细了解 synchronized 锁升级过程
前言 首先,synchronized 是什么?我们需要明确的给个定义--同步锁,没错,它就是把锁. 可以用来干嘛?锁,当然当然是用于线程间的同步,以及保护临界区内的资源.我们知道,锁是个非常笼统的概念 ...
- [C#] C# 知识回顾 - 你真的懂异常(Exception)吗?
你真的懂异常(Exception)吗? 目录 异常介绍 异常的特点 怎样使用异常 处理异常的 try-catch-finally 捕获异常的 Catch 块 释放资源的 Finally 块 一.异常介 ...
- synchronized锁重入
package synLockIn_1; /* synchronized锁重入,当一个线程得到一个对象锁且还未释放锁时,再次请求此对象锁时可以再次得到该对象的锁 * 此例中线程1进入Service类的 ...
- Java多线程4:synchronized锁机制
脏读 一个常见的概念.在多线程中,难免会出现在多个线程中对同一个对象的实例变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实是被更改过 ...
- synchronized锁自旋
http://www.jianshu.com/p/5dbb07c8d5d5 原理 通常说的synchronized在方法或块上加锁,这里的锁就是对象锁(当然也可以在类上面),或者叫重量锁,在JVM中又 ...
- 【转】was mutated while being enumerated 你是不是以为你真的懂For...in... ??
原文网址:http://www.jianshu.com/p/ad80d9443a92 支持原创,如需转载, 请注明出处你是不是以为你真的懂For...in... ??哈哈哈哈, 我也碰到了这个报错 . ...
- javascript的语法作用域你真的懂了吗
原文:javascript的语法作用域你真的懂了吗 有段时间没有更新了,思绪一下子有点转不过来.正应了一句古话“一天不读书,无人看得出:一周不读书,开始会爆粗:一月不读书,智商输给猪.”.再加上周五晚 ...
随机推荐
- NOIP2009普及组
T3]细胞分裂 [算法]数论 [题解]均分的本质是A整除B,A整除B等价于A的质因数是B的子集. 1.将m1分解质因数,即m1=p1^a1*p2^a2*...*pk^ak 所以M=m1^m2=p1^( ...
- cf823div2C
cf823div2C 题目链接 题目 给你两个字符串\(s_1,s_2\).每次操作可以让\(s_1\)的前k个和\(s_2\)的后k个交换.询问是否可以通过多次上述操作,使得\(s_1=s_2\). ...
- oracle从1到10生成顺序号
oracle从1到10生成顺序号,脚本案例如下: select 'ABAB'|| lpad(level,5,0) as serial_no from dual connect by level< ...
- mysqld_safe Directory '/tmp/mysql' for UNIX socket file don't exists.
报错版本:mysql-5.7.35 1.报错完整提示信息: [root@localhost bin]# 2022-11-15T04:04:43.122905Z mysqld_safe Logging ...
- java 通过反射以及MethodHandle执行泛型参数的静态方法
开发过程中遇到一个不能直接调用泛型工具类的方法,因此需要通过反射来摆脱直接依赖. 被调用静态方法示例 public class test{ public static <T> T get( ...
- MFS分布式存储特性及组件说明
1.MFS MooseFS是一个具有冗余容错功能的分布式网络文件系统,它将数据分别存放在多个物理服务器或单独磁盘或分区上,确保一份数据有多个备份副本,然而对于访问MFS的客户端或者用户来说,整个分布式 ...
- 两步解决macbook电池不充电
问题描述: 1.电源适配器是冷的,判断并没有充电,更换拔插笔记本的不同TypeC插口问题依然.(怀疑适配器坏了,但心想Apple质量一个适配器不至于那么不抗用) 2.偶尔能开起来机,则显示电源3%,瞬 ...
- Repeater 绑定数据是根据数据修改行的颜色值信息
<ItemTemplate> <tr <%# Eval("dayu20").ToString()=="0"? "style=' ...
- 关于Appium执行用例过程中问题处理办法
关于Appium执行用例过程中问题处理办法 1. 运行环境 1.1 windows10 64位系统 1.2 华为荣耀V10 Android 9 1.3 appium-desktop 1 ...
- 快速掌握Linux三剑客命令使用
前言 Linux三剑客指的是grep.sed以及awk命令的使用,这三个命令功能异常强大,大到没朋友.grep命令主打"查找",sed命令主打"编辑",awk命 ...