`

《分布式JAVA应用 基础与实践》 第三章 3.3 JVM线程资源同步及交互机制(一)

阅读更多

3.3  JVM线程资源同步及交互机制

Java程序采用多线程的方式来支撑大量的并发请求处理,程序在多线程方式执行的情况下,复杂程度远高于单线程串行执行的程序。尤其是在多核或多 CPU系统中,多线程执行的程序所带来的最明显的问题是线程之间共同管理的资源的竞争及线程之间的交互。JVM的线程实现及调度方式(抢占式、协作式)取 决于操作系统,超出了本书范围,本节中仅介绍JVM线程资源同步机制和线程之间的交互机制。

3.3.1  线程资源同步机制

首先来看典型获取下一ID的程序:

    int i = 0 ; public int getNextId(){ return i++; }

以下为上面程序在JVM中的执行步骤:

1) JVM首先在main memory(JVM堆) 给i分配一个内存存储场所,并存储其值0;

2)线程启动后,会自动分配一片working memory区(通常是操作数栈),当线程执行到return i++时,JVM中并不是简单的一个步骤就可以完成的。i++动作在JVM中分为装载i、读取i、进行i+1操作、存储i及写入i五个步骤才得以完成。

装载i

线程发起一个装载i的请求给jvm线程执行引擎,引擎接收请求后会向main memory发起一个read i的指令。

当read i执行完毕后,一段时间线程会将i的值从main memory区复制到working memory区中。

读取i

此步负责的是从main memory中读取i。

进行i+1操作

此步由线程完成。

存储i

将i+1的值赋给i,然后存储到working memory中。

写入i

一段时间后i的值会写回到main memory中。

从以上步骤描述来看,这里面最关键的问题有两点:一是working memory中i值和main memory中的i值的同步是需要时间的;二是i++由多个操作组成。只要多个线程在这个时间段内同时执行了操作,就会出现获取i值相同的现象。举个简单 的例子:假设线程A已执行i+1操作,但尚未执行完写入i步骤,线程B就完成了装载i的过程,那么当线程B执行完毕时,其得到的值和A就是一样的了,这是 以上代码在多线程执行时会出现获取相同值的原因。

JVM把对于working memory的操作分为了use、assign、load、store、lock和unlock,对于working memory的操作的指令由线程发出,对于main memory的操作分为了read、write、lock和unlock;对于main memory的操作的指令由线程执行引擎发出 ,其含义分别为:

use

use由线程发起,需要完成将变量的值从working memory中复制到线程执行引擎中。

assign

assign由线程发起,需要完成将变量值复制到线程的working memory中,例如a=i,这时线程就会发起一个assign动作。

load

load由线程发起,需要完成将main memory中read到的值复制到working memory中。

store

store由线程发起,负责将变量的值从working memory中复制到main memory中,并等待main memory通过write动作写入此值。

 

read

read由main memory发起,负责从main memory中读取变量的值。

write

write由main memory发起,负责将working memory的值写入到main memory中。

lock

lock动作由线程发起,同步操作main memory,给对象加上锁。

unlock

unlock动作由线程发起,同步操作main memory,去除对象的锁。

JVM中保证以下操作是顺序的:

1)同一个线程上的操作一定是顺序执行的;

2)对于main memory上的同一个变量的操作一定是顺序执行的,也就是不可能两个请求同时读取变量值;

3)对于加了锁的main memory上的对象操作,一定是顺序执行的,也就是两个以上加了lock的操作,同时肯定只有一个是在执行的。

为了避免资源操作的脏数据问题,JVM提供了synchronized关键字、volatile关键字和lock/unlock机制。

采用synchronized关键字改造上面的代码为:

    public synchronized int getNextId(){ return i++; }

当多线程执行此段代码时,线程A执行到getNextId()方法,JVM知道该方法上有synchronized关键字,于是在执行其他动作前首 先按照对象的实例ID加上一个lock。然后再继续执行return i++,而此时如线程B并发访问getNextId()方法,JVM观察到这个对象的实例ID上有lock,于是将线程B放入等待执行的队列中,只有当线 程A的return i++执行完毕后,JVM才会释放对象实例ID上的lock,重新标记为unlock。这时当下次线程调度到线程B时,线程B才得以执行 getNextId()方法,由于这个过程是串行的,因此可以保证每个线程getNextId都是不一样的值。

synchronized除了可直接写在方法上外,也可直接定义在对象上,区别仅在于JVM会根据这些情况来决定lock标记是打在什么上。需要注意的是,如果synchronized是标记在一个static方法上,那么执行时锁的粒度为当前Class。

lock/unlock机制的原理和synchronized相同,synchronized、lock/unlock机制都可用于保证某段代码执 行的原子性,由于锁会阻塞其他线程同样需要锁的部分的执行,因此在使用锁机制时要避免死锁的产生,尤其是在多把锁的情况下,例如:

    private Object a = new Object(); private Object b = new Object(); public void callAB(){ synchronized(b){ synchronized(a){ // Do something; } } } public void executeAB(){ synchronized(a){ synchronized(b){ // Do something; } } }

当上面的callAB和executeAB被两个线程同时执行时,就会产生死锁的现象,导致系统挂起。这是一个使用synchronized的例 子,对于直接使用lock/unlock来编写的多线程程序而言,一定要保证lock和unlock是成对出现的,并且要保证lock后程序执行完毕时一 定要unlock,否则就有线程会出现锁饿死的现象。

volatile的机制有所不同,它仅用于控制线程中对象的可见性,但并不能保证在此对象上操作的原子性。就像上面i++的场景,即使把i定义为 volatile也是没用的,对于定义为volatile的变量,线程不会将其从main memory复制到work memory中,而是直接在main memory中进行操作。它的代价虽然比synchronized、lock/unlock低,但用起来要非常小心,毕竟它不能保证操作的原子性。

有了synchronized、lock/unlock及volatile后,在Java中就可以很好地控制多线程程序中资源的竞争。但由于这三个机制对于系统的性能都是有影响的,例如会将操作由并行化变为串行化,因此需要根据合理的需求对线程中的资源进行同步。

 

 

分享到:
评论

相关推荐

    Java分布式应用学习笔记03JVM对线程的资源同步和交互机制

    ### Java分布式应用学习笔记03:JVM对线程的资源同步和交互机制 在深入探讨Java虚拟机(JVM)如何处理线程间的资源同步与交互机制之前,我们先来明确几个关键概念:线程、多线程、同步、并发以及它们在Java中的实现...

    构建高性能的大型分布式java应用

    **3.3 JVM线程机制** JVM中的线程管理涉及到线程的创建、调度、同步和死锁等问题。Java提供了丰富的API来管理和控制线程行为,包括同步工具类、线程池等。 #### 第四章:深入理解JDK **4.1 集合** Java集合框架...

    分布式java应用

    分布式Java应用是现代软件开发中的一个重要领域,尤其是在大型企业级系统和互联网...《分布式java应用》这本书可能涵盖了以上的一些或全部内容,对于想要深入理解和实践分布式Java应用的开发者来说,是一份宝贵的资源。

    java分布式应用总结.pdf

    - **线程资源同步与交互**:涉及synchronized、wait/notify、Lock接口、信号量等并发控制手段,以及线程的状态和管理。 4. **Java类库**: - **集合包**:ArrayList、LinkedList、Vector、Stack等基本容器的特性...

    java 核心知识 包含 JVM 线程 集合 数据库 算法 负载等一系列

    Java是世界上最流行的编程语言之一,尤其在企业级应用开发领域占据主导地位。本文将深入探讨Java的核心知识,包括JVM(Java虚拟机)、线程、集合、数据库、算法以及负载均衡等多个方面。 首先,让我们从Java虚拟机...

    Java分布式应用学习笔记-谈JVM.doc

    在Java分布式应用中,理解JVM的运作机制能够帮助开发者优化代码,减少资源消耗,提高系统的稳定性和效率。深入研究JVM的内存管理、类加载、编译和执行策略,对于构建高性能的分布式系统至关重要。因此,无论是开发...

    java分布式应用程序设计 pdf+源码

    本资源包含一本名为“JAVA分布式程序设计”的电子书以及相关的源码示例,旨在帮助开发者深入理解和实践Java在分布式环境中的应用。 《JAVA分布式程序设计》这本书可能涵盖了以下几个核心知识点: 1. **分布式系统...

    Java 分布式应用程序设计

    在Java分布式应用程序设计中,我们...以上各章内容共同构成了Java分布式应用程序设计的基础,涵盖了从基础网络编程到复杂分布式系统架构的诸多方面。通过学习和实践,开发者可以掌握构建高效、可靠的分布式系统的技能。

    Java网络编程与分布式计算.pdf

    Java网络编程和分布式计算是Java语言中非常重要的技术领域,它们让Java程序员能够开发出可以跨网络进行通信和协作的应用程序。Java作为一种成熟的编程语言,拥有强大的网络和分布式计算能力,这得益于其丰富的API和...

    Java分布式开发与集群技术

    在当前信息技术迅速发展的背景下,Web服务器和移动互联网软件开发中,对分布式开发和集群技术的需求日益增长...在具体实践中,开发者需要根据应用的需求,结合Java的相关技术,构建出稳定、高效和可扩展的分布式系统。

    一种基于JAVA语言的分布式应用服务模式.pdf

    Java语言的多线程能力是构建分布式应用服务模式的一大优势。它可以将任务分解为独立的线程,提升并发处理能力,确保在分布式网络中各节点间高效交互。服务动态加载是Java在此领域的核心应用,允许服务对象根据需求...

    java网络编程与分布式计算

    10. **RMI(远程方法调用)**:Java的RMI机制使得Java对象可以在不同的JVM之间进行交互,实现分布式服务。 11. **JMS(Java消息服务)**:用于在分布式环境中发送和接收消息,是解耦和异步处理的关键技术。 12. **...

    elecfans.com-Java网络编程与分布式计算

    Java网络编程与分布式计算是Java开发中的重要领域,它涵盖了多方面的重要概念和技术。Java语言以其平台无关性和强大的网络支持,成为了实现网络应用和分布式系统的主要工具。在本专题中,我们将深入探讨Java如何处理...

    Java_network_programming_and_distributed_data.rar_JAVA分布式_java p

    - **RMI(Remote Method Invocation)**:Java的远程方法调用机制,允许在不同 JVM 间的对象进行透明调用,实现分布式系统中的对象交互。 - **JMS(Java Message Service)**:消息队列服务,支持异步通信,是...

    Java网络编程与分布式计算

    Java作为一种广泛使用的编程语言,其在网络编程和分布式计算方面的强大能力使得开发者能够创建跨越多个计算机节点的应用程序,实现数据共享和任务并行处理。 一、Java网络编程基础 Java网络编程主要涉及Socket编程...

    java JVM详解

    SUN JVM 提供了详细的内存管理机制,旨在优化 Java 应用程序的性能。内存管理的关键部分包括: - **Heap (堆)**:用于存储 Java 对象的实例和数组。 - **Method Area (方法区域)**:存储已加载类的信息、常量、静态...

    2022java面试题、JVM面试题、多线程面试题、并发编程、

    上实现了跨平台性,因为字节码是一种与硬件平台无关的中间代码,可以在任何支持Java虚拟机(JVM)的平台上被执行。字节码的主要优点包括: 1. 跨平台性:字节码使得Java程序无需针对不同操作系统或硬件进行重新编译...

    Java线程迁移机制的研究

    ### Java线程迁移机制的研究 #### 一、引言 随着Java移动代理技术和集群技术的发展,Java线程迁移成为了一个重要的研究领域。线程迁移在Java移动代理系统中用于将代理线程从一个节点转移到另一个节点,在Web包容器...

Global site tag (gtag.js) - Google Analytics