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. QThead实现模拟VC线程函数

    // 头文件 #ifndef QTHREADBASE_H #define QTHREADBASE_H #include <iostream> #include <stdlib.h&g ...

  2. RKO组——冲刺随笔(1)

    这个作业属于哪个课程 至诚软工实践F班 这个作业要求在哪里 第五次团队作业:项目冲刺 这个作业的目标 记录冲刺计划.要求包括当天会议照片.会议内容以及项目燃尽图(项目进度) 1.昨日进展 小组成员讨论 ...

  3. Bad Request - Invalid Hostname HTTP Error 400. The request hostname is invalid.

    VS 调试 显示 如下错误 解决办法: 1 打开调试运行中的 IIS Express 2 点击其中一条记录 3  点击配置 4 找到对应项目站点 .添加 <binding protocol=&q ...

  4. 各种系统名词解释:MIS 、ERP、CRM、OA

    MIS :信息系统.针对企业使用的软件,都可以叫做MIS系统. (管理信息系统--Management Information System)系统 ,是一个由人.计算机及其他外围设备等组成的能进行信息 ...

  5. iframe页面加载完成为什么还是获取不到里面的dom

    iframe页面加载完成为什么还是获取不到里面的dom? 因为Iframe是跨域,跨域的情况下是无法获取到iframe里面的DOM的,即使iframe加载完成,也无法获取到里面的DOM. 有什么方法获 ...

  6. axios post params(post像get一样使用params传参)

    今天有一个接口是/create?name=***&age=****,但是是一个post请求,我只见过get的这种请求. 找了好久- 解决方法: 发表的axios签名是axios.post(ur ...

  7. 2021.06.29 mac系统下zsh: command not found:***报错问题

    最近老是遇到zsh: command not found:vue,zsh: command not found:nrm, zsh: command not found:tsc, zsh: comman ...

  8. 替代if esle 的高级方法

    if else 是入门最常遇到的一种结构,这种结构简单易懂,深受初学者喜爱.但是 If-Else通常是一个糟糕的选择. 它的可读性差,如果用的太多,会导致结构重构困难.今天我就介绍替代 If-Else ...

  9. windows tips

    u启动经典dos工具实现硬盘分区教程 https://jingyan.baidu.com/article/a3f121e4dbe55afc9052bbfe.html?st=2&net_type ...

  10. Python:logging简要版

    日志等级(从低到高):DEBUG < INFO < WARNING < ERROR < CRITICAL 1.调试时,可记录DEBUG.INFO类的信息 2.投入使用,建议使用 ...