请选择 进入手机版 | 继续访问电脑版

[多线程] synchronized关键字简介 多线程中篇(十一)

编程语言 编程语言 1619 人阅读 | 0 人回复

前面说过,Java对象都有与之关联的一个内部锁和监视器

内部锁是一种排它锁,能够保障原子性、可见性、有序性

从Java语言层面上说,内部锁使用synchronized关键字实现

synchronized可以修饰方法,静态方法和实例方法都可以,也可以修饰一段代码({} 包裹)

image.png

synchronized修饰的方法被叫做同步方法

  • 修饰的静态方法叫做同步静态方法
  • 修饰的实例方法叫做同步实例方法
  • synchronized修饰的代码块(或者一整个方法)就是曾经说过的临界区

synchronized关键字同步机制的使用,需要借助于锁对象

synchronized关键字修饰静态方法,锁对象隐含的是该类的class实例对象;修饰的实例方法隐含的是该对象本身(this)

对于同步代码段,则需要显式的指定锁对象

示例

image.png

注意:

对于锁对象,应该声明为final的

因为如果一旦锁对象发生了变化,那么很可能使用的将不是同一个锁对象,也就失去了同步的意义了,更甚一步,通常声明为private final

如上代码示例,借助于synchronized关键字,就可以实现原子性、可见性、有序性,所以对于该临界区内的代码,必然不会出现线程安全问题

但是这是一种排他锁,也就是对临界区的处理串行化,所以势必影响性能

锁泄漏

对于synchronized来说,这是一种内部锁,对于锁的申请和释放,都是借助于底层实现的,换句话说你只需要使用synchronized关键字即可

底层JVM会帮助我们实现锁的获取与锁的释放,即使出现问题,也会释放锁,所以synchronized的内部锁不存在锁泄露问题

对于锁泄漏,有时候可能是同一个线程持续操作,由于锁的可重入性,所以并不会发现问题,但是对于高并发,这就很可能爆发出来问题了

调度

Java虚拟机会给每个内部锁分配一个入口集 Entry Set,用于记录等待获得内部锁的线程

多个线程竞争时,只会有一个线程获得锁,其他线程获取失败,会进入BLOCKED等待状态,位于入口集的等待区中

锁释放后,会随机的唤醒一个线程,Java虚拟机内部对于内部锁是非公平的,也仅仅支持非公平调度,唤醒的线程可能会跟其他的线程竞争,所以他并不一定可以竞选成功,可能会被再次置入等待状态

这个过程跟前面介绍的监视器的过程是一样的

锁对象的确认

前面提到

synchronized修饰的同步实例方法,锁对象为当前对象本身this;静态方法锁对象为该类型对应的xxx.class对象实例;

这都是隐式的,如何确认?其实很简单

可以定义另外的方法显式的声明锁对象为该对象this或者xxx.class对象实例,对其中一个线程进行sleep,观察显式方法对锁的获取情况,就可以佐证这一结论。

如果是不同的锁的话,将不会收到任何影响,如果是同一个锁就需要进行等待。

同步继承性

synchronized关键字修饰的方法可以进行同步,对于同步方法的继承性是什么样子的?

比如父类中

public synchronized void service();

子类中

@override

public void service();

对于子类中的方法调用,并不会具有同步的特性,所以,一个方法是否具有同步的特性,在于这个方法本身是否有synchronized修饰

同步代码块

synchronized即可以修饰方法,也可以修饰代码块

为什么还要用同步代码块?直接加到方法上多省事儿?

synchronized同步保障了原子性、可见性、有序性,这个内部锁机制是排他的,换言之,相当于部分串行

串行自然可以解决多线程安全问题,如果整个项目全部都是synchronized的方法,那么肯定不会有线程安全问题,但是为什么不这么做?还不是因为性能问题,多核CPU放在那里,难道就只是摆设嘛

既然是相当于串行,很显然,串行化的代码越多,那么效率必然将会越低,所以希望减少非必要的串行化,留给多核机器以及编译器CPU更多的优化空间

所以同步代码块顺势而出

同步代码块保障了更少的“串行化”代码,那么一个方法中,同步代码块之外的代码是如何进行的?是异步的!

进入同步代码块之前会多线程并发,但是一旦执行到同步代码块,将会串行

小结

对于synchronized关键字,从应用层面上来说是非常简单的,就只有代码中的三种样式,但是底层的原理是很复杂的,涉及到JMM以及原子性、可见性、有序性的概念

所以想要学习synchronized,务必要理解这些概念

对于多线程编程来说,synchronized更大程度上来说,更相当于是一个语法糖,底层的机制全部被封装了,如果理解了底层的概念,语法糖的东西,就没什么理解难度

原子性、可见性、有序性是问题根源,JMM是问题解决方案,编译器、JVM底层负责实现,synchronized只是一个关键字而已,但是synchronized却是完全代表了底层的一切

为什么说synchronized关键字修饰的方法(代码段)是线程安全的?那是因为底层的原子性、可见性、有序性的保障。

Java中任何一个对象都有与之关联的内部锁和监视器,所以任何的一个对象都可以用来作为锁对象

所以,借助于synchronized关键字和锁对象,进行合理的安排,你一定可以编写出来正确的并发程序(自身的安排组织不当怪不得synchronized)

common_log.png 转载务必注明出处:程序员潇然,疯狂的字节X,https://crazybytex.com/thread-72-1-1.html

关注下面的标签,发现更多相似文章

文章被以下专栏收录:

    黄小斜学Java

    疯狂的字节X

  • 目前专注于分享Java领域干货,公众号同步更新。原创以及收集整理,把最好的留下。
    包括但不限于JVM、计算机科学、算法、数据库、分布式、Spring全家桶、微服务、高并发、Docker容器、ELK、大数据等相关知识,一起进步,一起成长。
热门推荐
[若依]微服务springcloud版新建增添加一个
[md]若依框架是一个比较出名的后台管理系统,有多个不同版本。
[CXX1300] CMake '3.18.1' was not
[md][CXX1300] CMake '3.18.1' was not found in SDK, PATH, or
java 解析modbus 协议 Modbus4j应用 使用mo
[md]本文记录使用java开发modbus协议程序,与串口进行连接,使用