Java 锁分类

锁的分类

从宏观上分为乐观锁与悲观锁

乐观锁

乐观锁是一种乐观思想,认为读多写少,遇到并发的可能性低,每次拿数据时候并不会上锁,因为认为不会被别人修改。但是更新的时候会判断有没有人会更新这条数据,采取写的时候先读取版本号然后加锁,主要是和上一次版本号进行比较,如果一样则更新这条数据,如果不一样则会重复读,比较,写操作。它是基于CAS来实现的。

CAS 简介

CAS:它是解决轻微冲突的多线程场景下使用锁造成性能损耗的一种机制。先是比较,如果不符合预期,则重试。它有三个操作因素:内存位置,预期原值与新值。如果内存位置的值与预期原值相等,则处理器将该位置值更新为新值,如果不相等,则获取当前值,然后进行不断的轮询操作直到成果达到某个阙值退出。

悲观锁

悲观思想,认为写多读少,遇到并发写可能性比较高,每次读写数据都会上锁,别的线程想要读写只会被阻塞住直到拿到锁。java中的悲观锁就是SynchronizedAQS(AbstractQueuedSynchronizer)框架下的锁则是先尝试cas乐观锁去获取锁,获取不到,才会转换为悲观锁,如RetreenLock

AQS 简介

AbstractQueuedSynchronizer简称AQS是一个抽象同步框架,可以用来实现一个依赖状态的同步器。JDK1.5中提供的java.util.concurrent包中的大多数的同步器(Synchronizer)Lock, Semaphore, Latch, Barrier等,它们都是基于java.util.concurrent.locks.AbstractQueuedSynchronizer这个类的框架实现的。

公平锁与非公平锁

  • 公平锁:指的是多个线程按照申请锁的顺序来获取锁。
  • 非公平锁:多个线程并不是按照申请锁的顺序获取锁,有可能后申请的比先申请的线程优先获取锁,可能造成饥饿现象,也就是线程无法访问资源而出现无法执行下去的现象。 RetreenLock它是通过构造函数来确定是不是公平锁,默认是非公平锁。非公平锁的吞吐量比公平锁大。而Synchronized是非公平锁,它没有通过AQS实现线程调度,无法成为公平锁。 //默认创建是非公平锁 public ReentrantLock() {     sync = new NonfairSync(); } //通过bool值来控制是否是公平的还是不公平的,为true公平锁,为false不公平锁 public ReentrantLock(boolean fair) {     sync = fair ? new FairSync() : new NonfairSync(); }

可重入锁

也称为递归锁,线程可以重复获取一把锁,同一个线程在外层方法获取锁,进入内层方法会自动获取锁。synchronizedReentrantLock 都是可重入锁,可重入锁在一定程度上可以避免死锁。

独享锁与共享锁

  • 独享锁指的是锁一次只能被一次线程持有
  • 共享锁同时可以被多个线程持有 synchronizedReentrantLock 都是独享锁,ReadWriteLock 的读锁是共享锁,写锁是独享锁;ReentrantLock 的独享锁和共享锁也是通过 AQS来实现的。

互斥锁与读写锁

其实是独享锁与共享锁具体说法;互斥锁Java中实现就是ReentrantLock,而读写锁Java实现是ReadWriteLock

分段锁

实质上是一种锁的策略,并不是具体的锁。对于ConcurrentHashMap它的并发实现在JDK 11之前是都过分段锁来实现的。当需要put元素时候,并不是对hashMap整个加锁,而是通过hashCode知道在那个分段,进行分段加锁。在多线程操作中,只要put元素时候不放在同一个分段区域中,就可以进行并行插入元素,统计大小时候需要获取所有分段锁。归根结底分段锁是用来细化锁的粒度。

偏向锁

从始至终只要一个线程请求一把锁。同步代码一直被一个线程访问,线程自动获取锁。 Java偏向锁是Java6引入的一项多线程优化。它会偏向第一个访问锁的线程,如果运行过程中,只有一个线程访问,没有多线程争用情况,则线程无需同步,这时候线程就会被加一个偏向锁。 但是再运行的时候,遇到其他线程占锁,则持有偏向锁的线程会被挂起,并且JVM会消除它身上的偏向锁,将锁升级为轻量级锁。

偏向锁适用场景

始终只有一个线程在执行同步代码块,在它没有执行完成前,没有其他线程去执行,锁没有竞争,但是有了竞争,就会升级为轻量级锁。这时候升级的轻量级锁如果撤销的话,会触发stop the world操作。

stop the world 简介

Java中Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互。

轻量级锁

是由偏向锁升级而来,偏向锁时候,一个线程进入同步代码块,这时候另一个线程加入锁的争用,它就会升级为轻量级锁。 多个线程在不同时间段请求同一把锁,也就是没有竞争的情况下,Java虚拟机就会采用轻量级锁,来避免重量级锁阻塞以及重复唤醒。

轻量级锁释放

当轻量级锁在释放的期间,会由轻量级锁切换到重量级锁,之前在获取锁的时候它拷贝的对象头markWord,在释放锁的时候它发现自己持有锁的时被其他线程访问,并且此线程对markword进行了修改,两者对比发现不一致就切换到重量级锁。

重量级锁

它是Java中的基础锁,在这种状态下,Java虚拟机会阻塞加锁失败的线程,并且在目标锁被释放的时候,唤醒这些线程。Java中synchronized就是一种重量级锁。 当轻量级锁在释放的期间,会由轻量级锁切换到重量级锁,之前在获取锁的时候它拷贝的对象头mark Word,在释放锁的时候它发现自己持有锁的时被其他线程访问,并且此线程对mark word进行了修改,两者对比发现不一致就切换到重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。

什么是Java对象头

Java对象头包括两部分信息,分别是Mark Word与元数据指针,Mark Word用于存储对象运行时的数据,比如HashCode、锁状态标志、Gc分代年龄、线程持有的锁等,而元数据指针用于指向方法区中的目标类的元数据,通过元数据可以确定对象的具体类型。

自旋锁

当持有锁的线程能够在很短的时间内释放锁,而那些等待竞争锁的线程就无需做内核态与用户态之间的切换进入阻塞挂起状态,它们只需要等一等,自旋,等持有锁的线程释放锁后可以立即获取锁,减少了线程上下文切换。但是会循环造成CPU消耗增加。

解决自选锁CPU浪费

如果锁的竞争激烈,或者持有锁的线程需要长时间占用锁执行同步块,这时候就不适合使用自旋锁了,因为自旋锁在获取锁前一直都是占用cpu做无用功,占着CPU却不用,并且这个时候有大量线程在竞争一个锁,会导致获取锁的时间很长,线程自旋的消耗大于线程阻塞挂起操作的消耗,其它需要cup的线程又不能获取到cpu,造成cpu的浪费。所以这种情况下我们要关闭自旋锁;

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 线程优化

    Process中定义,值越小,优先级越高,默认是THREAD_PRIORITY_DEFAULT 0

    Yif
  • Android 开发艺术探索笔记二

    不管是Activity,Dialog还是Toast,它们视图都是附加在window上的,window才是view的直接管理者。

    Yif
  • Activity 基础知识

    类加载方案需要重启App后让ClassLoader重新加载新的类,为什么需要重启,因为类是无法卸载的,要想重新加载类就需要重启App,因此采用类加载方案的热修复...

    Yif
  • Java多线程编程-(13)-从volatile和synchronized的底层实现原理看Java虚拟机对锁优化所做的努力

    对于Java来说我们知道,Java代码首先会编译成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上进行执行。

    Java后端技术
  • 让人头大的各种锁,从这里让你思绪清晰

    说到了锁我们经常会联想到生活中的锁,在我们日常中我们经常会接触到锁。比如我们的手机锁,电脑锁,再比如我们生活中的门锁,这些都是锁。

    乱敲代码
  • Vista 及后续版本的新线程池

    在上一篇的博文中,说了下老版本的线程池,在Vista之后,微软重新设计了一套线程池机制,并引入一组新的线程池API,新版线程池相对于老版本的来说,它的可控性更高...

    Masimaro
  • 【你问我答】这些Java并发问题,专家是这么回答的

    针对上期Java高并发【你问我答】中读者提出的问题,王锐同学的回答如下。 一 ---- 美团内部使用过Akka么?有什么坑? ——Absurd “ 答: 只简...

    美团技术团队
  • 35.QT-多线程

    比如,当下载多个文件时,该下载相关的进程就会创建多个线程,每个线程负责下载一个文件

    张诺谦
  • c++ 网络编程(九)TCP/IP LINUX/windows--使用IOCP模型 多线程超详细教程 以及 多线程实现服务端

    原文链接:https://www.cnblogs.com/DOMLX/p/9661012.html

    徐飞机
  • C# 多线程学习系列三之CLR线程池系列之ThreadPool

    1、进程和CLR的关系 一个进程可以只包含一个CLR,也可以包含多个CLR 2、CLR和AppDomain的关系 一个CLR可以包含多个AppDomain 3、...

    郑小超.

扫码关注云+社区

领取腾讯云代金券