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锁?的更多相关文章

  1. Synchronized锁在Spring事务管理下,为啥还线程不安全?

    前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 大年初二,朋友问了我一个技术的问题(朋友实在是好学, ...

  2. Java线程同步:synchronized锁住的是代码还是对象

    所以我们在用synchronized关键字的时候,能缩小代码段的范围就尽量缩小,能在代码段上加同步就不要再整个方法上加同步.这叫减小锁的粒度,使代码更大程度的并发.原因是基于以上的思想,锁的代码段太长 ...

  3. java并发笔记之证明 synchronized锁 是否真实存在

    警告⚠️:本文耗时很长,先做好心理准备 证明:偏向锁.轻量级锁.重量级锁真实存在 由[java并发笔记之java线程模型]链接: https://www.cnblogs.com/yuhangwang/ ...

  4. 详细了解 synchronized 锁升级过程

    前言 首先,synchronized 是什么?我们需要明确的给个定义--同步锁,没错,它就是把锁. 可以用来干嘛?锁,当然当然是用于线程间的同步,以及保护临界区内的资源.我们知道,锁是个非常笼统的概念 ...

  5. [C#] C# 知识回顾 - 你真的懂异常(Exception)吗?

    你真的懂异常(Exception)吗? 目录 异常介绍 异常的特点 怎样使用异常 处理异常的 try-catch-finally 捕获异常的 Catch 块 释放资源的 Finally 块 一.异常介 ...

  6. synchronized锁重入

    package synLockIn_1; /* synchronized锁重入,当一个线程得到一个对象锁且还未释放锁时,再次请求此对象锁时可以再次得到该对象的锁 * 此例中线程1进入Service类的 ...

  7. Java多线程4:synchronized锁机制

    脏读 一个常见的概念.在多线程中,难免会出现在多个线程中对同一个对象的实例变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实是被更改过 ...

  8. synchronized锁自旋

    http://www.jianshu.com/p/5dbb07c8d5d5 原理 通常说的synchronized在方法或块上加锁,这里的锁就是对象锁(当然也可以在类上面),或者叫重量锁,在JVM中又 ...

  9. 【转】was mutated while being enumerated 你是不是以为你真的懂For...in... ??

    原文网址:http://www.jianshu.com/p/ad80d9443a92 支持原创,如需转载, 请注明出处你是不是以为你真的懂For...in... ??哈哈哈哈, 我也碰到了这个报错 . ...

  10. javascript的语法作用域你真的懂了吗

    原文:javascript的语法作用域你真的懂了吗 有段时间没有更新了,思绪一下子有点转不过来.正应了一句古话“一天不读书,无人看得出:一周不读书,开始会爆粗:一月不读书,智商输给猪.”.再加上周五晚 ...

随机推荐

  1. 3Des加密解密,java c#通用。

    1.需要实现对其他系统的单点登陆,我们实现的方法很简单,就是将当前系统的账号通过加密去获取 需要直接登陆上的系统的token,然后访问需直接登陆的系统就带着token,就相当于登陆了. 2.然后呢,我 ...

  2. 使用Jquery的.css('border')在火狐不兼容

    改成如下就可以兼容火狐.IE.谷歌(border-left-color.border-left-width等)

  3. MySql创建表遇到的问题

    SQL语句如下: CREATE TABLE IF NOT EXISTS `student`{ `id` INT(4) NOT NULL COMMENT '学号', `name` VARCHAR(30) ...

  4. C知识点

    1.变量在内存中所占存储空间的首地址,称为该变量的地址:而变量在存储空间中存放的数据,即变量的值. C语言中,指针就是变量的地址.一个变量的值是另一个变量的地址,且变量类型相同,则称该变量为指针变量. ...

  5. 配置tlpi_hdr.h 头文件《linux系统编程》(转载)

    https://www.cnblogs.com/pluse/p/6296992.html#:~:text=tlpi_hdr.h%E6%96%87%E4%BB%B6%E5%88%99%E5%8C%85% ...

  6. Java 题目集 编程

    7-1 多数组排序 (20 分)   3个整数数组进行整体排序,根据输入的三个数组的元素,输出排序后的结果(从大到小) 输入格式: 第1个数组的长度 第1个数组的各个元素 第2个数组的长度 第2个数组 ...

  7. Serverless 遇到 FinOps: Economical Serverless

    Serverless 遇到 FinOps: Economical Serverless 摘要:本文基于 FunctionGraph 在 Serverless 领域的 FinOps 探索和实践,提出业界 ...

  8. Shell脚本之while read line的用法

    Shell脚本之while read line的用法 while read line do - done < file read通过输入重定向,把file的第一行所有的内容赋值给变量line,循 ...

  9. buildroot交叉编译ros过程中遇到的问题

    问题:Download error on https://pypi.python.org/simple/python-dateutil/:unknown url type:https --Some p ...

  10. Double-Checked Locking 双重检查锁问题

    Code Correctness: Double-Checked Locking Abstract Double-checked locking 是一种不正确的用法,并不能达到预期目标. Explan ...