`
春花秋月何时了
  • 浏览: 41825 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

Java内存模型JMM之六深入理解synchronized(2)

 
阅读更多

本文承接未完待续的 Java内存模型JMM之六深入理解synchronized(1)

3.7 锁消除

消除同步锁是JVM另外一种锁的优化,这种优化更彻底, JVM通过运行时JIT编译(可以理解为当某段代码即将第一次被执行时进行编译,又称即时编译),对一些在代码上要求添加同步,但是通过数据逃逸技术分析发现不可能存在共享数据竞争,这时JVM会对这些没有必要的同步锁进行消除。所以锁消除可以节省毫无意义的请求锁的时间,那么明知不存在数据竞争为何还进行了同步操作?作为程序员来说,这的确不会写出这样的代码,但是有时候程序并不是我们所想的那样,我们虽然没有显示的使用同步锁,但是我们在使用一些JDK内置的API时,如StringBuffer、Vector、HashTable等,这个时候会存在隐形的同步加锁操作。比如StringBuffer的append()方法,Vector的add()方法。

public void add(String str1, String str2) {
	//StringBuffer是线程安全,由于sb只会在append方法中使用,不可能被其他线程引用
	//因此sb属于不可能共享的资源,JVM会自动消除内部的锁
	StringBuffer sb = new StringBuffer();
	sb.append(str1)
	  .append(str2);
}

 以上代码很常见吧,特别是在拼接一些SQL、HQL等操作的时候, 由于该处的sb对象是一个局部变量,并且不会被其他线程引用到,但是StringBuffer的append方法却是个同步方法,此时,JVM就会通过锁消除机制将其锁消除。

 

3.7 锁粗化

原则上,我们在编写代码的时候,总是推荐奖同步块的作用范围限制的尽量小,只在共享数据的实际作用域中才进行同步,这是为了使得需要同步的操作尽可能的少,如果存在锁竞争也能够使等待锁的线程能够尽快地拿到锁。大部分情况下,这样的原则是没有问题的,但是如果一系列的连续操作都是对同一个锁的反复加锁和解锁,甚至在循环体中进行反复的加解锁操作,这样的情况即使没有线程竞争,频繁的进行互斥同步操作也会导致不必要的性能损耗。

锁粗化就是将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁,如下代码示例:

 

public void vectorTest(){
	Vector<String> vector = new Vector<String>();
	for(int i = 0 ; i < 10 ; i++){
		vector.add(i + "");
	}

	System.out.println(vector);
}
以上代码是很常见的在循环体中进行集合元素添加操作,我们知道vector的add方法是一个同步方法,每次add都需要加锁,执行完之后又需要解锁操作, 这时候JVM检测到对同一个对象(vector)连续加锁、解锁操作,就会合并一个更大范围的加锁、解锁操作,即将加锁解锁操作移到for循环之外,以一次性的加解锁操作取代多次的加解锁操作。

 

3.8 synchronized不能被interrupt中断

线程的中断方法说的是,只能中断正处于阻塞状态或者正准备执行一个阻塞操作的线程,这里说的阻塞其实就是指调用了Join, wait, sleep等方法导致线程进入的WAITING/TIMED_WAITING状态,因为这些方法能够感知到中断操作从而抛出中断异常。而所谓的synchronized不能被interrupt方法中断,指的是当线程执行到synchronized方法或者代码块的时候,由于对象锁被占用导致线程不得不通过调用底层的park方法进入死等状态,这时线程其实也是进入了阻塞状态,但是由于没有显示的调用能够抛出中断异常的方法,从而导致被阻塞的线程不能从阻塞状态恢复过来,使得线程一直处于锁等待的阻塞状态。如下代码示例可以作为一种说明:

public class ThreadTest implements Runnable{
	
	public ThreadTest() {
		//该线程已持有当前实例锁
        new Thread() {
            public void run() {
                f(); // Lock acquired by this thread
            }
        }.start();
	}

	public synchronized void f() {
        System.out.println("Trying to call f()");
        while(true) // Never releases lock
            Thread.yield();
    }
	
	public void run() {
        //中断判断
        while (true) {
            if (Thread.interrupted()) {
                System.out.println("中断线程!!");
                break;
            } else {
                f();
            }
        }
    }

	public static void main(String[] args) throws InterruptedException {
		Thread t = new Thread(new ThreadTest()); 
		t.start();
        TimeUnit.SECONDS.sleep(1);
        //中断线程,无法生效
        t.interrupt();
	}

}

 在创建ThreadTest示例时,其构造方法里面立即就执行了synchronized方法f,f方法会导致其一直持有对象锁,当线程t被创建出来也调用f方法的时候,将会一直处于锁等待状态,也是阻塞状态,虽然后来调用了interrupt中断方法,但是线程t并不能被中断,因为没有可以抛出中断异常的方法,线程t只能一直等待直到获取到对象锁。

 

3.9 synchronized锁及锁优化总结

原理 优点 缺点 适用场景
偏向锁 如果一个线程获得了锁,那么该锁就进入偏向模式,当这个线程再次请求锁时,无需再做任何同步操作  省去重复获取/释放锁的开销 如果竞争激烈,会带来额外的锁撤销的消耗 大多数时候都只有一个相同的线程,并且多次访问同步块的场景,不存在竞争
轻量级锁 仅仅通过CAS指令和自旋操作达到锁的获取和释放 避免了线程从用户态切换到内核态的消耗,线程并不会被阻塞,提高了程序的响应速度 如果存在锁竞争,将会通过自旋消耗CPU 不存在竞争,多线程之间交替执行,并且同步块执行较快的场景
重量级锁 通过操作系统底层的实现,阻塞竞争的线程和唤醒被阻塞的线程 比起其它情况,几乎没有优点 线程阻塞/唤醒需要在用户态和内核态之间切换,消耗大量成本,并且由于阻塞,使线程响应变慢 竞争激烈,同步代码块逻辑复杂需占用大量时间的场景
自旋/自适用自旋 通过执行多次无意义的指令,避免不必要的挂起和恢复线程时状态转换造成的性能消耗 避免了线程从用户态切换到内核态的消耗 白白消耗CPU 轻量级锁和重量级锁产生竞争时,在切换到内核态之前
锁消除 通过数据逃逸技术分析对不存在数据共享的同步锁实施锁消除 使其消除了不必要的获取/释放锁的消耗 还好吧,完全由JVM自行完成,依赖JVM的JIT编译 JVM的即时编译,针对JDK底层的API
锁粗化 将多次对同一个锁对象的获取/释放操作,扩展为一次获取/释放操作 省去重复获取/释放锁的开销 完全依赖JVM自身 将同步块置于循环操作中的场景

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    深入理解Java内存模型 pdf 超清版

    深入理解Java内存模型对于编写高效的并发程序至关重要。本文将详细介绍JMM的核心概念、工作原理以及相关的编程实践。 1. **核心概念** - **线程私有区域**: 包括程序计数器、虚拟机栈、本地方法栈,这些区域中的...

    Java内存模型详解JMM.docx

    JMM的目的之一是为了提供一个跨平台的内存模型,使得Java开发者可以更好地理解Java语言的语言特性和内存相关的内容。JMM的概念包括堆、栈、本机内存、防止内存泄漏等方面。 JSR133是JMM的一部分,它是Java语言规范...

    深入理解 Java 内存模型

    《深入理解 Java 内存模型》这本书由程晓明编著,旨在帮助开发者深入理解和应用 JMM。 1. **内存层次结构**:Java 内存模型将内存分为堆内存、栈内存、方法区(在 Java 8 及以后版本中被元空间替代)和程序计数器等...

    深入理解java内存模型

    这本书"深入理解Java内存模型"显然是为了帮助读者深入探讨这个主题,以便更好地理解和解决并发编程中的问题。 Java内存模型主要涉及以下几个核心概念: 1. **主内存**:所有线程共享的数据存储区域,包括类的静态...

    深入Java内存模型-JMM

    Java内存模型,简称JMM(Java Memory Model),是Java虚拟机规范中定义的一个抽象概念,它描述了在多线程环境下,如何保证各个线程对共享数据的一致性视图。JMM的主要目标是定义程序中各个变量的访问规则,以及在...

    深入理解 Java 内存模型_程晓明_InfoQ_java_内存模型_

    深入理解Java内存模型可以帮助开发者避免并发编程中常见的问题,如数据竞争、死锁和活锁等。通过合理地使用同步机制,可以编写出高效且线程安全的代码,这对于大型分布式系统和高并发应用尤为重要。 总之,Java内存...

    深入理解Java内存模型(经典).rar

    在《深入理解Java内存模型(经典)》这本书中,作者可能详细探讨了JMM的原理、规则以及在实际编程中的应用,包括案例分析和最佳实践。通过阅读这本书,开发者可以更深入地掌握Java并发编程的核心技术,提高程序的...

    深入理解JAVA内存模型(高清完整版)

    本教程《深入理解JAVA内存模型》将带你深入探讨这一主题,尤其关注Java中的同步原语——synchronized、volatile和final。 首先,我们要了解JMM的基础结构。JMM规定了程序中各个线程如何访问和修改共享变量,包括主...

    深入理解Java内存模型

    在深入理解Java内存模型之前,我们首先需要了解并发编程中的两个基本问题:线程间的通信和线程间的同步。 在并发编程中,线程间的通信是指线程之间如何交换信息,主要有两种并发模型。共享内存并发模型中,线程之间...

    java内存模型JMM(Java Memory Model)1

    Java内存模型(JMM,Java Memory Model)是Java平台中用于描述如何在多线程环境中管理内存的一套规范。它确保了并发编程时不同线程之间的数据一致性、可见性和原子性,以避免出现数据竞争和其他并发问题。以下是JMM...

    深入理解Java内存模型(二)共3页.pdf.zip

    Java内存模型,简称JMM(Java Memory Model),是Java虚拟机规范中定义的一个抽象概念,它...通过阅读"深入理解Java内存模型(二)共3页.pdf.zip"中的内容,开发者可以进一步了解JMM的细节,解决并发编程中的复杂问题。

    java内存模型文档

    这些文档如"Java内存模型.docx"、"Java内存模型2.docx"、"深入Java核心 Java内存分配原理精讲.docx"、"java内存模型.pdf"将深入探讨这些概念,帮助开发者更深入地理解Java内存模型及其在实际编程中的应用。...

    深入理解Java虚拟机(jvm性能调优+内存模型+虚拟机原理).zip

    《深入理解Java虚拟机》是一本深度探讨Java虚拟机(JVM)的著作,涵盖了JVM性能调优、内存模型以及虚拟机原理等多个关键领域。本文将基于这些主题,详细阐述其中的重要知识点。 首先,我们要了解Java虚拟机(JVM)...

    深度剖析java内存模型

    Java内存模型(Java Memory Model,简称JMM)是Java虚拟机(JVM)规范中定义的一...深入理解JMM的运作机制,合理使用Java提供的同步机制和并发工具类,才能有效解决多线程编程中的各种问题,编写出安全可靠的并发程序。

    java内存模型.pdf

    Java内存模型(JMM)是Java虚拟机(JVM)的一部分,它定义了程序中不同变量如何交互,特别是在多线程环境下。JMM确保了在各种硬件和操作系统平台上,Java程序的行为具有一致性和可预测性。Java内存模型的主要目标是...

    深入理解JAVA内存模型。。

    总结来说,深入理解Java内存模型能帮助我们更好地掌握Java并发编程的底层原理,编写出高效、无错的并发代码,同时也有利于进行JVM的性能优化。通过阅读"深入理解JAVA内存模型.pdf",可以系统学习和掌握这些关键知识...

    Java 内存模型

    总之,深入理解Java内存模型对于编写高效、线程安全的Java程序至关重要。这需要我们熟悉JMM中的核心概念,如可见性、有序性和原子性,并掌握相关的同步工具。同时,理解JVM的内存管理,包括垃圾收集的工作原理,也是...

Global site tag (gtag.js) - Google Analytics