Java线程并发中常见的锁机制详细介绍
作者:路上有多远 发布时间:2023-07-04 05:33:33
随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题。本文着重介绍了在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是线程安全的。这样说可能还不够具体,可以翻看一下源码吧。
Vector中的几乎所有操作是带有sychronized的,而ArrayList是没有的,所以Vector是线程安全的。
接下来我们来测试一下,开启偏向锁和不开启偏向锁对程序性能的影响有多大。
配置JVM启动(开启偏向锁)参数为:
配置JVM启动(关闭偏向锁)参数为:
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开始,自旋锁的参数被取消,虚拟机不再支持由用户配置自旋锁,自旋锁总是会执行,自旋锁次数也由虚拟机自动调整。


猜你喜欢
- Linux下的五种I/O模型1)阻塞I/O(blocking I/O)2)非阻塞I/O (nonblocking I/O)3) I/O复用(
- 最小堆基本思想:堆对应一棵完全二叉树,且所有非叶结点的值均不大于(或不小于)其子女的值,根结点(堆顶元素)的值是最小(或最大)的,每次都取堆
- 公司最近也开始基于android4.0 ICS修改框架了,公司的手机暂时不适合拿回家测试,也没有kernel的权限。从个人的角度看,我手上现
- this总要有个事物来代表类的当前对象,就像C++中的this指针一样,Java中的this关键字就是代表当前对象的引用。它有三个主要的作用
- 本文实例为大家分享了WheelView实现上下滑动选择器的具体代码,供大家参考,具体内容如下1.获得wheelwheel是GitHub上的一
- 分页application.ymlspring: datasource: url: jdbc:mysql://127.0.0.1/jpa?u
- 在C#2.0中,微软给我们带来了一些新的特性,例如泛型,匿名委托等。然而,这些新的特性多多少少会给人一种从别的语言中“抄”来的感觉(例如泛型
- 为大家分享的解决MyEclipse中的Building workspace问题的方法如下方法一:点击“Project”,取消勾选“Build
- 1. ObsoleteAttributeObsoleteAttribute 适用于除组件、模块、参数和返回值以外的所有程序元素。将元素标记为
- 自定义注解+springAop参数非空校验自定义注解,来对对应的方法进行入参校验,为空返回参数错误新建注解类@interface Param
- 本文实例为大家分享了Android实现简单banner轮播图的具体代码,供大家参考,具体内容如下说明:想玩一个简单的轮播图效果
- merge结合include优化android布局,效果不知道,个人感觉使用上也有很大的局限,不过还是了解一下,记录下来。布局文件都要有根节
- 最近正在学习c#,对其中的方法重写和隐藏的概念很是模糊,现在将其归纳如下:1:方法重写:就是在基类中的方法用virtual关键字来标识,然后
- 想用java做一个像windows里一样的txt编辑软件,涉及到字体设置选项卡,在网上找了很久都没找到,就生气啦自己写一个,现在贴这里分享一
- 类是使用关键字 class 声明的,如下面的示例所示:访问修饰符 class 类名 { //类成员: // Methods, prope
- 本文实例讲述了Java使用Random类生成随机数。分享给大家供大家参考,具体如下:一 代码import java.util.Random;
- 接上篇:播放器-IOS(Swift)篇安卓端原生播放器的接入思路与ios基本一致,所以本篇就不废话了,直接上代码:创建插件VideoView
- 本地仓库是指存在于我们本机的仓库,在我们加入依赖时候,首先会跑到我们的本地仓库去找,如果找不到则会跑到远程仓库中去找。对于依赖的包大家可以从
- 最近比较空闲没有项目做,于是乎捋了捋平时工作会遇到的一些常见问题,首先想到了多用户登录限制问题,下面就对此问题做一点思考讲解。
- 简介Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效