Java线程并发中常见的锁机制详细介绍

随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题。本文着重介绍了在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开始,自旋锁的参数被取消,虚拟机不再支持由用户配置自旋锁,自旋锁总是会执行,自旋锁次数也由虚拟机自动调整。

(0)

相关推荐

  • java线程并发cyclicbarrier类使用示例

    复制代码 代码如下: package com.yao; import java.util.Random;import java.util.concurrent.CyclicBarrier; /** * CyclicBarrier类似于CountDownLatch也是个计数器, * 不同的是CyclicBarrier数的是调用了CyclicBarrier.await()进入等待的线程数, * 当线程数达到了CyclicBarrier初始时规定的数目时,所有进入等待状态的线程被唤醒并继续. * Cy

  • java多线程并发executorservice(任务调度)类

    复制代码 代码如下: package com.yao; import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.ScheduledFuture;import java.util.concurrent.TimeUnit; /** * 以下是一个带方法的类,它设置了 ScheduledExecutorService ,2

  • 深入探究Java多线程并发编程的要点

    关键字synchronized synchronized关键可以修饰函数.函数内语句.无论它加上方法还是对象上,它取得的锁都是对象,而不是把一段代码或是函数当作锁. 1,当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一段时间只能有一个线程得到执行,而另一个线程只有等当前线程执行完以后才能执行这块代码. 2,当一个线程访问object中的一个synchronized(this)同步代码块时,其它线程仍可以访问这个object中是其它非synchr

  • java线程并发countdownlatch类使用示例

    复制代码 代码如下: package com.yao; import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; /** * CountDownLatch是个计数器,它有一个初始数, * 等待这个计数器的线程必须等到计数器倒数到零时才可继续. */public class CountDownLatchTe

  • 使用java的HttpClient实现多线程并发

    说明:以下的代码基于httpclient4.5.2实现. 我们要使用java的HttpClient实现get请求抓取网页是一件比较容易实现的工作: public static String get(String url) { CloseableHttpResponseresponse = null; BufferedReader in = null; String result = ""; try { CloseableHttpClienthttpclient = HttpClient

  • java线程并发blockingqueue类使用示例

    如果BlockingQueue是满的任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有新的空间才会被唤醒继续操作. BlockingQueue提供的方法主要有: add(anObject): 把anObject加到BlockingQueue里,如果BlockingQueue可以容纳返回true,否则抛出IllegalStateException异常. offer(anObject):把anObject加到BlockingQueue里,如果BlockingQueue

  • java线程并发semaphore类示例

    复制代码 代码如下: package com.yao; import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore; /** * Java 5.0里新加了4个协调线程间进程的同步装置,它们分别是: * Semaphore, CountDownLatch, CyclicBarrier和Exchanger. * 本例主要介

  • 浅谈Java线程并发知识点

    发布:一个对象是使它能够被当前范围之外的代码所引用: 常见形式:将对象的的引用存储到公共静态域:非私有方法中返回引用:发布内部类实例,包含引用. 逃逸:在对象尚未准备好时就将其发布. 不要让this引用在构造函数中逸出.例,在构造函数中启动线程,线程会包含对象的引用. 同步容器:对容器的所有状态进行穿行访问,Vector.Hashtable,Cllections.synchronizedMap|List 并发容器:ConcurrentHashMap,CopyOnWriteArrayList,Co

  • java多线程并发中使用Lockers类将多线程共享资源锁定

    复制代码 代码如下: package com.yao; import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReadWriteLock;import java.util.c

  • Java线程并发中常见的锁机制详细介绍

    随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题.本文着重介绍了在java并发中常见的几种锁机制. 1.偏向锁 偏向锁是JDK1.6提出来的一种锁优化的机制.其核心的思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作.也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操作了,从而节约了操作时间,如果在此之间有其他的线程进行了锁请求,则锁退出偏向模式.在JVM中使用-XX:+UseBiasedLocking pac

  • tomcat中Servlet的工作机制详细介绍

    tomcat中Servlet的工作机制 在研究Servlet在tomcat中的工作机制前必须先看看Servlet规范的一些重要的相关规定,规范提供了一个Servlet接口,接口中包含的重要方法是init.service.destroy等方法,Servlet在初始化时要调用init方法,在销毁时要调用destroy方法,而对客户端请求处理时则调用service方法.对于这些机制的支持都必须由Tomcat内部去支持,具体则是由Wrapper容器提供支持. 在tomcat中消息流的流转机制是通过四个不

  • JavaScript中的分号插入机制详细介绍

    仅在}之前.一个或多个换行之后和程序输入的结尾被插入 也就是说你只能在一行.一个代码块和一段程序结束的地方省略分号. 也就是说你可以写如下代码 复制代码 代码如下: function square(x) {     var n = +x     return n * n } 但是却不可以写的像下面代码一样,这样就报错了哦 复制代码 代码如下: function area(r) {    r = +r    return Math.PI*r*r }//error 仅在随后的输入标记不能解析时插入

  • java 高并发中volatile的实现原理

    java 高并发中volatile的实现原理 摘要: 在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的"可见性".可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值.它在某些情况下比synchronized的开销更小 1. 定义: java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量.

  • Java线程池中多余的线程是如何回收的

    最近阅读了JDK线程池ThreadPoolExecutor的源码,对线程池执行任务的流程有了大体了解,实际上这个流程也十分通俗易懂,就不再赘述了,别人写的比我好多了. 不过,我倒是对线程池是如何回收工作线程比较感兴趣,所以简单分析了一下,加深对线程池的理解吧. 那么,就以JDK1.8为例分析吧. 1.runWorker(Worker w) 工作线程启动后,就进入runWorker(Worker w)方法. 里面是一个while循环,循环判断任务是否为空,若不为空,执行任务:若取不到任务,或发生异

  • Java线程通信中关于生产者与消费者案例分析

    相关方法: wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器. notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级高的那个. notifyAll():一旦执行此方法,就会唤醒所有被wait的线程. 说明: 1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中. 2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器.

  • java线程池中Worker线程执行流程原理解析

    目录 引言 Worker类分析 runWorker(Worker)方法 getTask()方法 beforeExecute(Thread, Runnable)方法 afterExecute(Runnable, Throwable)方法 processWorkerExit(Worker, boolean)方法 tryTerminate()方法 terminated()方法 引言 在<[高并发]别闹了,这样理解线程池执行任务的核心流程才正确!!>一文中我们深度分析了线程池执行任务的核心流程,在Th

  • Java线程并发访问代码分析

    这篇文章主要介绍了Java线程并发访问代码分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 class ConcurrentThread { /** * 分析线程并发访问代码解释原因 * volatile关键字: * 1):保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的 * 2):禁止进行指令重排序 * volatile本质是告诉JVM当前变量在寄存器(工作内存)中的值是不确定的,需

  • 浅谈JAVA 线程状态中可能存在的一些误区

    BLOCKED 和 WAITING 的区别 BLOCKED 和 WAITING 两种状态从结果上来看,都是线程暂停,不会占用 CPU 资源,不过还是有一些区别的 BLOCKED 等待 Monitor 锁的阻塞线程的线程状态,处于阻塞状态的线程正在等待 Monitor 锁进入 synchronized   Block 或者 Method ,或者在调用 Object.wait 后重新进入同步块/方法.简单的说,就是线程等待 synchronized 形式的锁时的状态 下面这段代码中, t1 在等待

  • Java线程操作的常见方法【线程名称获取、设置、线程启动判断等】

    本文实例讲述了Java线程操作的常见方法.分享给大家供大家参考,具体如下: 一 线程名称的操作 1 代码 public class GetNameThreadDemo extends Thread { public void run() { for( int i = 0; i < 5; ++i ) { printMsg(); try { Thread.sleep(1000); // 睡眠1秒 } catch( InterruptedException e ) { e.printStackTrac

随机推荐