【245期】面试官:同类中两个方法加同步锁,多个线程支持同时访问这两个方法吗?...
点击上方“Java精选”,选择“设为星标”
别问别人为什么,多问自己凭什么!
下方有惊喜,留言必回,有问必答!
每天 08:15 更新文章,每天进步一点点...
看到这个问题第一反应是不懂,然后查询了网上的一些说法,感觉略有不一。细看之下发现大家对这个问题的分析角度不太一样,就会出现不同的结果,在这里记一下我收集来的不同的角度和自己的例子,感觉这个题目还是蛮有意思的。
首先,同步锁有两种,JVM的synchronized和JDK的ReentrantLock;
然后,多个线程访问这个类的两个方法也有不同的形式,例如访问这个类的两个方法是通过同一个类的实例对象来访问还是通过不同的类的实例对象访问;
再者,一个类的两个方法加了同步锁,这两个被同步方法也没有说明是什么样的方法。他可能是类的普通实例方法,也可能是类中Runnable对象的run方法。
看到这里也许会觉得我对于问题过于的咬文嚼字,但是我想要探讨更多的可能,不同的情形有着不同的结果,而且这些不同的情形能开拓思路,让我们看问题能多个角度,也可以帮我加深多线程的理解。如果本文中有错误或者不恰当的例子,或者代码写的不严谨不规范风格不好,都可以留言提出。
一.synchronized
1.多个线程同时访问同一个类实例对象的两个同步方法:
package synchronizedTest;public class Example1 {private int num = 0 ;(省略getter.setter,后同)public synchronized void method1() {System.out.println("同步方法1进入");for(int i = 0 ; i<10 ; i++) {System.out.print("同步方法1:"+num+"--");num++ ;}System.out.println("同步方法1结束");}public synchronized void method2() {System.out.println("method2进入:");for(int i = 0 ; i<10 ; i++) {System.out.print("method2:"+num+"--");num++ ;}System.out.println("method2结束");}public static void main(String[] args) {final Example1 example1 = new Example1() ;Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {example1.method1();}}) ;Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {example1.method2();}}) ;try {thread2.join();thread1.join(); thread1.start();thread2.start();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} }}
输出结果:
method1进入
同步方法1:0--同步方法1:1--同步方法1:2--同步方法1:3--同步方法1:4--同步方法1:5--同步方法1:6--同步方法1:7--同步方法1:8--同步方法1:9--method1结束
method2进入:
method2:10--method2:11--method2:12--method2:13--method2:14--method2:15--method2:16--method2:17--method2:18--method2:19--method2结束
显然此时多个线程是不能访问同个类(的一个实例对象)的两个同步方法的
推荐下自己做的 Spring boot 的实战项目:
https://gitee.com/yoodb/jing-xuan
2.多个线程同时访问同一个类的不同实例对象的两个同步方法:
将上面的代码稍作修改,主函数中多new一个该类实例
final Example1 example2 = new Example1() ;
再修改thread2的run方法调用的类实例为example2
Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {example2.method2();}}) ;
得到结果:
同步方法1进入
method2进入:
method2:0--method2:1--同步方法1:0--同步方法1:1--同步方法1:2--method2:2--同步方法1:3--method2:3--同步方法1:4--method2:4--同步方法1:5--method2:5--同步方法1:6--同步方法1:7--method2:6--同步方法1:8--同步方法1:9--method2:7--同步方法1结束
method2:8--method2:9--method2结束
这时候显然,多个线程是能访问同个类(的不同实例对象)的两个同步方法的。另外,更多面试题推荐公众号Java精选,回复java面试,获取最新面试题资料,支持在线随时随地刷题。
小结:这是因为synchronized是对象锁,即线程获得的锁是施加在一个实例对象上的,如果不同的线程访问的是同一对象上的不同的同步方法,那么显然不能同时进行。
如果是不同对象上的不同的同步方法,那么就是可以同时进行的。
3.多个线程同时访问同一个类实例对象的两个Runnable对象的run方法:
package synchronizedTest;public class Example2 {private int num ;public Runnable runnable1 = new Runnable() {@Overridepublic void run() {//同步锁synchronized (this) {System.out.println("线程1进入");for(int i = 0 ; i < 10 ; i ++) {System.out.print("线程1:"+num+"--");}System.out.println("线程1结束");}}};public Runnable runnable2 = new Runnable() {@Overridepublic void run() {//同步锁synchronized (this) {System.out.println("thread2进入");for(int i = 0 ; i < 10 ; i ++) {System.out.print("thread2:"+num+"--");}System.out.println("thread2结束");}}};public static void main(String[] args) {Example2 example = new Example2() ; //创建一个对象new Thread(example.runnable1).start(); //同步方法1new Thread(example.runnable2).start(); //同步方法2}
}
输出结果:
thread2进入
线程1进入
thread2:0--线程1:0--线程1:0--thread2:0--线程1:0--线程1:0--线程1:0--thread2:0--线程1:0--thread2:0--thread2:0--线程1:0--thread2:0--线程1:0--thread2:0--thread2:0--线程1:0--thread2:0--线程1:0--thread2:0--线程1结束
thread2结束
可见此时多个线程是能同时访问同个类的两个同步方法的。这是因为
synchronized(this){ //... }中锁住的不是代码块,即这个锁在run方法中,但是并不是同步了这个run方法,而是括号中的对象this,也就是说,多个线程会拿到各自的锁,就能够同时执行run方法。(在run方法前声明synchronized也是同样的效果)
new Thread(example.runnable1).start(); //同步方法1
new Thread(example.runnable2).start(); //同步方法2
打印出这个this对象,是两个不同的类实例对象:
synchronizedTest.Example2$1@65db6dfa
synchronizedTest.Example2$2@471fab
也说明了不同线程的实例对象不同,都是各自对象的锁,不可以认为是类似于例子1中的同一实例对象,而应该类似与例子2的不同类的实例对象。
推荐下自己做的 Spring Cloud 的实战项目:
https://gitee.com/yoodb/jingxuan-springcloud
总结:分析synchronized同步锁的核心在于他是个对象锁,找清楚锁的对象
二.ReentrantLock锁
1.多个线程同时访问同一个类实例对象的两个同步方法:
将例子1的synchronized改为引入ReentrantLock
package ReentrantLockTest;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockExample {private int num;private Lock lock = new ReentrantLock();public void method1() {lock.lock();System.out.println("同步方法1进入");for(int i = 0 ; i<10 ; i++) {System.out.print("同步方法1:"+num+"--");num++ ;}System.out.println("同步方法1结束");lock.unlock();}public void method2() {lock.lock();System.out.println("method2进入:");for (int i = 0; i < 10; i++) {System.out.print("method2:" + num + "--");num++;}System.out.println("method2结束");lock.unlock();}public static void main(String[] args) {final LockExample example = new LockExample() ;Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {example.method1();}}) ;Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {example.method2();}}) ;try {thread2.join();thread1.join();thread1.start();thread2.start();} catch (InterruptedException e) {e.printStackTrace();}}
}
输出结果:
同步方法1进入
同步方法1:0--同步方法1:1--同步方法1:2--同步方法1:3--同步方法1:4--同步方法1:5--同步方法1:6--同步方法1:7--同步方法1:8--同步方法1:9--同步方法1结束
method2进入:
method2:10--method2:11--method2:12--method2:13--method2:14--method2:15--method2:16--method2:17--method2:18--method2:19--method2结束
可见此时多个线程是不能访问同个类(的一个实例对象)的两个同步方法的
2.多个线程同时访问同一个类的不同实例对象的两个同步方法:
修改main函数的即可:
public static void main(String[] args) {final LockExample example1 = new LockExample() ;//两个实例final LockExample example2 = new LockExample() ;Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {example1.method1(); //实例1的同步方法1}}) ;Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {example2.method2();//实例2的同步方法2}}) ;try {thread2.join();thread1.join();thread1.start();thread2.start();} catch (InterruptedException e) {e.printStackTrace();}}
输出结果:
同步方法1进入
method2进入:
同步方法1:0--method2:0--method2:1--同步方法1:1--method2:2--同步方法1:2--同步方法1:3--method2:3--同步方法1:4--method2:4--同步方法1:5--同步方法1:6--method2:5--同步方法1:7--method2:6--同步方法1:8--同步方法1:9--同步方法1结束
method2:7--method2:8--method2:9--method2结束
可见,多个线程是能访问同个类(的不同实例对象)的两个同步方法的。
总结:ReentrantLock和synchronized的前两个例子结论都相同
3.多个线程同时访问同一个类实例对象的两个Runnable对象的run方法:
package ReentrantLockTest;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Lockexample2 {private int num;private Lock lock = new ReentrantLock();public Runnable runnable1 = new Runnable() {@Overridepublic void run() {lock.lock();//上锁System.out.println("线程1进入");for(int i = 0 ; i < 10 ; i ++) {System.out.print("线程1:"+num+"--"); }System.out.println("线程1结束");lock.unlock();}};public Runnable runnable2 = new Runnable() {@Overridepublic void run() {lock.lock();//上锁System.out.println("thread2进入");for(int i = 0 ; i < 10 ; i ++) {System.out.print("thread2:"+num+"--");}System.out.println("thread2结束");lock.unlock();}};public static void main(String[] args) {Lockexample2 example = new Lockexample2();new Thread(example.runnable1).start(); new Thread(example.runnable2).start(); }
}
输出结果:
线程1进入
线程1:0--线程1:0--线程1:0--线程1:0--线程1:0--线程1:0--线程1:0--线程1:0--线程1:0--线程1:0--线程1结束
thread2进入
thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2结束
这里可以看到,与synchronized的第三个例子出现了不同的结果。在这个地方,ReentrantLock不允许多线程同时访问一个类的不同同步方法。
推荐下自己几个月熬夜整理的各个大厂面试资料:
https://gitee.com/yoodb/ebooks
这里要注意的是ReentrantLock与synchronized不同,ReentrantLock的实现方式是要先创建ReentrantLock对象,然后用这个对象的方法来上锁。而一个类的实例中只有一个ReentrantLock对象:
private Lock lock = new ReentrantLock();
而本例中,线程的创建是建立在同一个类实例上的:
Lockexample2 example = new Lockexample2();
new Thread(example.runnable1).start();
new Thread(example.runnable2).start();
因此,ReentrantLock对象lock是同一个,因此第一个线程进入同步方法1后就获取了锁,第二个线程无法获取这个锁,只能等待。
如果换成是两个实例对象:
public static void main(String[] args) {Lockexample2 example = new Lockexample2();Lockexample2 example2 = new Lockexample2();new Thread(example.runnable1).start(); new Thread(example2.runnable2).start(); }
输出结果
线程1进入
thread2进入
线程1:0--线程1:0--线程1:0--线程1:0--thread2:0--线程1:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--线程1:0--thread2:0--线程1:0--线程1:0--thread2:0--线程1:0--thread2结束
线程1:0--线程1结束
可见不同的实例对象中是不同的ReentrantLock对象,因此可以同时访问
小结:ReentrantLock锁的核心在与ReentrantLock对象是不是同一个。
三.结论
重新看看这个问题:一个类中的两个方法都加了同步锁,多个线程能同时访问这个类的两个方法吗?
现在应该比较清楚了,这个问题要分成synchronized和ReentrantLock两个情况:
1、对于synchronized
1)一个类中的两个方法都加了同步锁,多个线程不能同时访问这个类的同一实例对象的两个方法
2)一个类中的两个方法都加了同步锁,多个线程能同时访问这个类的不同实例对象的两个方法
3)一个类中的两个方法**(Runnable的run方法)都加了同步锁,多个线程能**同时访问这个类的两个方法(不论是不是同一实例对象)
2、对于ReentrantLock
1)一个类中的两个方法都加了同步锁,多个线程不能同时访问这个类的同一实例对象的两个方法(不论同步加在实例方法中或是run方法中)
2)一个类中的两个方法都加了同步锁,多个线程能同时访问这个类的不同实例对象的两个方法(不论同步加在实例方法中或是run方法中)
版权声明:本文为CSDN博主「Geepyyyy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
https://blog.csdn.net/weixin_40616523/article/details/87883267
公众号“Java精选”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!
------ THE END ------
精品资料,超赞福利!
>Java精选面试题<
3000+ 道面试题在线刷,最新、最全 Java 面试题!
期往精选 点击标题可跳转
【237期】Java 8 判空新写法
【238期】Java 8 中 Lambda 实现原理及源码剖析!
【239期】面试官问:你觉得 ThreadLocalRandom 这玩意安全吗?
【240期】面试官问:说说基于 Redis 实现延时队列服务?
【241期】面试官问:有了 for 循环 为什么还要 forEach?
【242期】Redis 过滤请求绝技 — 布隆过滤器与布谷鸟过滤器
【243期】面试官问:如何设计 API 返回码(错误码)?
【244期】MySQL 中的反斜杠 \\,太坑了!!
技术交流群!
最近有很多人问,有没有读者&异性交流群,你懂的!想知道如何加入。加入方式很简单,有兴趣的同学,只需要点击下方卡片,回复“加群”,即可免费加入交流群!
文章有帮助的话,在看,转发吧!
【245期】面试官:同类中两个方法加同步锁,多个线程支持同时访问这两个方法吗?...相关推荐
- 求职面试时,如何从面试官话语中揣测是否被录用?
求职面试时,如何从面试官话语中揣测是否被录用? 面试官: 非常感谢您来应聘,我们会尽快联系你,最晚明天下班前给您答复. 基本上可以肯定就是你了,除非遇到特殊情况,HR可能在你没回到家就给你打电话,通知 ...
- 面试官 | SpringBoot 中如何实现异步请求和异步调用?
作者 | 会炼钢的小白龙 来源 | cnblogs.com/baixianlong/p/10661591.html 一.SpringBoot中异步请求的使用 1.异步请求与同步请求 特点: 可以先释放 ...
- 面试官 | Java中的注解是如何工作的?
自Java5.0版本引入注解之后,它就成为了Java平台中非常重要的一部分.开发过程中,我们也时常在应用代码中会看到诸如@Override,@Deprecated这样的注解.这篇文章中,我将向大家讲述 ...
- 惊艳面试官-Java中关于随机数生成8种方式的思考
Java中生成随机数常用的有下面这8种写法:简而言之,名称带安全的未必安全,名字简洁的未必简单. Math.random() Random ThreadLocalRandom SecureRandom ...
- 面试官:不使用synchronized和lock,如何实现一个线程安全的单例?
单例,大家肯定都不陌生,这是Java中很重要的一个设计模式.稍微了解一点单例的朋友也都知道实现单例是要考虑并发问题的,一般情况下,我们都会使用synchronized来保证线程安全. 那么,如果有这样 ...
- 面试官:说一下Synchronized底层实现,锁升级的具体过程?
介绍 这是我去年7,8月份面试的时候被问的一个面试题,说实话被问到这个问题还是很意外的,感觉这个东西没啥用啊,直到后面被问了一波new Object,Integer对象等作为加锁对象行吗?会出现哪些问 ...
- 在c#中用mutex类实现线程的互斥_面试官经常问的synchronized实现原理和锁升级过程,你真的了解吗...
本篇文章主要从字节码和JVM底层来分析synchronized实现原理和锁升级过程,其中涉及到了简单认识字节码.对象内部结构以及ObjectMonitor等知识点. 阅读本文之前,如果大家对synch ...
- 已定义了两个整数变量A和B,完成下列功能: (1)若两个数中有一个是奇数,则将奇数存入A中,偶数存入B中(2)若两个数均为奇数,则将两数均加1后存入原变量(3)若两个数均为偶数,则两个变量均不改变。
已定义了两个整数变量A和B,试编写程序完成下列功能: (1)若两个数中有一个是奇数,则将奇数存入A中,偶数存入B中: (2)若两个数均为奇数,则将两数均加1后存入原变量: (3)若两个数均为偶数,则两 ...
- 同步锁 php,python线程中同步锁详解
这篇文章主要为大家详细介绍了python线程中同步锁的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 在使用多线程的应用下,如何保证线程安全,以及线程之间的同步,或者访问共享变量等问题是十 ...
最新文章
- Twitter团队最新研究:快速高效的可扩展图神经网络SIGN
- ffmpeg3.4 yuv编码为h264
- DNS and Bind (一)
- UVA11624 Fire!
- U3D的飞船太空射击例子中,使用coroutine
- android替换电话应用,android – 替换调用应用程序
- 工业级交换机的功率和管理功能详解
- hibernate sql 执行两次_使用 Hibernate 和 MySQL 需要知道的五件事
- JQuery-UI dialog hide属性的取值范围
- iOS开发中的火星坐标系及各种坐标系转换算法
- 无机金属专业里有计算机课吗,无机非金属材料工程专业课程有不少
- MVC 中 Html.RenderPartial()与Html.Partial()区别
- 4*4行列式矩阵键盘
- My97DateTimePicker使用说明
- 行人跟踪之身份识别(一)
- java调用百度地图的不同显示方式
- 使用 Microsoft Symbol Server 获取调试符号文件
- linux 开放连续端口,CentOS/Linux 开放80、8080端口或者开放某个端口
- 使用burp进行网站爆破
- JavaScript键盘事件常见用法实例分析