当前位置:首页 > 后端开发 > Java多线程:synchronized的可重入性

Java多线程:synchronized的可重入性

6个月前 (05-27)19

Java多线程:线程间通信之volatile与sychronized这篇文章中我们了解了synchronized的基本特性,知道了一旦有一个线程访问某个对象的synchronized修饰的方法或代码区域时,该线程则获取这个对象的锁,其他线程不能再调用该对象被synchronized影响的任何方法。那么,如果这个线程自己调用该对象的其他synchronized方法,Java是如何判定的?这就涉及到了Java中锁的重要特性:可重入性,也就是今天的主题。

1. 线程安全与可重入性

在回答引言的问题前,我们先讲解一下可重入性。在线程这块知识中,可重入性常常和线程安全进行对比。

1.1. 线程安全

线程安全函数的概念比较直观,众所周知,同一进程的不同线程会共享同一主内存,线程的私有栈中只包括PC,栈,操作数栈,局部变量数组和动态链接。对共享内存进行读写时,若要保证线程安全,则必须通过加锁的方式。

1.2. 可重入

1.2.1. 定义

关于可重入这一概念,我们需要参考维基百科。

若一个程序或子程序可以“在任意时刻被中断然后操作系统调度执行另外一段代码,这段代码又调用了该子程序不会出错”,则称其为可重入(reentrant或re-entrant)的。即当该子程序正在运行时,执行线程可以再次进入并执行它,仍然获得符合设计时预期的结果。与多线程并发执行的线程安全不同,可重入强调对单个线程执行时重新进入同一个子程序仍然是安全的。

1.2.2. 可重入的条件

  • 不在函数内使用静态或全局数据。
  • 不返回静态或全局数据,所有数据都由函数的调用者提供。
  • 使用本地数据(工作内存),或者通过制作全局数据的本地拷贝来保护全局数据。
  • 不调用不可重入函数。

1.3. 可重入与线程安全

一般而言,可重入的函数一定是线程安全的,反之则不一定成立。在不加锁的前提下,如果一个函数用到了全局或静态变量,那么它不是线程安全的,也不是可重入的。如果我们加以改进,对全局变量的访问加锁,此时它是线程安全的但不是可重入的,因为通常的枷锁方式是针对不同线程的访问(如Java的synchronized),当同一个线程多次访问就会出现问题。只有当函数满足可重入的四条条件时,才是可重入的。

2. synchronized的可重入性

2.1. synchronized是可重入锁

回到引言里的问题,如果一个获取锁的线程调用其它的synchronized修饰的方法,会发生什么?

从设计上讲,当一个线程请求一个由其他线程持有的对象锁时,该线程会阻塞。当线程请求自己持有的对象锁时,如果该线程是重入锁,请求就会成功,否则阻塞。

我们回来看synchronized,synchronized拥有强制原子性的内部锁机制,是一个可重入锁。因此,在一个线程使用synchronized方法时调用该对象另一个synchronized方法,即一个线程得到一个对象锁后再次请求该对象锁,是永远可以拿到锁的

在Java内部,同一个线程调用自己类中其他synchronized方法/块时不会阻碍该线程的执行,同一个线程对同一个对象锁是可重入的,同一个线程可以获取同一把锁多次,也就是可以多次重入。原因是Java中线程获得对象锁的操作是以线程为单位的,而不是以调用为单位的。

2.2. synchronized可重入锁的实现

之前谈到过,每个锁关联一个线程持有者和一个计数器。当计数器为0时表示该锁没有被任何线程持有,那么任何线程都都可能获得该锁而调用相应方法。当一个线程请求成功后,JVM会记下持有锁的线程,并将计数器计为1。此时其他线程请求该锁,则必须等待。而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增。当线程退出一个synchronized方法/块时,计数器会递减,如果计数器为0则释放该锁。

3. 参考文章

可重入函数与线程安全函数

可重入

java内置锁synchronized的可重入性

作者:CieloSun
来源链接:https://www.cnblogs.com/cielosun/p/6684775.html

“Java多线程:synchronized的可重入性” 的相关文章

Java多线程看这一篇就足够了(吐血超详细总结)

Java多线程看这一篇就足够了(吐血超详细总结)

进程与线程 进程是程序的一次动态执行过程,它需要经历从代码加载,代码执行到执行完毕的一个完整的过程,这个过程也是进程本身从产生,发展到最终消亡的过程。多进程操作系统能同...

Java对象的四种引用

前言 在JDK1.2之前,创建的对象只有在处于可触及(reachable)的状态下,才能被程序使用。也就是说,若一个对象不被任何变量引用,那么...

Java常用集合源码级深度解析

Java常用集合源码级深度解析

  Java集合工具包位于Java.util包下,包含了很多常用的数据结构,如数组、链表、栈、队列、集合、哈希表等。学习Java集合框架下大致可以分为如下五个...

通俗易懂的java多线程(又干货又可爱哦)

通俗易懂的java多线程(又干货又可爱哦)

文章目录 一、前言 二、线程概述 2.1 概述 2.2 并...

JAVA多线程及线程状态转换

JAVA多线程及线程状态转换

以下内容整理自:http://blog.csdn.net/wtyvhreal/article/details/44176369 线程:是指进程中...

Java (JDK 多版本切换)—— Windows平台

Java (JDK 多版本切换)—— Windows平台

0. 背景     常常在不同的应用中需要用到不同版本的Java ,需要切换不同JAVA_HOME. 1. 方法    Step...

Java对多线程的理解

Java对多线程的理解

线程是依赖于进程 线程:是进程中的单个顺序,是一条执行路径。 一、继承Thread类 Thread类是...

Java集合,集合的方法,集合的遍历

Java集合,集合的方法,集合的遍历

package com.atgui.java; import org.junit.Test; import java.u...

【Java】把一个对象转化为String字符串

把一个对象obj转化为字符串,我有三个想法。 obj + ""; obj.toString(); String.value...

java中23种常用设计模式

面向对象语言的三大特点: 封装 集成 多态 面向对象的六大原则: 单一职责原则 开闭原则 里氏替换...