一、是什么?(作用)

synchronized关键字解决了多个线程之间访问资源的同步性问题,保证了被其修饰的方法或是代码块在任意时刻只能有一个线程执行。

而在早期的Java版本中,synchronized属于重量级锁,效率低下。为什么呢?

因为监视器锁(monitor)是依赖底层的操作系统Mutex Lock来实现的,Java的线程是映射到操作系统的原生线程之上的。如果要挂起或唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高。

庆幸的是,在Java 6之后Java官方从JVM层面对synchronized做了较大的优化,所以现在的synchronized锁效率也优化的很不错了。JDK 1.6对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗话、偏向锁、轻量级锁等技术来减少操作的开销。

故而,目前不论是各种开源框架还是JDk源码都大量使用了synchronized关键字。

二、怎么用?(实践)

1、主要有三种使用方式:

  • 修饰实例方法:给当前对象实例加锁。进入同步代码前要获得 当前对象实例的锁
synchronized void method(){
//业务代码
}
  • 修饰静态方法:给当前类加锁。会作用于当前类的所有对象实例,进入同步代码前要获得当前类的锁。因为静态成员不属于任何一个实例对象,是类成员(static表明这是该类的一个静态资源,无论new了多少对象,只有一份)。所以,如果一个线程A调用一个实例对象的非静态synchronized方法,而线程B需要调用这个实例对象所属类的静态synchronized方法,是允许的,不会发生互斥现象的,因为访问静态synchronized方法占用的锁是当前类的锁,而访问非静态synchronized方法占用的锁是当前实例对象的锁,故而不矛盾。
synchronized static void method(){
//业务代码
}
  • 修饰代码块:给指定对象加锁。synchronized(thisobject)表示进入同步代码块前要获得thisobject对象的锁synchronized(类.class)表示进入同步代码前要获得指定类的class锁
synchronized(this){
//业务代码
}

2、小结

  • synchronized关键字加在static静态方法和synchronized(class)代码块上都是给加锁。
  • synchronized关键字加在实例方法上是给对象实例加锁。
  • 尽量不要使用synchronized(String str),因为在JVM中,字符串常量池具有缓存功能!

三、为什么?(底层原理)

synchronized关键字底层原理属于JVM层面。可看 深入理解synchronized底层原理,一篇文章就够了! 这篇文章。

四、面试问题

1、使用双重校验锁实现对象单例(线程安全)

public class Singleton {
private volatile static Singleton uniqueInstance; private Singleton() {} public static Singleton getUniqueInstance(){
//先判断对象是否已实例化过,若没有方可进入加锁代码块
if(uniqueInstance == null){
//类对象加锁
synchronized(Singleton.class){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}

需要注意的是uniqueInstance采用volatile关键字修饰是很必要的。为什么呢?uniqueInstance = new Singleton();这段代码其实是分三步执行的:

  • 1、为uniqueInstance分配内存空间
  • 2、初始化uniqueInstance
  • 3、将uniqueInstance指向分配的内存地址

但由于JVM具有指令重排序的特性,执行顺序有可能变成 1 -> 3 -> 2。指令重排序在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程T1执行了1和3,此时T2调用getUniqueInstance()后发现uniqueInstance不为空,因此返回uniqueInstance,但此时uniqueInstance还未被初始化。

此处就不做细分析,简而言之,使用volatile可以禁止JVM的指令重排,保证上述代码在多线程环境下也能正常运行。

2、构造方法能否使用synchronized关键字修饰?

回答:不可以,构造方法本身就是线程安全的,不存在同步的构造方法一说。

Java并发编程 —— synchronized关键字的更多相关文章

  1. Java并发编程 Volatile关键字解析

    volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了 ...

  2. Java并发编程-synchronized

    多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同步用以解决多个线程同时访问时可能出现的问题.同步机制可以使用synchronized关键字实现.synchronized关 ...

  3. Java并发编程-synchronized指南

    在多线程程序中,同步修饰符用来控制对临界区代码的访问.其中一种方式是用synchronized关键字来保证代码的线程安全性.在Java中,synchronized修饰的代码块或方法不会被多个线程并发访 ...

  4. Java并发编程volatile关键字

    volatile理解 Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和volatile 关键字机制.volatile具有synchronized关键字的“可见性”,vo ...

  5. java并发编程--Synchronized的理解

    synchronized实现锁的基础:Java中每一个对象都可以作为锁,具体表现为3种形式. (1)普通同步方法,锁是当前实例对象 (2)静态同步方法,锁是当前类的Class对象 (3)同步方法块,锁 ...

  6. 并发编程——synchronized关键字的使用

    前言 我们一般对共享数据操作的时候,为了达到线程安全我们会使用synchronized关键字去修饰方法或者代码块.那么今天我们就来讲一讲synchronized关键字的使用. 专栏推荐: 并发编程专栏 ...

  7. Java并发编程1--synchronized关键字用法详解

    1.synchronized的作用 首先synchronized可以修饰方法或代码块,可以保证同一时刻只有一个线程可以执行这个方法或代码块,从而达到同步的效果,同时可以保证共享变量的内存可见性 2.s ...

  8. 并发编程-synchronized关键字大总结

    0.synchronized 的特点: 可以保证代码的原子性和可见性. 1.synchronized 的性质: 可重入(可以避免死锁.单个线程可以重复拿到某个锁,锁的粒度是线程而不是调用).不可中断( ...

  9. Java并发编程_synchronized关键字的用法(一)

    synchronized:意思是 同步,也就是 共享资源 Synchronized修饰方法:对象锁 Static Synchronized修饰方法:类锁 下面代码手动敲一遍,就会理解  一.Synch ...

  10. java并发编程 volatile关键字 精准理解

    1.volatile的作用 一个线程共享变量(类的成员变量.类的静态成员变量等)被volatile修饰之后,就具有以下作用: 1)并发中的变量可见性(不同线程对该变量进行操作时的可见性),即一个线程修 ...

随机推荐

  1. 这可能是Matplotlib和Seaborn最全的入门文档

    matplotlib是python第一个数据可视化库,在数据分析,可视化领域的地位和贡献是无法磨灭的.但也正是因为有了这位老大哥的出现给后续基于matplotlib实现的绘图库实现了可能. 而对于绘图 ...

  2. 【Redis场景拓展】秒杀问题-全局唯一ID生成策略

    全局唯一ID 为什么要使用全局唯一ID: 当用户抢购时,就会生成订单并保存到订单表中,而订单表如果使用数据库自增ID就存在一些问题: 受单表数据量的限制 id的规律性太明显 场景分析一:如果我们的id ...

  3. .Net7运行模型之托管Main函数的调用

    前言: .Net7的CLR最具特色的一个地方,就是运行模型.因为它主宰了整个CLR的运行过程. 又因为其庞大的代码量,有的几十万行甚至百万行.所以理解起来非常不容易.本篇拆分来看下,里面一个细节Mai ...

  4. 在 SpringBoot 项目中简单实现 JWT 验证

    使用 SpringBoot 提供 api 的时候,我更喜欢使用 jwt 的方式来做验证.网上有会多 Spring Security 整合 jwt 的,也有 Shiro 整合 jwt 的,感觉有点复杂. ...

  5. Task记录1.CancellationToken 取消Task任务的操作

    //1.创建取消令牌数据 CancellationTokenSource tokenSource = new CancellationTokenSource(); //2.创建取消令牌 Cancell ...

  6. 第三方模块:requests模块和openpyxl模块

    1.第三方模块的下载应由 第三方模块:别人写的模块 一般情况下功能都特别强大 我们如果想使用第三方模块 第一次必须先下载后面才可以反复使用(等同于内置模块) 下载第三方模块的方式 1.pip工具 注意 ...

  7. ssh 连接 wsl2

    修改ssh相关config https://cloud.tencent.com/developer/article/1538305 其中,端口可以设置成 2222 以防万一 然后在wsl 中执行如下命 ...

  8. layedit 清空 编辑器

    使用layedit.setContent(index,"") 即可以清除 layui.use('layedit', function(){ var layedit = layui. ...

  9. JAVA虚拟机20-基于栈的解释器执行过程示例

    1.准备代码 public int calc() { int a = 100; int b = 200; int c = 300; return (a + b) * c; } 2.使用javap -v ...

  10. docker02-centos上安装

    1.前提说明Docker 运行在 CentOS 7 上,要求系统为64位.系统内核版本为 3.10 以上.Docker 运行在 CentOS-6.5 或更高的版本的 CentOS 上,要求系统为64位 ...