07.synchronized都问啥?
大家好,我是王有志。关注王有志,一起聊技术,聊游戏,从北漂生活谈到国际风云。最近搞了个抽奖送书的活动,欢迎点击链接参与。
如果Java面试有什么是必问的,synchronized
必定占据一席之地。初出茅庐时synchronized
的用法,成长后synchronized
的原理,可谓是Java工程师的“一生之敌”。
synchronized都问啥?
按照惯例,先来看synchronized
的常见问题:
根据统计数据可以总结出synchronized
的5大考点:
synchronized
的使用方式:synchronized
是什么?synchronized
怎么用?- 不同用法都有什么效果?
synchronized
的实现原理:synchronized
的特性是如何实现的?synchronized
锁升级的原理。
今天我们先来看synchronized
的基础部分。
synchronized是什么?
synchronized
是Java中的关键字,提供了原生同步机制,实现互斥语义和可见性保证,通常称为互斥锁。
- 互斥指的是,当线程获取到锁后,其它试图获取锁的线程只能阻塞;
- 可见性指的是,
synchronized
修饰的语句内修改共享变量可以立即被其它线程获取。
互斥就意味着,同一时间只有一个线程执行synchronized
修饰的代码,那么:
- 无论怎么重排序,都会遵循as-if-serial语义,因此
synchronized
中不存在有序性问题; - 不主动释放锁,其他线程无法执行
synchronized
中代码,无需考虑原子性问题。
因此synchronized
中互斥就代表了对有序性问题和原子性问题的保证。不过前提是JSR-133中反复提到的correctly synchronized(正确的同步),举个例子:
public class IncorrectlySynchronized {
private Integer count = 0;
public void add() {
synchronized (count) {
count++;
}
}
public static void main(String[] args) throws InterruptedException {
IncorrectlySynchronized incorrectlySynchronized = new IncorrectlySynchronized();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
incorrectlySynchronized.add();
}
});
Thread t2 = new Thread(()-> {
for (int i = 0; i < 10000; i++) {
incorrectlySynchronized.add();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(incorrectlySynchronized.count);
}
}
看似该加synchronized
的地方都加了,但是结果却会出乎意料,这就典型的错误同步的例子。
synchronized锁什么?
既然是锁,那么synchronized
锁的是什么呢?
《The Java Language Specification》中描述(节选)到:
Each object in Java is associated with a monitor, which a thread can lock or unlock.
The synchronized statement computes a reference to an object; it then attempts to perform a lock action on that object's monitor and does not proceed further until the lock action has successfully completed.
Java中每个对象都与一个监视器关联,线程可以锁定或者解锁该监视器。synchronized
语句尝试锁定与对象关联的监视器,锁定成功后才可以继续执行。
通常,我们将synchronized
锁定与对象关联的监视器理解为synchronized
锁定对象本身。
在我们知道synchronized
锁什么后,再去看用法,很多内容就会一目了然了。
synchronized怎么用?
作为关键字,synchronized
有两种用法:
- 修饰代码块
- 修饰方法
- 修饰成员方法
- 修饰静态方法
之前有个同事特别迷信“背技术”,为了区分不同用法的效果,背了某机构的“线程八锁”,但每过一段时间就会忘记。
其实,知道了synchronized
锁什么,不同用法的效果自然就出来了,看一个例子:
public class SynchronizedDemo {
public static void main(String[] args) throws InterruptedException {
SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
Thread t1 = new Thread(synchronizedDemo::lockMemberMethod1);
Thread t2 = new Thread(synchronizedDemo::lockMemberMethod2);
t1.start();
// 确保t1先执行
TimeUnit.SECONDS.sleep(1);
t2.start();
}
private synchronized void lockMemberMethod1() {
System.out.println("方法1");
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private synchronized void lockMemberMethod2() {
System.out.println("方法2");
}
}
通过实例变量调用成员方法时,会隐式的传递this
。这个例子中,t1和t2想锁定的监视器是谁的?synchronizedDemo
对象的。t1先获取到,那么t2只能等待t1释放后再获取了。
那此时的锁定范围是什么?synchronizedDemo
对象。
修改下代码:
public static void main(String[] args) throws InterruptedException {
SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
SynchronizedDemo synchronizedDemo2 = new SynchronizedDemo();
Thread t1 = new Thread(synchronizedDemo::lockMemberMethod1);
Thread t2 = new Thread(synchronizedDemo2::lockMemberMethod2);
t1.start();
t2.start();
}
t2不再争夺synchronizedDemo
而是争夺synchronizedDemo2
,结果上也能看出t1和t2之间不存在竞争关系。
那么使用synchronized
修饰静态方法和代码块是什么效果呢?
private static synchronized void lockStaticMethod() {
System.out.println("静态方法!");
}
private void lockCodeBlock(int count) {
synchronized (this) {
System.out.println("成员方法的代码块!");
}
}
使用synchronized
修饰静态方法,锁定的对象是SynchronizedDemo.class
。所有SynchronizedDemo
的实例对象共用同一个SynchronizedDemo.class
,同一时间不同变量,只有一个线程可以执行lockStaticMethod
方法。
至于synchronized
修饰代码块,就比较灵活了,括号中是谁就锁定谁。如果是this
就锁定实例变量,如果是SynchronizedDemo.class
效果就和修饰静态方法一样。
至于前面错误的同步的例子,它的问题是count
对象在不断变化(Integer
实现相关)的,因此synchronized
锁定的并不是同一个对象。
结语
今天的内容非常基础,难度也不大。
重点可以放在synchronized
锁什么的部分,以及是如何推导出synchronized
不同用法产生的不同效果的。这样的方式更接近于问题的本质,也能更好的举一反三,而不是死记硬背“线程八锁”这种东西。
好了,今天就到这里了,Bye~~
07.synchronized都问啥?的更多相关文章
- Aaron Swartz – 互联网天才开挂的人生历程:每时每刻都问自己,现在这世界有什么最重要的事是我能参与去做的?
Aaron说的一句话让我挺有感触的-- 相信你应该真的每时每刻都问自己,现在这世界有什么最重要的事是我能参与去做的? 如果你没在做那最重要的事,那又是为什么? 1986年11月8日,有个叫Aaron ...
- 阿里面试Java程序员都问些什么?
刚开始也是小白,也是一步步成成起来的.需要提的一点是,你将来是需要靠这个吃饭的,所以请对找工作保持十二分的热情,而且越早准备越好. 阿里一面 一面是在上午9点多接到支付宝的面试电话的,因为很期望能够尽 ...
- 关于微信小程序,你想知道的他们都问了
微信公开课深圳站小程序专场刚刚结束,大家通过"微信公开课+"互动小程序提出了许多问题.我们筛选了后台问得最多的九个问题进行解答,快来看看这里有没有你想要的答案吧! @谢杨:小程序是 ...
- Java多线程系列 基础篇07 synchronized底层优化
转载 http://www.cnblogs.com/paddix/ 作者:liuxiaopeng http://www.infoq.com/cn/articles/java-se-16-synchro ...
- 2019年Spring Boot面试都问了什么?快看看这22道面试题!
Spring Boot 面试题 1.什么是 Spring Boot? 2.Spring Boot 有哪些优点? 3.什么是 JavaConfig? 4.如何重新加载 Spring Boot 上的更改, ...
- 2019年JVM面试都问了什么?快看看这22道面试题!(附答案解析)
一. Java 类加载过程? Java 类加载需要经历一下 7 个过程: 1. 加载 加载是类加载的第一个过程,在这个阶段,将完成一下三件事情: • 通过一个类的全限定名获取该类的二进制流. • 将该 ...
- 月薪20k+的Android面试都问些什么?(含答案)
金九银十跳槽季接近尾声了,可是今年由于疫情的影响仍然不太好找工作,相信大家肯定急需一套Android面试宝典,下面就分享给大家我珍藏已久的Android高阶面试宝典,供大家学习 ! 1.自定义Hand ...
- Java中的Lock与synchronized
并发编程学习笔记之Lock与synchronized 一.什么是可重入锁 Lcok在Java中是一个接口,一般在面试问题中问到的可能是ReentrantLock与synchronized的区别.Ree ...
- 解决多线程安全问题-无非两个方法synchronized和lock 具体原理(百度-美团)
还有其他的锁,如果想要了解,参考:JAVA锁机制-可重入锁,可中断锁,公平锁,读写锁,自旋锁, 用synchronized实现ReentrantLock 美团面试题参考:使用synchronized ...
- Hashtable数据存储结构-遍历规则,Hash类型的复杂度为啥都是O(1)-源码分析
Hashtable 是一个很常见的数据结构类型,前段时间阿里的面试官说只要搞懂了HashTable,hashMap,HashSet,treeMap,treeSet这几个数据结构,阿里的数据结构面试没问 ...
随机推荐
- 微软出品自动化神器【Playwright+Java】系列(五) 之 常见点击事件操作
写在前面 明天就是周五了,这周有那么一两天心情特别不好,真的是做什么都没兴致,所以导致整个人都很丧,什么都不想做. 本打算周一就更新这篇文章的,但由于公司一直加班,每天到家很晚,都是挤时间去学,理解后 ...
- JUC(2)使用condition实现精准通知唤醒
package com.orderPC; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks. ...
- 《吐血整理》高级系列教程-吃透Fiddler抓包教程(30)-Fiddler如何抓取Android7.0以上的Https包-番外篇
1.简介 通过宏哥前边几篇文章的讲解和介绍想必大家都知道android7.0以上,有android的机制不在信任用户证书,导致https协议无法抓包.除非把证书装在系统信任的证书里,此时手机需要roo ...
- F118校准(二)-- 操作步骤(使用任意品牌PG点屏,并使用PX01 PG校准F118)
1. 准备工作 硬件连接: CA310通过USB线材连接PC PX01通过USB线材连接PC F118通过灰排线连接PX01左上角的GPIO扩展口(如下图所示) 启动LcdTools软件,点击&quo ...
- LcdTools如何通过PX01把EDP屏的EDID拷贝出来
PX01点EDP屏在上电过程会自动读取屏EDID,怎么把EDP EDID值拷贝出来呢? 在上电时序函数调用SetEdidRdShowEn(ON)指令开启EDID值读取显示功能.如下图 通过上述操作开机 ...
- C# 6.0 添加和增强的功能【基础篇】
C# 6.0 是在 visual studio 2015 中引入的.此版本更多关注了语法的改进,让代码更简洁且更具可读性,使编程更有效率,而不是和前几个版本一样增加主导性的功能. 一.静态导入 我们都 ...
- ES6 学习笔记(十三)promise的简单使用
1.什么是promise 在JavaScript中,我们经常会用到回调函数,而回调函数的使用让我们没法使用return,throw等关键字.JS引用promise正好解决了这个问题. promise单 ...
- 有依赖的背包问题(Acwing 10)
1 # include<iostream> 2 # include<cstring> 3 # include<algorithm> 4 using namespac ...
- C#文件路径
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Run ...
- 万字干货_JDK动态代理及其源码解析 拿捏了
目录 代理模式 静态代理 静态代理和动态代理的区别?什么是静态.动态? 静态代理的使用步骤 示例 静态代理的缺陷 解决静态代理的缺陷的思路 JDK动态代理 JDK 动态代理类使用步骤 示例 底层原理 ...