synchronized不能锁静态变量_面试官:请说一下对象锁和类锁的区别

463a878ce7d2c85264e0f2cfb4c43a11.png

有锁才有自由

生活中不存在绝对的自由,绝对的自由通常对应的无序和混沌,只有在道德、法律、伦理的约束下的相对自由,才能使人感受到自由。

而在多线程编程中,锁是至关重要的,锁就是道德,就是法律约束,没有锁的多线程环境将会是混乱的,所有线程都在争夺资源,最后的结果就是导致系统崩溃,而有了锁之后,多线程环境才能稳定高效的工作。

synchronized 关键字

synchronized 是我们所说的重量级锁,所说的重量级是相对于那些自旋锁(AQS)而言的,比如可重入锁ReentrantLock。很多人谈 synchronized 色变,说它性能差、太重,貌似言之凿凿。放在多年前确实如此,但是 Java 1.7、1.8 已经对 synchronized 做了很大优化,其性能和那些轻量级锁几乎没有差距。

所以,我们再程序中其实可以放心的使用它,即使没有用过,也肯定在一些源码里见过,比如 Netty 中就有很多地方用到了它。

下面开始进入今天的主题,类锁和实例锁。看名字就已经很明显了,类锁就是所在类上的锁,实例就是锁在类实例上的锁。

实例锁

类声明后,我们可以 new 出来很多的实例对象。这时候,每个实例在 JVM 中都有自己的引用地址和堆内存空间,这时候,我们就认为这些实例都是独立的个体,很显然,在实例上加的锁和其他的实例就没有关系,互不影响了。

通常我们使用实例锁的方式有下面三种:

1、 锁住实体里的非静态变量

非静态变量是实例自身变量,不会与其他实例共享,所以锁住实体内声明的非静态变量可以实现对象锁。锁住同一个变量的方法块共享同一把锁。

dc2f2cb34bd8cef8acf1739f88a82267.png

2、锁住 this 对象

this 指的是当前对象实例本身,所以,所有使用 synchronized(this)方式的方法都共享同一把锁。

aae5f75fcd40204667821f63d372bc0c.png

3、直接锁非静态方法

最简单、最直观的一种方式,直接加在方法返回类型前。

ce3766d16c1321df96e3ed38924f7094.png
使用对象锁的情况,只有使用同一实例的线程才会受锁的影响,多个实例调用同一方法也不会受影响。

下面来做个测试,开启 5 个线程,每个线程都 new 一个新的实例来分别调用上面三种方式的方法,方法完成的动作就是输出线程名称,然后休眠 10 秒钟。

public class ObjectLock {
​private Object lock = new Object();
​/*** 锁住非静态变量* @throws InterruptedException*/public void lockObjectField() throws InterruptedException{synchronized (lock){System.out.println(Thread.currentThread().getName());Thread.sleep(10*1000);}}
​/*** 锁住 this 对象 this 就是当前对象实例* @throws InterruptedException*/public void lockThis() throws InterruptedException{synchronized (this){System.out.println(Thread.currentThread().getName());Thread.sleep(10*1000);}}
​/*** 直接锁住非静态方法* @throws InterruptedException*/public synchronized void methodLock() throws InterruptedException{System.out.println(Thread.currentThread().getName());Thread.sleep(10*1000);}
​public static void main(String[] args){for (int i = 0; i < 5; i++) {Thread worker = new Thread(new ObjectLockWorker());worker.setName("kite-" + i);worker.start();}}
​public static class ObjectLockWorker implements Runnable{@Overridepublic void run() {try {ObjectLock objectLock = new ObjectLock();// 方式 1objectLock.lockObjectField();// 方式 2//objectLock.lockThis();// 方式 3//objectLock.methodLock();} catch (Exception e) {e.printStackTrace();}}}
}

我们预测的结果就是每个线程都会立刻输出线程名称,然后各自休眠 10 秒

分别调用方式1、2、3,效果都是一样的,我们看到输出结果和我们预测的是一样的,5 个线程都立即输出线程名,然后等待 10 秒,整个程序退出。

类锁

类锁是加载类上的,而类信息是存在 JVM 方法区的,并且整个 JVM 只有一份,方法区又是所有线程共享的,所以类锁是所有线程共享的。

使用类锁的方式有如下方式:

1、锁住类中的静态变量

因为静态变量和类信息一样也是存在方法区的并且整个 JVM 只有一份,所以加在静态变量上可以达到类锁的目的。

19269c7f4b7025bd3ce028cac14f2a3d.png

2、直接在静态方法上加 synchronized

因为静态方法同样也是存在方法区的并且整个 JVM 只有一份,所以加在静态方法上可以达到类锁的目的。

3d6b261daf234e5e008dffa7e097564d.png

3、锁住 xxx.class

对当前类的 .class 属性加锁,可以实现类锁。

3da33b680371483b426f1cd31ec87fd9.png
类锁是所有线程共享的锁,所以同一时刻,只能有一个线程使用加了锁的方法或方法体,不管是不是同一个实例。

下面同样来做个测试,开启 5 个线程,除了调用静态方法的方式,其他两种方式中每个线程都 new 一个新的实例来分别调用,方法内完成的动作就是输出线程名称,然后休眠 10 秒钟。

public class ClassLock {
​private static Object lock = new Object();
​/*** 锁住静态变量* @throws InterruptedException*/public void lockStaticObjectField() throws InterruptedException{synchronized (lock){System.out.println(Thread.currentThread().getName());Thread.sleep(10*1000);}}
​/*** 锁住静态方法* @throws InterruptedException*/public static synchronized void methodLock() throws InterruptedException{System.out.println(Thread.currentThread().getName());Thread.sleep(10*1000);}
​/*** 锁住 xxx.class* @throws InterruptedException*/public void lockClass() throws InterruptedException{synchronized (ClassLock.class){System.out.println(Thread.currentThread().getName());Thread.sleep(10*1000);}}
​public static void main(String[] args){for (int i = 0; i < 5; i++) {Thread worker = new Thread(new ClassLockWorker());worker.setName("kite-" + i);worker.start();}}
​public static class ClassLockWorker implements Runnable{@Overridepublic void run() {try {ClassLock classLock = new ClassLock();// 方式 1classLock.lockStaticObjectField();// 方式 2//ClassLock.methodLock();// 方式 3//classLock.lockClass();} catch (Exception e) {e.printStackTrace();}}}
}

我们预测的结果就是刚开始只有1个线程抢到锁,然后输出线程名,之后等待 10 秒中,之后是下一个抢到锁的线程,输出线程名,然后等待 10 秒。直到最后一个抢到锁的线程,整个过程历时大约 50 秒。

分别调用方式1、2、3,观察执行结果,和我们预测的是一致的。

怎么样,面试再考你对象锁和类锁的时候,是不是能从容一点儿了呢。

你还可以读:

Java 中的几种线程池不知道你用对了没有​mp.weixin.qq.com
06e7ca15c424a77a97836faeee664480.png
从 volatile 说起,可见性和有序性是什么​mp.weixin.qq.com
cc46ee437ba22c0b6f98cbbfc88cc711.png
我们说的 CAS 自旋锁是什么​mp.weixin.qq.com
661ad61838f971c1475b0e2156e47138.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/501392.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机主板开机接线端子,装机必看,机箱前置面板接线向导,不怕再接错线了...

对于新手来说&#xff0c;在自己DIY一台电脑整机的时候&#xff0c;肯定是既兴奋又恐慌。兴奋是自己总算能亲自动手&#xff0c;组装一台自己称心如意且个性化十足的台式机。让大家感到恐慌的是主板那么多针脚和接口&#xff0c;该从何处下手&#xff0c;对于不懂行不专业的人来…

atoi函数_每日干货丨C语言中的字符串处理库函数介绍与实现

strlen函数&#xff1a;求字符串的长度size_t strlen(const char *s)头文件:#include说明:求出s指向的字符串的长度(不包括null字符)。返回值:返回s指向的字符串的长度。size_t strlen(const char *s){size_t len 0;while(*s) len;return len;}strcpy和strncpy函数&…

从windows计算机中卸载office,如何彻底卸载电脑中的Office组件|Office办公软件卸载不干净怎么办...

‍有不少用户遇到原本在电脑中的软件卸载不干净&#xff0c;而无法重新安装软件的问题。比如说有位用户在安装Office办公软件时就是因为没有彻底卸载原先电脑中的软件才导致无法安装。接下来系统城小编就和大家分享一下彻底卸载电脑中的Office组件的方法。1、打开电脑自带的控制…

python调用函数怎么表示_Python---7函数(调用定义函数)

函数 Python内置了很多有用的函数&#xff0c;我们可以直接调用。 要调用一个函数&#xff0c;需要知道函数的名称和参数&#xff0c;比如求绝对值的函数abs&#xff08;&#xff09;&#xff0c;只有一个参数。可以直接从Python的官方网站查看文档&#xff1a;也可以在交互式命…

解决Android studio 的Glide 4.1.0第三方jar包如何导入libs目录

很多人在让自己的安卓实现导入外部的图片资源的时候遇到一系列的问题&#xff0c;今天就来好好解决 首先&#xff0c;相去github的官网下载这个jar包&#xff0c;下载任意一个即可&#xff0c;注意&#xff1a;你的网络可能会被墙&#xff0c;你可以换成手机的热点去下载&#…

没有显示屏怎么启动服务器,中关村xp系统提示“没有启动服务器服务”如何解决...

通常情况下&#xff0c;windowsxp系统电脑发生故障都会弹出警告提示&#xff0c;然后用户就可以利用这些信息判断出错来源从而解决问题。比如&#xff0c;一位中关村XP系统用户反馈自己的电脑发生故障&#xff0c;弹出警告提示&#xff1a;没有启动服务器服务。这该如何处理呢&…

python如何改变入参的值_从事数据分析3年后,发现用python入门数据分析这三本书必看!...

做数据分析为什么选择python&#xff1f;为什么选择python来做数据分析&#xff1f;先来看一下用python做数据分析有什么优势1、python在数据分析方面有非常专业的模块&#xff0c;很多常用功能&#xff0c;在做数据分析时非常方便。2、python相比于其他计算机编程&#xff0c;…

Android studio使用手册说明

Android studio project约是eclipse workspace Android studio module是eclipse project 首先&#xff0c;从eclipse到Android studio是一个必然的过程了&#xff0c;Android studio集成了eclipseADT插件开发安卓APK&#xff0c;所有人都是希望越方便越好&#xff0c;不会去自…

Android studio的设置界面介绍

1.编译按钮 2.运行选择按钮 3.运行按钮 4.设置界面 5.设置页面布局 6.系统保存设置 7.密码设置 8.下载按钮&#xff1a; 9.数据传递&#xff1a; 11.改色&#xff1a; 12.自定义完成指定代码的检查 13.Notifications 14.宏设置 15.SDK管理&#xff0c;可以直接导入自己的SDK&am…

打印机显示rpc服务器,win10打印机出现RPC服务器不可用的处理办法

win10打印机出现RPC服务器不可用的处理办法&#xff1f;大家在使用打印机打印的时候&#xff0c;多多少少都会碰到一些这样那样的故障。这不就有win10系统的网友&#xff0c;在使用打印机打印的时候&#xff0c;提示&#xff1a;RPC服务器不可用。出现这样的故障&#xff0c;大…

Android Studio的快捷键图文并茂

首先在设置界面可以看到很多快捷键&#xff0c;并支持将之前软件的快捷键习惯导入安卓开发中&#xff0c;默认的就是安卓开发的快捷键&#xff0c;所以基本不需要一些学习成本 双击shirf可以跳出查找界面 修改快捷键 按住ctrlshirftalt并用鼠标单击要添加代码的位置。就可以实现…

python中str和input_python中input()与raw_input()的区别分析

使用input和raw_input都可以读取控制台的输入&#xff0c;但是input和raw_input在处理数字时是有区别的 纯数字输入 当输入为纯数字时 input返回的是数值类型&#xff0c;如int,float raw_inpout返回的是字符串类型&#xff0c;string类型 输入字符串为表达式 input会计算在字符…

解决Android studio 加载不出网络图片的步骤

第一&#xff1a;网络问题&#xff0c;好像连家里的wifi没有手机的wifi能下载那个github的压缩包&#xff0c; 第二&#xff1a;版本问题&#xff0c;我把导入的包降低版本了&#xff0c;系统匹配成功&#xff0c; 第三&#xff1a;布局的时候需要把宽度高度的比例和加载的网…

现代操作系统初理解

1.现代操作系统是由中断驱动的&#xff1a;如果没有进程要执行&#xff0c;没有I/O 设备要服务&#xff0c;也没有用户请求要响应&#xff0c;操作系统将会静静地等待某件事件的发生。 2.操作系统和用户共享了计算机系统的硬件和软件&#xff1a;既然是共享了&#xff0c;就可…

python没有菜单栏_解决Jupyter Notebook开始菜单栏Anaconda下消失的问题

出现的问题状况是&#xff1a;在Anaconda文件下&#xff0c;少了Jupyter Notebook快捷键&#xff0c;不然每次都要打开Anaconda Navigator再打开Jupyter Noterbook&#xff0c;有点麻烦。为了避免将Anaconda软件卸载重装才出现Jupyter Notebook&#xff0c;就去寻找解决办法。首…

小米机顶盒显示网络无法连接服务器,小米盒子无线网络连接不上怎么回事 - 卡饭网...

无线网络连接不上怎么回事,如何解决无线网络连接不上怎么回事,如何解决 无线网络连接不上怎么回事&#xff0c;如何解决&#xff0c;很多电脑用户可能会遇到这个问题。 一&#xff1a;先检查下你的无线网络开关是否打开&#xff0c;没打开请打开 二&#xff1a;检查你的无线网络…

联想服务器忘记系统登录密码,联想路由器登陆密码忘记了怎么办

路由器是互联网系统中的重要组成部分&#xff0c;是连接因特网中局域网和广域网的设备&#xff0c;它可以根据信道实际的情况选择最合适的路径&#xff0c;那么你知道联想路由器登陆密码忘记了怎么办吗?下面是学习啦小编整理的一些关于联想路由器登陆密码忘记了的相关资料&…

编写五子棋的完整python代码_python实现五子棋游戏

本文实例为大家分享了python实现五子棋游戏的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下 话不多说&#xff0c;直接上代码&#xff1a; 全部工程文件&#xff0c;在GitHub&#xff1a;五子棋 效果预览&#xff1a;#!/usr/bin/env python3 #-*- coding:utf-8 -*- …

服务器网站显示多协议,解决高防IIS8服务器https协议多个同时访问的解决方法

iis提示:至少一个其他网站正在使用同一个https绑定&#xff0c;而次绑定用另一个证书配置。确实要重用https绑定并将其他网站重新指定为使用新证书吗&#xff1f;网友解释&#xff1a;SNI指是一项用于改善SSL/TLS的技术&#xff0c;在SSLv3/TLSv1中被启用。它允许客户端在发起S…

如何计算实际物理地址?

1.确认物理地址&#xff1a; 2.由逻辑地址的页号去页表中查找对应的物理内存的页号 3.通过物理页号加上偏移量得到实际物理地址 例如&#xff1a;已知每页大小为1024byte&#xff08;逻辑页表和实际内存分页的大小都是1024byte&#xff09; 那么&#xff08;3,1023&#xf…