Java线程并发中常见的锁--自旋锁 偏向锁

摘要:
本文着重介绍了在java并发中常见的几种锁机制。由于BasicObjectLock对象在线程栈中,因此该指针必然指向持有该锁的线程栈空间。若线程依然不能获得锁,才会被挂起。使用自旋锁后,线程被挂起的几率相对减少,线程执行的连贯性相对加强。

随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题。本文着重介绍了在java并发中常见的几种锁机制。

1.偏向锁

偏向锁是JDK1.6提出来的一种锁优化的机制。其核心的思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作。也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操作了,从而节约了操作时间,如果在此之间有其他的线程进行了锁请求,则锁退出偏向模式。在JVM中使用-XX:+UseBiasedLocking

复制代码
复制代码
package jvmProject;

import java.util.List;
import java.util.Vector;

public class Biased {

public static List<Integer> numberList = new Vector<Integer>();
public static void main(String[] args) {
    long begin = System.currentTimeMillis();
    int count = 0;
    int startnum = 0;
    while(count<10000000){
        numberList.add(startnum);
        startnum+=2;
        count++;        
    }
    long end = System.currentTimeMillis();
    System.out.println(end-begin);
}

}

复制代码
复制代码

初始化一个Vector,往里面添加10000000个Integer对象,然后输出时间差。以此来测试偏向锁的性能。至于为什么要使用Vector而不使用ArrayList呢?

因为ArrayList是线程不安全的,Vector是线程安全的。这样说可能还不够具体,可以翻看一下源码吧。

Java线程并发中常见的锁--自旋锁 偏向锁第5张Java线程并发中常见的锁--自旋锁 偏向锁第6张

Vector中的几乎所有操作是带有sychronized的,而ArrayList是没有的,所以Vector是线程安全的。

接下来我们来测试一下,开启偏向锁和不开启偏向锁对程序性能的影响有多大。

配置JVM启动(开启偏向锁)参数为:

Java线程并发中常见的锁--自旋锁 偏向锁第7张

Java线程并发中常见的锁--自旋锁 偏向锁第8张

配置JVM启动(关闭偏向锁)参数为:

Java线程并发中常见的锁--自旋锁 偏向锁第9张

Java线程并发中常见的锁--自旋锁 偏向锁第10张

Perfect!开启偏向锁的程序运行时间明显较短,开启偏向锁比不开启偏向锁,在单个线程中操作一个对象的同步方法,是有一定的优势的。其实也可以这样理解,当只有一个线程操作带有同步方法的Vector对象的时候,此时对Vector的操作就转变成了对ArrayList的操作。

偏向锁在锁竞争激烈的场合没有太强的优化效果,因为大量的竞争会导致持有锁的线程不停地切换,锁也很难保持在偏向模式,此时,使用偏向锁不仅得不到性能的优化,反而有可能降低系统的性能,因此,在激烈竞争的场合,可以尝试使用

-XX:-UseBiastedLocking参数禁用偏向锁。

2.轻量级锁

如果偏向锁失败,Java虚拟机就会让线程申请轻量级锁,轻量级锁在虚拟机内部,使用一个成为BasicObjectLock的对象实现的,这个对象内部由一个BasicLock对象和一个持有该锁的Java对象指针组成。BasicObjectLock对象放置在Java栈帧中。在BasicLock对象内部还维护着displaced_header字段,用于备份对象头部的Mark Word.

当一个线程持有一个对象的锁的时候,对象头部Mark Word信息如下

[ptr |00] locked

末尾的两位比特为00,整个Mark Word为指向BasicLock对象的指针。由于BasicObjectLock对象在线程栈中,因此该指针必然指向持有该锁的线程栈空间。当需要判断一个线程是否持有该对象时,只需要简单地判断对象头的指针是否在当前线程的栈地址范围即可。同时,BasicLock对象的displaced_header,备份了原对象的Mark word内容,BasicObjectLock对象的obj字段则指向持有锁的对象头部。

3.重量级锁

当轻量级锁失败,虚拟机就会使用重量级锁。在使用重量级锁的时,对象的Mark Word如下:

[ptr |10] monitor

重量级锁在操作过程中,线程可能会被操作系统层面挂起,如果是这样,线程间的切换和调用成本就会大大提高。

4.自旋锁

自旋锁可以使线程在没有取得锁的时候,不被挂起,而转去执行一个空循环,(即所谓的自旋,就是自己执行空循环),若在若干个空循环后,线程如果可以获得锁,则继续执行。若线程依然不能获得锁,才会被挂起。

使用自旋锁后,线程被挂起的几率相对减少,线程执行的连贯性相对加强。因此,对于那些锁竞争不是很激烈,锁占用时间很短的并发线程,具有一定的积极意义,但对于锁竞争激烈,单线程锁占用很长时间的并发程序,自旋锁在自旋等待后,往往毅然无法获得对应的锁,不仅仅白白浪费了CPU时间,最终还是免不了被挂起的操作 ,反而浪费了系统的资源。

在JDK1.6中,Java虚拟机提供-XX:+UseSpinning参数来开启自旋锁,使用-XX:PreBlockSpin参数来设置自旋锁等待的次数。

在JDK1.7开始,自旋锁的参数被取消,虚拟机不再支持由用户配置自旋锁,自旋锁总是会执行,自旋锁次数也由虚拟机自动调整。

http://www.cnblogs.com/think-in-java/p/5520462.html

免责声明:文章转载自《Java线程并发中常见的锁--自旋锁 偏向锁》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇wkhtmltopdf 中文参数详解go安装goctl下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

【转】linux kvm虚拟机配置及常见问题处理

egrep '(vmx|svm)' --color=always /proc/cpuinfo 注意:如果查询的结果中包含有vmx,那么就可以证明服务器是支持虚拟化的 安装包 yum -y groupinstall "Virtualization" "Virtualization Client" "Virtualization Platform" modpr...

解决mySQL占用内存超大问题

为了装mysql环境测试,装上后发现启动后mysql占用了很大的虚拟内存,达8百多兆。网上搜索了一下,得到高人指点my.ini。再也没见再详细的了..只好打开my.ini逐行的啃,虽然英文差了点,不过多少M还是看得明的^-^ 更改后如下: innodb_buffer_pool_size=576M ->256M InnoDB引擎缓冲区占了大头,首要就是...

【数据库开发】数据库连接池及其工作原理

对于一个简单的数据库应用,由于对于数据库的访问不是很频繁。这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什么明显的性能上的开销。但是对于一个复杂的数据库应用,情况就完全不同了。频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。 连接复用。通过建立一个数据库连接池以及一套连接使用管理...

虚拟机中安装GHO文件配置说明

前言: 我们知道如何使用虚拟机安装ISO的文件,这个比较简单;但是我们如果安装GHO的文件呢?工作中有的时候需要还原客户环境的系统镜像去寻找问题,这就使用到GHO的文件系统镜像了。那么我们该如何将GHO文件安装到虚拟机呢?表急~~接下来咱们就唠唠。 前提: 1、  获取到GHO的镜像文件 2、  主机安装VMware (这可以直接从网上下载,百度一大堆,这...

SocketChannel简述

前言 在前面的Channel概述的分类中提到过SocketChannel主要是用来基于TCP通信的通道。这篇文章详细介绍下SocketChannel SocketChannel是什么 SocketChannel特点 SocketChannel的使用 SocketChannel A selectable channel for stream-orient...

linux(1):VMware虚拟软件下安装centos6.8

前言:Linux是一种自由和开放源代码的类UNIX操作系统,继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。本人学习Linux已经有一段时间了,从一开始的小白到现在的略有所悟,深知小白学习的艰辛,因此打算系统把Linux学习的东西整理一下,发布一系列相关博客。该系列博客不定期更新,如有问题欢迎探讨。 所需一系列工具: win7系统...