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. vue移动端购物商场首页制作

    1.搭建项目框架 新建首页主组件及其子组件并将子组件展示出来 2.封装所需接口 3.编写轮播图组件 <template> <div id="swipercom"& ...

  2. Python+unittest+requests 接口自动化测试框架搭建 完整的框架搭建过程 实战

    一.Python+unittest+requests+HTMLTestRunner 完整的接口自动化测试框架搭建_00--框架结构简解 首先配置好开发环境,下载安装Python并下载安装pycharm ...

  3. Stram流 - 随笔

    函数式编程 注重函数 - 关注对数据进行了什么操作 流 中间操作 去重 authors.stream() .distinct() ; 查询指定匹配 .filter(new Predicate<A ...

  4. unittest框架基本使用

    1.简介 unittest是python内置的单元测试框架,具备编写用例.组织用例.执行用例.输出报告等自动化框架的条件.使用unittest前需要了解该框架的五个概念: 即test case,tes ...

  5. HTTP 协议相关

    一. HTTP常见请求头 1. Host (主机和端口号) 2. Connection (连接类型) 3.Upgrade-Insecure-Requests (升级为HTTPS请求) 4. User- ...

  6. python-实现栈结构

    # encoding=utf-8 class Stack(object): """栈""" def __init__(self): &quo ...

  7. android装包

    一.找到对应包体apk 二.数据线连接电脑及手机,弹出USB连接选项并选择传输文件 注:如果未弹出USB连接选项可尝试换根数据线解决 三.点击我的电脑找到本机设备 四.将对应包体文件拖入本机设备 五. ...

  8. PYinstall打包程序出现编码错误的解决 'utf-8' codec can't decode byte 0xce in position 171: invalid continuation b

    网上说,先执行,再打包 chcp 65001 试过没有用. 解决方案: 把import的包批量注释,然后寻找是import那个文件导致. 虽然注释会导致程序运行出错,但是打包才不管你能不能运行. 最后 ...

  9. 截取屏幕 转为GIF 图片

    近期winform 做的一个截取屏幕的软件给大家!谁要留言给我哦! sss

  10. Java-10接口与抽象类

    Java-10接口与抽象类 抽象方法 abstract method机制 这是一个不完整的方法,它只有一个声明,没有方法体 abstract void f(); 包含抽象方法的类被称为抽象类:如果一个 ...