你真的懂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的语法作用域你真的懂了吗 有段时间没有更新了,思绪一下子有点转不过来.正应了一句古话“一天不读书,无人看得出:一周不读书,开始会爆粗:一月不读书,智商输给猪.”.再加上周五晚 ...
随机推荐
- bootstrap栅格系统,排版,代码,表格表单
一.栅格系统 栅格系统用于通过一系列的行(row)与列(column)的组合来创建页面布局. 实例: <!DOCTYPE html> <html lang="zh-CN&q ...
- vector的使用方法
vector是STL容器的可变长度数组.可变长度数组的头文件是<vector>,有以下常见的使用方法: 1.vector<int> v(N,i):建立一个可变长度数组v,内部元 ...
- 利用fread读取二进制文件的bug
最近在做一个项目时需要读取二进制文件,我用C语言的fread进行读取,代码如下: FILE *fp; int read_data; fopen_s(&fp, file_path, " ...
- 手机设置网络代理后,网络不可用之app-jmeter性能测试
1.检查crt证书是否安装 (手机) 2.Jmeter设置HTTP代理端口和ip与手机设置代理参数一致即可
- Android Studio Dolphin 稳定版正式发布
作者 / Yuri Blaise, Product Manager, Android为了帮助开发者们更轻松地打造高质量应用,Android Studio 团队深入调研,为大家带来了最新稳定版 An ...
- Unity检测鼠标是否与UI交互
在Unity项目中,假设在鼠标按键时会触发游戏内的操作,但是在鼠标与UI进行交互时我们希望停止游戏中的操作,这是需要使用EventSystem中的方法来检测鼠标是否正在与UI交互 private bo ...
- angularJS:一个页面多个ng-app
var app = angular.module('myApp', []); app.controller('myCtrl', function($scope, $rootScope) { $scop ...
- RPA的市场需求
最基本的RPA软件机器人定义:机器人通过记录员工在电脑桌面上的操作行为,将业务处理规则和操作行为记录下来,并模拟人的方式在电脑上自动执行一系列特定的工作流程.采用RPA软件机器人解决方案,快速实施,快 ...
- 11.1/2 鼠标显示问题(harib08a)11.2 实现画面外的支持(harib08b)
ps:能力有限,若有错误及纰漏欢迎指正.交流 11.1 鼠标显示问题(harib08a) 存在问题: 在harib07d中鼠标移动到最右侧后就不能再往右移了 解决办法: 将 if (mx > ...
- Z 函数
简单记一下,避免忘记. z 函数 对于字符串 \(S\),我们将 \(z(i)\) 定义为从 \(i\) 开始的后缀与 \(S\) 的最长公共前缀的长度. \(O(n)\) 求出 z 函数 我们添加一 ...