您的位置 首页 java

Java虚拟机如何执行线程同步

所有 Java 程序都被编译成类文件,这些文件包含字节码,即Java 虚拟机 的机器语言。本文介绍了Java虚拟机如何处理线程同步,包括相关的字节码。(1,750字)

本月的 Under The Hood 研究了Java语言和Java虚拟机(JVM)中的线程同步。本文是我去年夏天开始的一系列字节码文章中的最后一篇。它描述了仅两个与 线程 同步直接相关的操作码,即用于进入和退出监视器的操作码。

线程和共享数据

Java编程语言的优势之一是它在语言级别上支持多线程。这种支持的大部分集中在协调对多个线程之间共享的数据的访问上。

JVM将正在运行的Java应用程序的数据组织到几个运行时数据区域中:一个或多个Java堆栈,堆和方法区域。有关这些内存区域的背景知识,请参阅第一篇“ 高级技术” 文章:“ 精益,精简的虚拟机”。

在Java虚拟机内部,每个线程都被授予一个Java堆栈,该堆栈包含其他线程无法访问的数据,包括该线程调用的每个方法的局部变量,参数和返回值。堆栈上的数据仅限于基本类型和对象引用。在JVM中,不可能将实际对象的映像放置在堆栈上。所有对象都驻留在堆上。

JVM内部只有一个堆,并且所有线程都共享它。堆只包含对象。无法将单独的原始类型或对象引用放在堆上-这些东西必须是对象的一部分。数组驻留在堆上,包括原始类型的数组,但是在Java中,数组也是对象。

除了Java堆栈和堆之外,其他地方的数据可能位于JVM中的method区域,该区域包含程序使用的所有类(或静态)变量。方法区域与堆栈类似,因为它仅包含基本类型和对象引用。但是,与堆栈不同,方法区域中的类变量由所有线程共享。

对象和类锁

如上所述,Java虚拟机中的两个内存区域包含所有线程共享的数据。这些是:

  • 堆,其中包含所有对象
  • 方法区域,其中包含所有类变量

如果多个线程需要同时使用相同的对象或类变量,则必须正确管理它们对数据的访问。否则,程序将具有不可预测的行为。

为了协调多个线程之间的共享数据访问,Java虚拟机将锁与每个对象和类关联。锁就像特权,一次只能有一个线程“拥有”。如果线程想要锁定特定的对象或类,它将询问JVM。在线程向JVM请求锁定之后的某个时刻-可能很快,也许以后,甚至可能永远不会-JVM将锁定提供给线程。当线程不再需要锁时,它将锁返回给JVM。如果另一个线程请求了相同的锁,则JVM将锁传递给该线程。

类锁实际上是作为对象锁实现的。JVM加载类文件时,将创建class的实例java.lang.Class。当您锁定一个类时,实际上是在锁定该类的Class对象。

线程无需获取锁即可访问实例或类变量。但是,如果某个线程确实获得了锁,则只有拥有该锁的线程将其释放,其他线程才能访问该锁数据。

监控器

JVM将锁与监视器结合使用。监视器本质上是监护人,因为它监视一系列代码,并确保一次仅执行一个线程。

每个监视器都与一个对象引用关联。当线程到达监视器监视下的代码块中的第一条指令时,该线程必须获得对引用对象的锁定。在线程获得锁之前,不允许其执行代码。一旦获得了锁,线程便进入受保护的代码块。

当线程离开该块时,无论它如何离开该块,它都会释放关联对象上的锁。

多重锁

允许单个线程多次锁定同一对象。对于每个对象,JVM都会对对象被锁定的次数进行计数。一个未锁定的对象的计数为零。当线程首次获取锁时,计数将增加为一。线程每次在同一对象上获取锁时,计数都会增加。每次线程释放锁定时,计数都会减少。当计数达到零时,将释放该锁并将其提供给其他线程。

同步块

在Java语言术语中,必须访问共享数据的多个线程的协调称为“ 同步”。该语言提供了两种内置的方法来同步对数据的访问:使用同步语句或同步方法。

同步语句

要创建同步语句,请将该 synchronized 关键字与一个表达式求值,该表达式的结果为对象引用,如以下reverseOrder()方法所示:

 class KitchenSync {
    private int[] intArray = new int[10];
    void reverseOrder() {
        synchronized (this) {
            int halfWay = intArray.length / 2;
            for (int i = 0; i < halfWay; ++i) {
                int upperIndex = intArray.length - 1 - i;
                int save = intArray[upperIndex];
                intArray[upperIndex] = intArray[i];
                intArray[i] = save;
            }
        }
    }}  

在上述情况下,直到在当前对象(this)上获得了锁定,才会执行同步块中包含的语句。如果this表达式代替了引用而产生了对另一个对象的引用,则在线程继续之前将获取与该对象关联的锁。

两种操作码monitorenter和和monitorexit用于方法中的同步块,如下表所示。

表1.监视器

操作码操作数描述 monitorenter没有弹出objectref,获取与objectref相关联的锁monitorexit没有弹出objectref,释放与objectref相关的锁

当monitorenterJava虚拟机遇到它时,它将获取堆栈上由objectref引用的对象的锁。如果线程已经拥有该对象的锁,则计数增加。每次monitorexit为对象上的线程执行时,计数都会减少。当计数达到零时,将释放监视器。

看一看由该类的方法生成的字节码序列。reverseOrder()KitchenSync

请注意,即使从同步块内引发异常,catch子句也可以确保锁定的对象将被解锁。无论同步块如何退出,线程进入该块时所获取的对象锁定都将被释放。

同步方法

要同步整个方法,只需将synchronized关键字作为方法限定符之一,如下所示:

 class HeatSync {
    private int[] intArray = new int[10];
    synchronized void reverseOrder() {
        int halfWay = intArray.length / 2;
        for (int i = 0; i < halfWay; ++i) {
            int upperIndex = intArray.length - 1 - i;
            int save = intArray[upperIndex];
            intArray[upperIndex] = intArray[i];
            intArray[i] = save;
        }
    }}  

JVM不使用任何特殊的操作码来调用同步方法或从同步方法返回。当JVM解析对方法的符号引用时,它将确定该方法是否同步。如果是,则JVM在调用该方法之前获取一个锁。对于实例方法,JVM获取与在其上调用该方法的对象关联的锁。对于类方法,它获取与该方法所属的类相关联的锁。同步方法完成后,无论是通过返回还是引发异常来完成,都将释放锁。

下个月来

现在,我已经遍历了整个字节码指令集,我将扩大本专栏的范围,以涵盖Java技术的各个方面或应用程序,而不仅仅是Java虚拟机。下个月,我将开始一个由多个部分组成的系列文章,其中提供了Java安全模型的深入概述。

Bill Venners从事专业软件编写已有12年了。他位于硅谷,以Artima Software Company的名义提供软件咨询和培训服务。多年来,他为消费电子,教育,半导体和人寿保险行业开发了软件。他在许多平台上用多种语言编程:各种微处理器上的汇编语言,Unix上的C,Windows上的C ++,Web上的Java。他是McGraw-Hill出版的《 Java虚拟机内部》一书的作者。

了解有关此主题的更多信息

  • Tim Lindholm和Frank Yellin(ISBN 0-201-63452-X)所著的《 Java虚拟机规范 》一书(),是Java系列( Addison-Wesley的)是权威的Java虚拟机参考。
  • 以前的“引擎盖下”文章:
  • “ 精益,卑鄙的虚拟机 ”对Java虚拟机进行了介绍。
  • “ Java类文件的生活方式 ”概述了Java类文件,即所有Java程序都编译成的文件格式。
  • “ Java的垃圾收集堆 ”概述了一般的垃圾收集,特别是Java虚拟机的垃圾收集堆。
  • “ 字节码基础 ”介绍Java虚拟机的字节码,并特别讨论基本类型,转换操作和堆栈操作。
  • 浮点 运算法则 ”描述Java虚拟机的浮点支持以及执行浮点操作的字节码。
  • “ 逻辑和算术 ”描述Java虚拟机对逻辑和整数算术以及相关字节码的支持。
  • “ 对象和数组 ”描述Java虚拟机如何处理对象和数组,并讨论相关的字节码。
  • “ 异常 ”描述Java虚拟机如何处理异常,并讨论相关的字节码。
  • “ Try-Finally ”描述Java虚拟机如何实现try-finally子句,并讨论相关的字节码。
  • “ 控制流 ”描述Java虚拟机如何实现控制流并讨论相关的字节码。
  • “ Aglets的体系结构 ”描述了Aglets(IBM的基于Java的自主软件代理技术)的内部工作原理。
  • “ Aglets的要点 ”分析了移动代理的实际实用程序,例如Aglets,IBM的基于Java的自主软件代理技术。
  • “ 方法调用和返回 ”介绍了Java虚拟机如何从方法(包括相关的字节码)中调用和返回。

文章来源:智云一二三科技

文章标题:Java虚拟机如何执行线程同步

文章地址:https://www.zhihuclub.com/186338.shtml

关于作者: 智云科技

热门文章

网站地图