大家好,我是王有志。关注王有志,一起聊技术,聊游戏,从北漂生活谈到国际风云。最近搞了个抽奖送书的活动,欢迎点击链接参与。

如果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都问啥?的更多相关文章

  1. Aaron Swartz – 互联网天才开挂的人生历程:每时每刻都问自己,现在这世界有什么最重要的事是我能参与去做的?

    Aaron说的一句话让我挺有感触的-- 相信你应该真的每时每刻都问自己,现在这世界有什么最重要的事是我能参与去做的? 如果你没在做那最重要的事,那又是为什么? 1986年11月8日,有个叫Aaron ...

  2. 阿里面试Java程序员都问些什么?

    刚开始也是小白,也是一步步成成起来的.需要提的一点是,你将来是需要靠这个吃饭的,所以请对找工作保持十二分的热情,而且越早准备越好. 阿里一面 一面是在上午9点多接到支付宝的面试电话的,因为很期望能够尽 ...

  3. 关于微信小程序,你想知道的他们都问了

    微信公开课深圳站小程序专场刚刚结束,大家通过"微信公开课+"互动小程序提出了许多问题.我们筛选了后台问得最多的九个问题进行解答,快来看看这里有没有你想要的答案吧! @谢杨:小程序是 ...

  4. Java多线程系列 基础篇07 synchronized底层优化

    转载 http://www.cnblogs.com/paddix/ 作者:liuxiaopeng http://www.infoq.com/cn/articles/java-se-16-synchro ...

  5. 2019年Spring Boot面试都问了什么?快看看这22道面试题!

    Spring Boot 面试题 1.什么是 Spring Boot? 2.Spring Boot 有哪些优点? 3.什么是 JavaConfig? 4.如何重新加载 Spring Boot 上的更改, ...

  6. 2019年JVM面试都问了什么?快看看这22道面试题!(附答案解析)

    一. Java 类加载过程? Java 类加载需要经历一下 7 个过程: 1. 加载 加载是类加载的第一个过程,在这个阶段,将完成一下三件事情: • 通过一个类的全限定名获取该类的二进制流. • 将该 ...

  7. 月薪20k+的Android面试都问些什么?(含答案)

    金九银十跳槽季接近尾声了,可是今年由于疫情的影响仍然不太好找工作,相信大家肯定急需一套Android面试宝典,下面就分享给大家我珍藏已久的Android高阶面试宝典,供大家学习 ! 1.自定义Hand ...

  8. Java中的Lock与synchronized

    并发编程学习笔记之Lock与synchronized 一.什么是可重入锁 Lcok在Java中是一个接口,一般在面试问题中问到的可能是ReentrantLock与synchronized的区别.Ree ...

  9. 解决多线程安全问题-无非两个方法synchronized和lock 具体原理(百度-美团)

    还有其他的锁,如果想要了解,参考:JAVA锁机制-可重入锁,可中断锁,公平锁,读写锁,自旋锁, 用synchronized实现ReentrantLock 美团面试题参考:使用synchronized ...

  10. Hashtable数据存储结构-遍历规则,Hash类型的复杂度为啥都是O(1)-源码分析

    Hashtable 是一个很常见的数据结构类型,前段时间阿里的面试官说只要搞懂了HashTable,hashMap,HashSet,treeMap,treeSet这几个数据结构,阿里的数据结构面试没问 ...

随机推荐

  1. 项目管理构建工具——Maven(高阶篇)

    项目管理构建工具--Maven(高阶篇) 我们在之前的文章中已经基本了解了Maven,但也仅仅只止步于了解 Maven作为我们项目管理构建的常用工具,具备许多功能,在这篇文章中我们来仔细介绍 分模块开 ...

  2. if、where、trim、choose、when、otherwise、foreach

    1.if if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行:反之标签中 的内容不会执行 <!--List<Emp> getEmpListBy ...

  3. CCS 2022 极客少年挑战赛 writeup

    ​ 目录 题目一DSDS 操作内容: 题目二 easy_re 操作内容: flag值: 题目三 1+1=all 解题过程 题目一DSDS 操作内容: 开环境然后进入网址在网址后./目录 进入目录得到个 ...

  4. Linux实战笔记__Ubuntu20.04上搭建Vulhub漏洞环境

    安装python3和pip3 安装docker 安装docker-compose 上传解压vulhub-master.zip 启动漏洞环境 进入某漏洞目录,执行docker-compose up -d ...

  5. 3.Task对象

    Task对象 用于调度或并发协程对象 在事件循环中可以添加多个任务   创建task对象三种方式 创建task对象可以让协程加入事件循环中等待被调度执行 3.7版本之后加入asyncio.create ...

  6. 【k8s】k8s pv、pvc无法删除问题。

    一般删除步骤为:先删除pod再删除pvc最后删除pv 遇到的问题 但是遇到pv使用处于"Terminating"状态,而且删不掉.如下图: 解决办法 直接删除k8s中的记录: ku ...

  7. centos 7.6镜像_Centos7 配置本地yum源为iso镜像

    创建挂载路径 sudo mkdir /media/iso 挂载ISO镜像到目录 sudo mount -o loop CentOS-7-x86_64-Minimal-1810.iso /media/i ...

  8. 幂次方表达:p1010

    1 题目ID: P1010 [NOIP1998 普及组] 幂次方 2 题目描述: 任何一个正整数都可以用 22 的幂次方表示.例如 137=2^7+2^3+2^0137=27+23+20. 同时约定方 ...

  9. VMware ESXi 8.0 SLIC 2.6 & macOS Unlocker (Oct 2022 GA)

    ESXi 8.0.0 GA (General Availability) 请访问原文 VMware ESXi 8.0 SLIC 2.6 & macOS Unlocker (Oct 2022 G ...

  10. 17、输入一行以空格分隔的英文,判断其共有多少单词,不能包含冠词a

    /*输入一行以空格分隔的英文,判断其共有多少单词,不能包含冠词a */ #include <stdio.h> #include <stdlib.h> int isWord(ch ...