返回顶部
首页 > 资讯 > 后端开发 > Python >Java同步锁synchronized用法的最全总结
  • 465
分享到

Java同步锁synchronized用法的最全总结

Java同步锁synchronized的总结 2023-03-21 17:03:06 465人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录一、并发同步问题二、锁的简介三、synchronized的三种应用方式1.修饰一个实例方法2.修饰一个静态方法3.修饰一个代码块(2)锁对象为类的Class对象四、synchro

一、并发同步问题

  线程安全是Java并发编程中的重点,而造成线程安全问题的主要原因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据。因此,当存在多个线程操作共享数据时,需要保证同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再进行,这种方式就叫互斥锁。也就是说当一个共享数据被正在访问的线程加上互斥锁后,在同一个时刻,其他线程只能处于等待的状态,直到当前线程处理完毕释放该锁。在 Java 中,关键字 synchronized可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),同时synchronized还有另外一个重要的作用,它可以可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以替代Volatile功能)。

二、锁的简介


synchronized是Java的关键字,是一种同步锁。
  Java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁称为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。
  Java内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,直到线程B释放这个锁。
  Java的对象锁和类锁:java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。
  在Java中,每个对象都有一把锁和两个队列,一个队列用于挂起未获得锁的线程,一个队列用于挂起条件不满足而等待的线程。而synchronized实际上也就是一个加锁和释放锁的集成。JVM负责跟踪对象被加锁的次数。如果一个对象被解锁,其计数变为0。在任务(线程)第一次给对象加锁的时候,计数变为1。每当这个相同的任务(线程)在此对象上获得锁时,计数会递增。只有首先获得锁的任务(线程)才能继续多次获取该对象上的锁。每当任务离开一个synchronized方法,计数递减,当计数为0的时候,锁被完全释放,此时别的任务就可以使用此资源。

三、synchronized的三种应用方式

synchronized可以修饰范围的包括:方法级别,代码块级别;而实际加锁的目标包括:对象锁(普通变量,静态变量),类锁。具体分为三种应用方式:

1.修饰一个实例方法

  被修饰的方法称为实例同步方法,其作用范围是整个方法,锁定的是该方法所属的对象(即调用该方法的对象)。所有需要获得该对象锁的操作都会对该对象加锁(即访问该对象的其他同步实例方法或进入对该对象加锁的代码块)。实例同步方法的代码如下:

public synchronized void method(){
   // 具体代码
}

  当一个对象O1在不同的线程中执行这个同步方法时,他们之间会形成互斥,达到同步的效果。但是这个对象所属类的另一对象O2却能够调用这个被加了synchronized关键字的方法。 每个对象实例对应一把锁,线程只有获得对象实例的锁才能执行它的synchronized方法。 如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法。但是该类的其他对象实例的 synchronized方法是不相干扰的。这种机制确保了同一时刻对于每一个对象实例,其所有声明为 synchronized 的成员方法中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为synchronized)。上边的示例代码等同于如下代码:

public void method(){
   synchronized(this){
       //具体代码
   }
}

  其中this指的是调用这个方法的对象,如O1。可见同步方法实质是将synchronized作用于对象引用。只有获得O1对象锁的线程,才能够调用O1的同步方法,而对O2而言,O1对象锁和它互不关联,其他线程调用O2中的相同方法时,并不会产生同步阻塞。程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱。sychronized修饰方法时需要注意以下3点:

(1)synchronized关键字不能继承。


  虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,必须显式地在子类为这个方法加上synchronized关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。这两种方式的示例代码如下:
  手动加上synchronized修饰

class Parent{
  public synchronized void method() {}
}
class Child{
  public synchronized void method() {}
}

  在子类中调用父类同步方法
class Parent{
  public synchronized void method() {}
}
class Child{
  public synchronized void method() {}
}

(2)在定义接口方法时不能使用synchronized关键字。
(3)构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。

2.修饰一个静态方法

  被修饰的方法被称为静态同步方法,其作用的范围是整个静态方法,锁是静态方法所属的类(即Class对象)。所有需要获得该类的任意对象的锁,都会触发同步。静态同步方法的示例如下图:

上述代码中,虽然创建了SynThread类的两个对象,但是该类中的run方法调用的是静态同步方法,所以在运行过程中会同步执行。因此,synchronized作用在静态方法上时,可以防止多个线程同时访问这个类中的静态方法,它对类的所有实例对象都起作用。

3.修饰一个代码块


  被修饰的代码块称为同步语句块。synchronized的括号中必须传入一个对象(实例对象或类的Class对象)作为锁。其作用范围是大括号{}括起来的代码,锁是Synchronized括号里指定的内容。按照对象的类型可以分为类锁和对象锁。

(1)锁对象为实例对象

public void method(Object o) {   
    synchronized(o) {          
       ...  
    }
}


上述代码锁定的就是o这个对象,只要进入以该对象为锁的任何代码都会触发同步。当有一个明确的对象作为锁时,可以直接以该对象作为锁。当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的对象来充当锁。例如:

private byte[] lock = new byte[0];

注:查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。因此使用特殊对象来充当锁,大大节省了系统的开销。

(2)锁对象为类的Class对象

public class Demo{
  ...
  public static void method(){
    synchronized(Demo.class){
      ...
    }
  }
}


上述代码是以Demo类的Class对象为锁,进入以该类任意实例对象为锁的代码都会触发同步,其效果类似于静态同步方法。

四、synchronized的实现原理

monitor对象
  Java中的同步代码块是使用monitorenter和monitorexit指令实现的,其中monitorenter指令插入到同步代码块的开始位置,monitorexit指令插入到同步代码块的结束位置。JVM保证每一个monitorenter都有一个monitorexit与之相对应。任何对象都有一个monitor与之相关联,当线程执行到monitorenter指令时,将会尝试获取锁对象所对应的monitor所有权,即尝试获取对象的锁;当线程执行monitorexit指令时,锁的monitor就会被释放。同步方法的实现与同步块略有不同,它依靠的是方法修饰符上的ACC_SYNCHRONIZED实现。synchronized具体的实现原理详见本人另一篇文章:
深入理解Java中Synchronized的实现原理

五、Synchronized与重入锁ReentrantLock的区别

(1) 相对于ReentrantLock而言,synchronized锁是重量级锁,重量级体现在活跃性差一点。同时synchronized锁是内置锁,意味着JVM能基于synchronized锁做一些优化:比如增加锁的粒度(锁粗化)、锁消除。
(2) 在synchronized锁上阻塞的线程是不可中断的:线程A获得了synchronized锁,当线程B也去获取synchronized锁时会被阻塞。而且线程B无法被其他线程中断(不可中断的阻塞),而ReentrantLock锁能实现可中断的阻塞。
(3) synchronized锁释放是自动的,当线程执行退出synchronized锁保护的同步代码块时,会自动释放synchronized锁。而ReentrantLock需要显示地释放:即在try-finally块中释放锁。
(4) 线程在竞争synchronized锁时是非公平的:假设synchronized锁目前被线程A占有,线程B请求锁未果,被放入队列中,线程C请求锁未果,也被放入队列中,线程D也来请求锁,恰好此时线程A将锁释放了,那么线程D将跳过队列中所有的等待线程并获得这个锁。而ReentrantLock能够实现锁的公平性。
(5) synchronized锁是读写互斥并且读读也互斥,ReentrantReadWriteLock 分为读锁和写锁,而读锁可以同时被多个线程持有,适合于读多写少场景的并发。
(6) ReentrantLock锁的是代码块,synchronized还能锁方法和类。ReentrantLock可以知道线程有没有拿到锁,而synchronized不能。

六、总结

(1) synchronized如果修饰的是代码块,则根据传入内容决定锁的类型;synchronized如果修饰的是实例方法,它获取的锁是调用该方法的对象实例;synchronized如果修饰的是静态方法,它获取的锁是调用该方法所属的类,访问该类所有的同步模块都会加锁(每个对象的同步方法、类的同步方法)。
(2) synchronized同步的关键要看锁的类型
  如果是对象锁(即同步块传入对象作为锁或者实例同步方法),那么其他任意需要获得该对象锁的同步机制都会触发加锁。即线程进入一个同步实例方法,就会获得其所属对象的锁。此时,如果其他线程进入该对象其他的同步实例方法或同步代码块时就会阻塞。
  如果是类锁(即同步块传入类作为锁或者静态同步方法),那么其他任意需要获得该类锁的同步机制都会触发加锁。即线程进入一个静态同步方法,就会获得该方法所属类的锁。此时,如果其他线程再进入该类的其他同步方法(静态或非静态),或者进入获取该类锁的同步块,都会阻塞。
(3) 对象的内置锁和对象的状态之间没有内在的关联,虽然大多数类都将内置锁用作一种有效的加锁机制,但对象的域并不一定通过内置锁来保护。当获取到与对象关联的内置锁时,并不能阻止其他线程访问该对象,当某个线程获得对象的锁之后,只能阻止其他线程获得同一个锁。所以synchronized只是一个内置锁的加锁机制,当某个方法加上synchronized关键字后,就表明要获得该内置锁才能执行,并不能阻止其他线程访问不需要获得该内置锁的方法。
(4) 为什么要使用同步代码块?
  首先对程序来讲同步的部分很影响运行效率,同步所覆盖的代码越多,对效率的影响就越严重。因此我们通常尽量缩小其影响范围。所以就出现了同步代码块。只把一个方法中该同步的地方同步,并且同步代码块可以指定锁对象。
(5) 使用synchronized进入一个临界区,会获得对应的锁,退出时不管是否立即使用该对象的其他同步方法,都要立即释放锁,重新竞争获得。如何连续使用一个对象的所有同步方法,不用中途释放锁,可以创建一个线程,使用一个同步代码块,同步锁指定为该对象,然后在该代码块中可以连续调用该对象的同步方法。
(6) synchronized方法的缺陷
   a.实例同步方法锁定的是调用这个同步方法的对象。也就是说,一个对象P1在不同的线程中执行其同步方法时会产生互斥达到同步效果。但是P1对象所属类所创建的另一对象P2却可以调用这个同步方法。同步方法实质是将synchronized作用于对象引用。只有拿到P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱。
  b.若将一个代码量大的方法声明为synchronized将会大大影响效率。典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都不会成功。
 

--结束END--

本文标题: Java同步锁synchronized用法的最全总结

本文链接: https://lsjlt.com/news/200742.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Java同步锁synchronized用法的最全总结
    目录一、并发同步问题二、锁的简介三、synchronized的三种应用方式1.修饰一个实例方法2.修饰一个静态方法3.修饰一个代码块(2)锁对象为类的Class对象四、synchro...
    99+
    2023-03-21
    Java同步锁 synchronized的总结
  • Java同步锁synchronized怎么使用
    本文小编为大家详细介绍“Java同步锁synchronized怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java同步锁synchronized怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一...
    99+
    2023-07-05
  • 怎么用Java实现synchronized锁同步机制
    这期内容当中小编将会给大家带来有关怎么用Java实现synchronized锁同步机制,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。synchronized 实现原理synchronized 是通过进入和...
    99+
    2023-06-25
  • java中synchronized Lock(本地同步)锁的8种情况
    目录lock1lock2lock3lock4lock5lock6lock7lock8Lock(本地同步)锁的8种情况总结与说明: * 题目: * 1.标准访问,请问是先打印邮件还是...
    99+
    2024-04-02
  • Java对象级别与类级别的同步锁synchronized语法怎么用
    本篇内容主要讲解“Java对象级别与类级别的同步锁synchronized语法怎么用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java对象级别与类级别的同步锁synchronized语法怎么...
    99+
    2023-06-29
  • Java对象级别与类级别的同步锁synchronized语法示例
    目录1. 对象级别的同步锁2. 类级别的同步锁3. 总结 Java synchronized 关键字 可以将一个代码块或一个方法标记为同步代码块。同步代码块是指同一时间只能有一个线程...
    99+
    2024-04-02
  • Java创建线程的七种方法,全网最全面总结~
    目录 前言 一、继承Thread,重写run方法 二、实现Runnable接口,重写run方法 三、使用匿名内部类创建 Thread 子类对象 四、使用匿名内部类,实现Runnable接口 五、lambda表达式 六、实现Callable...
    99+
    2023-09-13
    java 多线程
  • Java并发系列之JUC中的Lock锁与synchronized同步代码块问题
    目录一、Lock锁二、锁的底层三、案例案例一:传统的synchronized实现案例二:Lock锁的实现四、Lock锁和synchronized的区别写在前边: 在Java服务端中,...
    99+
    2024-04-02
  • Java调用Python程序方法总结(最
    如何使用Java调用Python程序本文为大家介绍如何java调用python方法,供大家参考。 实际工程项目中可能会用到Java和python两种语言结合进行,这样就会涉及到一个问题,就是怎么用Java程序来调用已经写好的pytho...
    99+
    2023-01-31
    程序 方法 Java
  • Java实现线程同步的四种方式总结
    目录什么是线程同步线程同步的几种方式1.使用synchronized关键字2.使用ReentrantLock3.使用原子变量实现线程同步4.ThreadLocal实现线程同步什么是线...
    99+
    2024-04-02
  • 最全总结SpringBean的作用域管理
    目录一、前言1.1 详细介绍下 singleton 作用域?1.2 和单例模式有何联系去区别呢?1.3 Prototype作用域二、bean的作用域三、singleton —— 唯一...
    99+
    2024-04-02
  • java同步机制及synchronized关键字的应用是怎样的
    java同步机制及synchronized关键字的应用是怎样的,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。Java对多线程的支持与同步机制深受大家的喜爱,似乎看起来使用了sy...
    99+
    2023-06-03
  • Python3中最常用的5种线程锁实例总结
    目录前言线程安全锁的作用Lock() 同步锁基本介绍使用方式死锁现象with语句RLock() 递归锁基本介绍使用方式with语句Condition() 条件锁基本介绍使用方式wit...
    99+
    2024-04-02
  • 【2022最新最有效】wampserver和mysql冲突解锁方法,同时运行,共存的方法,自己总结的经验!
    文章目录 前言一、安装mysql8.0.30二、安装wampserver3.0.6_x641.双击安装包,然后next2.选择路径 3.后面会叫你选择wampserver打开的浏览器,选择...
    99+
    2023-09-14
    mysql php 经验分享
  • Java线程状态及同步锁的操作方法
    线程的生命历程 线程的五大状态 创建状态:简而言之,当创建线程对象的代码出现的时候,此时线程就进入了创建状态。这时候的线程只是行代码而已。只有调用线程的start()方法时...
    99+
    2024-04-02
  • C/C++获取当前时间的方法总结(最全)
    目录一、获取当前时间1.使用C语言标准库2.使用VS提供的ATL模板库3.使用Win API二、代码解析1.time函数2.localtime函数3.tm结构体4.localtime...
    99+
    2023-03-19
    C++获取当前时间 C++获取时间 C++时间
  • 如何正确的在java中使用同步锁
    这篇文章给大家介绍如何正确的在java中使用同步锁,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。同步锁分类对象锁(this)类锁(类的字节码文件对象即类名.class)字符串锁(比较特别)应用场景在多线程下对共享资源的...
    99+
    2023-05-31
    java 同步锁 ava
  • Java中的Object类用法总结
    目录1.Object类是什么?2.Object类中的equals方法3.Object类中的hashCode方法4.编译器自动生成equals和hashCode总结1.Object类是...
    99+
    2023-05-17
    java的object类型 java中的object类 java object类有哪些方法
  • 在Java中Scanner的用法总结
    最近在做OJ类问题的时候,经常由于Scanner的使用造成一些细节问题导致程序不通过(最惨的就是网易笔试,由于sc死循环了也没发现,导致AC代码也不能通过。。。),因此对Scanne...
    99+
    2024-04-02
  • java中引用类型的用法总结
    一、class作为成员变量在定义一个类Role(游戏角色)时,代码如下:使用 int 类型表示角色id和生命值,使用String类型表示姓名。此时,String 本身就是引用类型,由于使用的方式类似常量,所以往往忽略了它是引用类型的存在。如...
    99+
    2016-05-29
    java 引用类型 用法
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作