Java并发编程 —— synchronized关键字
一、是什么?(作用)
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关键字的更多相关文章
- Java并发编程 Volatile关键字解析
volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了 ...
- Java并发编程-synchronized
多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同步用以解决多个线程同时访问时可能出现的问题.同步机制可以使用synchronized关键字实现.synchronized关 ...
- Java并发编程-synchronized指南
在多线程程序中,同步修饰符用来控制对临界区代码的访问.其中一种方式是用synchronized关键字来保证代码的线程安全性.在Java中,synchronized修饰的代码块或方法不会被多个线程并发访 ...
- Java并发编程volatile关键字
volatile理解 Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和volatile 关键字机制.volatile具有synchronized关键字的“可见性”,vo ...
- java并发编程--Synchronized的理解
synchronized实现锁的基础:Java中每一个对象都可以作为锁,具体表现为3种形式. (1)普通同步方法,锁是当前实例对象 (2)静态同步方法,锁是当前类的Class对象 (3)同步方法块,锁 ...
- 并发编程——synchronized关键字的使用
前言 我们一般对共享数据操作的时候,为了达到线程安全我们会使用synchronized关键字去修饰方法或者代码块.那么今天我们就来讲一讲synchronized关键字的使用. 专栏推荐: 并发编程专栏 ...
- Java并发编程1--synchronized关键字用法详解
1.synchronized的作用 首先synchronized可以修饰方法或代码块,可以保证同一时刻只有一个线程可以执行这个方法或代码块,从而达到同步的效果,同时可以保证共享变量的内存可见性 2.s ...
- 并发编程-synchronized关键字大总结
0.synchronized 的特点: 可以保证代码的原子性和可见性. 1.synchronized 的性质: 可重入(可以避免死锁.单个线程可以重复拿到某个锁,锁的粒度是线程而不是调用).不可中断( ...
- Java并发编程_synchronized关键字的用法(一)
synchronized:意思是 同步,也就是 共享资源 Synchronized修饰方法:对象锁 Static Synchronized修饰方法:类锁 下面代码手动敲一遍,就会理解 一.Synch ...
- java并发编程 volatile关键字 精准理解
1.volatile的作用 一个线程共享变量(类的成员变量.类的静态成员变量等)被volatile修饰之后,就具有以下作用: 1)并发中的变量可见性(不同线程对该变量进行操作时的可见性),即一个线程修 ...
随机推荐
- C Primer Plus 5.11 編程練習
/*C Primer Plus (5.10) 9*/ 1 #include<stdio.h> 2 #define G 103 3 int main() 4 { 5 char ch=96; ...
- 使用pyenv对python进行版本控制—很好用
相对于python自带的virtualenv来说,pyenv的使用要便利些,更不用说自带的插件python-virtualenv,创建虚拟环境就更为方便了,其实最让我心水的功能是创建的虚拟环境,进入设 ...
- 什么是C语言
什么是C语言? C语言是一门计算机语言 计算机语言是什么呢? 人和计算机交流的语言,如C/C++.Java.python 计算机语言的发展? 二进制语言(硬件-电-正电1/负电0 1010100101 ...
- 玩转web3第一篇——web3-react
概况 web3-react是由Noah Zinsmeister开发的一个web3框架,主要功能是实时获取DApp里的关键数据(如用户当前连接的地址.网络.余额等). Noah也是著名的去中心化交易所u ...
- Java入门与进阶P-3.7+P-3.8
猜数游戏 让计算机来想一个数,然后让用户来猜,用户每输入一个数,就告诉它这是大了还是小了,知道用户猜中为止,最后还要告诉用户它猜了多少次 因为需要不断重复让用户猜,所以需要用到循环 在实际写出程序之前 ...
- MySQL-SQL语句查询关键字
1.SQL语句查询关键字 1.select:指定需要查找的字段信息,eg:select *,select name.同时select也支持对字段做处理,eg:select char_length(na ...
- vue的异步组件
异步组件 异步组件:可以在首页加载之前先加载的组件,主要是做性能优化,提高用户体验 一.基本用法 在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件.Vue 提供了 de ...
- 基于ChatGPT的API的C#接入研究
今年开年,最火的莫过于ChatGPT的相关讨论,这个提供了非常强大的AI处理,并且整个平台也提供了很多对应的API进行接入的处理,使得我们可以在各种程序上无缝接入AI的后端处理,从而实现智能AI的各种 ...
- 快速上手vue前端存储库、全局状态管理工具pinia
pinia是什么,为什么我们要使用pinia? pinia是vue全局状态管理工具,类似vueX,用于全局的数据状态存储.修改变更等等 相较于vueX,pinia的使用较为简单,轻量级,上手容易,干掉 ...
- KingbaseES R6集群物理copy方式手工添加新备库节点
案例说明:对于主库数据量比较大的环境,在添加新节点是可以采用在线clone方式创建新的备库节点,也可以在离线的状态下,直接拷贝其中一个备库的所有集群相关目录来创建新的备库节点.本案例介绍了通过离线物理 ...