锁的种类

  • 独享锁 VS 共享锁

    • 独享锁:锁只能被一个线程持有(synchronized)
    • 共享锁:锁可以被多个程序所持有(读写锁)
  • 乐观锁 VS 悲观锁
    • 乐观锁:每次去拿数据的时候都乐观地认为别人不会修改,所以不进行加锁操作。乐观锁适用于多读的应用类型。(CAS,Atomic)

      • CAS(Compare And Swap),其思想是:我认为V的值应该为 A,如果是,那么将 V 的值更新为 B,否则不修改并告诉V的值实际为多少。这样一来当有多个线程尝试修改同一个对象时,只有一个线程能够成功修改,因为一旦有一个线程修改成功了,那么其他线程就没法满足 V 的值是 A 了。其他线程修改失败之后不会被挂起,而是再次尝试修改。
    • 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
  • 公平锁 VS 非公平锁
    • 公平锁:对于等待锁队列中的线程,按照先来先得的原则,给予锁。
    • 非公平锁:一个线程一执行到需要锁的地方,就尝试去要锁,失败了再进等待队列。这样一来,就能够大大减少唤醒线程的开销,但有可能会出现某线程饿死的情况。
      • synchronized 是非公平锁,lock 可以是公平也可以非公平。
  • 其他概念
    • 可重入锁(synchronized 和 lock):可重入性表明了锁的分配不是基于方法而是基于线程的。如:synchronized 了两个方法 method1, method2,method1 中调用了 method2,那么只要线程获得了 method1 的锁,那么它就一定能够进入 method2 而不需再申请锁。
    • 可中断锁(lockInterruptible()):对于等待锁的线程,若等待时间过长,想中断该等待过程,让该线程去做其他的事,那么就需要可中断锁。

Java 锁的状态

Java 锁状态的级别从低到高为:无锁状态、偏向锁状态、轻量级锁状态、重要级锁状态,而锁的状态是被记录在 Java 对象的对象头中。

  • Java 的对象头:由 Mark Word、指向类的指针、数组长度,其中 Mark Word 负责记录有关锁的信息

    • 32 位 JVM 中 Mark Word 存储的内容为

      锁状态 25bit 4bit 1bit (是否是偏向锁) 2bit (锁标志位)
      无锁 对象的 HashCode 分代年龄 0 01
      偏向锁 线程 ID 与 Epoch 分代年龄 1 01
      轻量级锁 指向栈中的锁记录指针 同前 同前 00
      重量级锁 指向重量级锁的指针 同前 同前 10
      GC 标记 11
  • 偏向锁、轻量级锁概念的出现,都是为了减少同步唤醒的代价,若所有锁都为重量级的,那么所有等待锁的线程必须进入阻塞态

    • 偏向锁:大多数情况下,锁并不存在被大量线程竞争,而且总是由一个线程多次重复申请,那么这时只需要让偏向锁记录该线程 ID,这样当该线程又来声请锁的时候,就能快速获得锁了。
    • 轻量级锁:存在竞争,但是不强力且持有锁的线程会很快释放锁,在这种情况下,等待锁的线程可以处于自旋状态,而不必进入阻塞状态。
  • Java 中锁的状态是一级一级往上升的,具体情况为:

    • 当未遇到同步代码块(即没有出现锁的时候),处于无锁状态。
    • 当对象被当作了同步锁,但当前只有一个线程 A 申请了该锁(没有竞争产生),那么对象的锁状态就会升级为偏向锁。
    • 当又有另一线程 B 来申请锁时,这时会检查当前占用该锁的线程 A 是否处于活跃状态或者仍然需要该锁,即是否有竞争会产生。若无,则偏向锁偏向 B,A 释放偏向锁。
    • 若有,那么偏向锁就会升级会轻量级锁。但若竞争进一步加大(有很多线程需求锁,且占用时间较长),那么锁就会进一步升级为重量级锁。

Java 锁机制的实现


synchronized

从上一节我们可以看出来,Java 中能够作为锁的只能是对象,所以 synchronized 的实现,也是通过对对象进行加锁实现的。

  • synchronized 的三种用法

    • synchronized aMethod,锁一个类的方法,可以防止多个线程同时调用同一个对象的这个方法,但是能够同时调用。
    • static synchronized aMethod,锁一个类的静态方法,可以防止多个线程同时这个类的该静态方法。
    • synchronized (object){代码块},把 object 对象当作该代码块的锁
  • synchronized 使用起来简单,但是有如下缺点
    • 不支持中断,可能造成其他线程等待很长的时间,甚至死锁。
    • 不支持读写锁,即读读操作是能够同时进行的,但是 synchronized 没法实现。

Lock

Lock 是 java.util.concurrent.locks 包里的一个接口,对应的 ReentrantLock 类实现了该接口。

  • 一般使用方法

    public class LockTest {
    private Lock alock = new ReentrantLock(); // 锁需要定义在具体使用锁的栈帧的上一层,即若在线程里定义的话,不同的线程会 new 出不同的锁,这样就不能实现同步了。 public static void main(String[] args) {
    Runnable r = () -> {
    alock.lock(); // 获得锁
    try {
    // do something
    } catch(Exception e){ } finally{
    alock.unlock();
    }
    }
    Thread test = new Thread(r);
    test.start();
    }
    }
  • 获得锁的方法

    • lock(): 获取锁,若失败,则等待
    • tryLock(long time, TimeUnit unit): 尝试获得锁,返回一个布尔值,表明是否获得到了锁。
    • lockInterruptibly(): 若某一个线程使用这个方法等待获取锁,那么可以通过 thread.interrupt() 的方法去让他中断等待,干别的事。
  • 除此之外,java.util.concurrent.locks 包内还定义有 ReadWriteLock 接口,并有多种读写锁的实现类。


volatile

一种轻量级同步机制,能够保证 volatile 修饰的变量具有可见性,但不具备原子性

  • 原子性与可见性

    • 原子性:在多线程并发的条件下,对于变量的操作是线程安全的,不会受到其他线程的干扰。Atomatic 基于底层硬件处理器提供的原子指令,保证并发时线程安全。
    • 可见性:在多线程并发的条件下,对于变量的修改,其他线程中能获取到修改后的。volatile 通过对于值的操作,会立即更新到主存中,当其他线程获取该值会从主存中获取。
  • 使用方法与 synchronized 一样,都是 Java 关键词。

Atomatic

前面提到过 Atomatic 是一种乐观锁,它是通过操作的原子性来保证自己的线程安全的,即操作是不可被打断的,且具有排他性。当某一线程进行该原子操作时,其他想要执行该操作的线程只能处于自旋状态,等待该操作完成。

  • 其实,这个给人的感觉,atomatic 有点像轻量级锁,不过这种线程的等待,不是通过软件实现的,而是通过硬件实现的(硬件上的原子操作)。
  • 不过这种依靠硬件上的原子操作实现的锁机制,导致 atomatic 的相关操作都比较基础。
  • 使用方法:参见 java.util.concurrent.atomic

Java 锁机制总结的更多相关文章

  1. 转 : 深入解析Java锁机制

    深入解析Java锁机制 https://mp.weixin.qq.com/s?__biz=MzU0OTE4MzYzMw%3D%3D&mid=2247485524&idx=1&s ...

  2. java锁机制的面试题

    java锁机制的面试题 1.ABA问题 2.CAS乐观锁 3.synchronize实现原理 4.synchronize与lock的区别 5.volatile实现原理 6.乐观锁的业务场景及实现方式 ...

  3. Java锁机制深入理解

    Java锁机制 背景知识 指令流水线 ​ CPU的基本工作是执行存储的指令序列,即程序.程序的执行过程实际上是不断地取出指令.分析指令.执行指令的过程. ​ 几乎所有的冯•诺伊曼型计算机的CPU,其工 ...

  4. java锁机制

    2.4 锁机制        临界区是指,使用同一个锁控制的同一段代码区或多段代码区之间,在同一时间内最多只能有一个线程在执行操作.这个概念与传统的临界区有略微的差别,这里不想强调这些概念上的差别,临 ...

  5. Java锁机制了解一下

    前言 回顾前面: 多线程三分钟就可以入个门了! Thread源码剖析 多线程基础必要知识点!看了学习多线程事半功倍 只有光头才能变强! 本文章主要讲的是Java多线程加锁机制,有两种: Synchro ...

  6. JAVA锁机制-可重入锁,可中断锁,公平锁,读写锁,自旋锁,

    如果需要查看具体的synchronized和lock的实现原理,请参考:解决多线程安全问题-无非两个方法synchronized和lock 具体原理(百度) 在并发编程中,经常遇到多个线程访问同一个 ...

  7. JAVA锁机制(上)

    在实际开发中经常会用到多线程协作来处理问题,锁是处理线程安全不可缺少的机制.在JAVA中可以通过至少三种方式来实现线程锁. 1.  synchronized修饰符,这种锁机制是虚拟机实现的一种锁. 2 ...

  8. java 锁机制(synchronized 与 Lock)

    在java中,解决同步问题,很多时候都会使用到synchronized和Lock,这两者都是在多线程并发时候常使用的锁机制. synchronized是java中的一个关键字,也就是说是java内置的 ...

  9. 【面试专栏】JAVA锁机制

    1. 悲观锁 / 乐观锁   在Java和数据库中都存在悲观锁和乐观锁的应用.Mysql锁机制中的悲观锁和乐观锁请查看:   Mysql锁机制--悲观锁和乐观锁   悲观锁:在获得数据时先加锁,只到数 ...

随机推荐

  1. C#控制台输出退格实现变换闪烁的字符效果

    C#控制台输出退格实现变换闪烁的字符效果,传统的Console.Clear()方法能清除控制台上的所有内容. 如果用 Console.Write('\u0008');可以实现输出退格,这样就可以方便地 ...

  2. [Objective-C语言教程]命令行参数(23)

    执行时,可以将一些值从命令行传递给Objective-C程序. 这些值称为命令行参数,很多时候它们对程序很重要,特别是当想要从外部控制程序而不是在代码中对这些值进行硬编码时就很有用了. 命令行参数使用 ...

  3. php 内存分配新

    https://yq.aliyun.com/articles/38307 https://yq.aliyun.com/ziliao/132720 http://blog.liyiwei.cn/%E3% ...

  4. 基于Django+celery二次开发动态配置定时任务 ( 二)

    一.需求 结合上一篇,使用djcelery模块开发定时任务时,定时任务的参数都保存在djcelery_periodictask表的args.kwargs字段里,并且是json格式.那么,当定时任务多了 ...

  5. iOS11 & iPhone X 适配指南

    苹果WWDC开发者大会上,终于发布了大家期待已久的iOS 11,有些新特性功能确实出人意料.不过大的方面苹果貌似也就 AR 和 GM 机器学习了,9月13日凌晨1点,苹果开了新品发布会,相信大家都已经 ...

  6. AFNetworking 3.1.0 使用中某些知识点讲解

    # POST / GET  请求 /*! 首先要知道,POST请求不能被缓存,只有 GET 请求能被缓存.因为从数学的角度来讲,GET 的结果是 幂等 的,就好像字典里的 key 与 value 就是 ...

  7. ORACLE 动态执行SQL语句

    本文转自 http://zhaisx.iteye.com/blog/856472 Oracle 动态SQL Oracle 动态SQL有两种写法:用 DBMS_SQL 或 execute immedia ...

  8. jpetStore 学习总结(1)

    最近学习了Springmvc4,对官方的例子jpetStore进行了分析研究,在官方网站下载spring-framework-2.5.6.SEC03,其中samples文件夹里就有jpetstore的 ...

  9. Could not parse UiSelector argument: 'XXX' is not a string 错误解决办法

    ebDriverWait(driver,20).until(EC.visibility_of_element_located((MobileBy.ANDROID_UIAUTOMATOR,new UiS ...

  10. FJOI2019 游记[大概是考完会解封?]

    Day -1 不知不觉就快省选了啊...从NOIP的爆炸到现在也已经过了两个月了啊... 因为高一没有封闭[划] 所以这些内容是翘了课[划]在机房写的 嘛...感觉自己还有好多东西不会啊……这两天一直 ...