Java锁机制了解一下

Java面试题006:一文深入了解多线程并发---锁机制 java当中的锁是在多线程环境下为保证共享资源健康、线程安全的一种手段。线程操作某个共享资源之前,先对资源加一层锁,保证操作期间没有其他线程访问资源,当操作完成后,再释放锁。锁的大分类,有两种:悲观锁、乐观锁。1、乐观锁乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,使用版本号机制或 CAS 算法。Java没有提供可直接使用的乐观锁。 阅读详情

前言

回顾前面:

只有光头才能变强!

本文章主要讲的是Java多线程加锁机制,有两种:

  • Synchronized
  • 显式Lock

不得不唠叨几句:

  • 在《Java核心技术卷 一》是先讲比较难的显式Lock,而再讲的是比较简单的Synchronized
  • 而《Java并发编程实战》在前4章零散地讲解了Synchronized,将显式Lock放到了13章

其实都比较坑,如果能先系统讲了Synchronized锁机制,接着讲显式Lock锁机制,那就很容易理解了。也不需要跨那么多章节。

那么接下来我们就开始吧~

一、synchronized锁

1.1synchronized锁是什么?

synchronized是Java的一个关键字,它能够将代码块(方法)锁起来

  • 它使用起来是非常简单的,只要在代码块(方法)添加关键字synchronized,即可以实现同步的功能~

    public synchronized void test() {
        // 关注公众号Java3y
        // doSomething
    }

synchronized是一种互斥锁

  • 一次只能允许一个线程进入被锁住的代码块

synchronized是一种内置锁/监视器锁

  • Java中每个对象都有一个内置锁(监视器,也可以理解成锁标记),而synchronized就是使用对象的内置锁(监视器)来将代码块(方法)锁定的!

1.2synchronized用处是什么?

  • synchronized保证了线程的原子性。(被保护的代码块是一次被执行的,没有任何线程会同时访问)
  • synchronized还保证了可见性。(当执行完synchronized之后,修改后的变量对其他的线程是可见的)

Java中的synchronized,通过使用内置锁,来实现对变量的同步操作,进而实现了对变量操作的原子性和其他线程对变量的可见性,从而确保了并发情况下的线程安全。

1.3synchronized的原理

我们首先来看一段synchronized修饰方法和代码块的代码:


public class Main {
    //修饰方法
    public synchronized void test1(){

    }


    public void test2(){
        // 修饰代码块
        synchronized (this){

        }
    }
}

来反编译看一下:

同步代码块:

  • monitorenter和monitorexit指令实现的

同步方法(在这看不出来需要看JVM底层实现)

  • 方法修饰符上的ACC_SYNCHRONIZED实现。

synchronized底层是是通过monitor对象,对象有自己的对象头,存储了很多信息,其中一个信息标示是被哪个线程持有。

具体可参考:

1.4synchronized如何使用

synchronized一般我们用来修饰三种东西:

  • 修饰普通方法
  • 修饰代码块
  • 修饰静态方法

1.4.1修饰普通方法:

用的锁是Java3y对象(内置锁)


public class Java3y {


    // 修饰普通方法,此时用的锁是Java3y对象(内置锁)
    public synchronized void test() {
        // 关注公众号Java3y
        // doSomething
    }

}

1.4.2修饰代码块:

用的锁是Java3y对象(内置锁)—>this


public class Java3y {

    public  void test() {

        // 修饰代码块,此时用的锁是Java3y对象(内置锁)--->this
        synchronized (this){
            // 关注公众号Java3y
            // doSomething
        }
    }
}

当然了,我们使用synchronized修饰代码块时未必使用this,还可以使用其他的对象(随便一个对象都有一个内置锁)

所以,我们可以这样干:

public class Java3y {


    // 使用object作为锁(任何对象都有对应的锁标记,object也不例外)
    private Object object = new Object();


    public void test() {

        // 修饰代码块,此时用的锁是自己创建的锁Object
        synchronized (object){
            // 关注公众号Java3y
            // doSomething
        }
    }

}

上面那种方式(随便使用一个对象作为锁)在书上称之为–>客户端锁,这是不建议使用的。

书上想要实现的功能是:给ArrayList添加一个putIfAbsent(),这需要是线程安全的。

假定直接添加synchronized是不可行的

使用客户端锁,会将当前的实现与原本的list耦合了:

书上给出的办法是使用组合的方式(也就是装饰器模式)

1.4.3修饰静态方法

获取到的是类锁(类的字节码文件对象):Java3y.class

public class Java3y {

    // 修饰静态方法代码块,静态方法属于类方法,它属于这个类,获取到的锁是属于类的锁(类的字节码文件对象)-->Java3y.class
    public synchronized void test() {

        // 关注公众号Java3y
        // doSomething
    }
}

1.4.4类锁与对象锁

synchronized修饰静态方法获取的是类锁(类的字节码文件对象),synchronized修饰普通方法或代码块获取的是对象锁。

  • 它俩是不冲突的,也就是说:获取了类锁的线程和获取了对象锁的线程是不冲突的!

public class SynchoronizedDemo {

    //synchronized修饰非静态方法
    public synchronized void function() throws InterruptedException {
        for (int i = 0; i <3; i++) {
            Thread.sleep(1000);
            System.out.println("function running...");
        }
    }
    //synchronized修饰静态方法
    public static synchronized void staticFunction()
            throws InterruptedException {
        for (int i = 0; i < 3; i++) {
            Thread.sleep(1000);
            System.out.println("Static function running...");
        }
    }

    public static void main(String[] args) {
        final SynchoronizedDemo demo = new SynchoronizedDemo();

        // 创建线程执行静态方法
        Thread t1 = new Thread(() -> {
            try {
                staticFunction();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // 创建线程执行实例方法
        Thread t2 = new Thread(() -> {
            try {
                demo.function();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        // 启动
        t1.start();
        t2.start();
    }
}

结果证明:类锁和对象锁是不会冲突的!

1.5重入锁

我们来看下面的代码:


public class Widget {

    // 锁住了
    public synchronized void doSomething() {
        ...
    }
}

public class LoggingWidget extends Widget {

    // 锁住了
    public synchronized void doSomething() {
        System.out.println(toString() + ": calling doSomething");
        super.doSomething();
    }
}
  1. 当线程A进入到LoggingWidget的doSomething()方法时,此时拿到了LoggingWidget实例对象的锁。
  2. 随后在方法上又调用了父类Widget的doSomething()方法,它又是被synchronized修饰。
  3. 那现在我们LoggingWidget实例对象的锁还没有释放,进入父类Widget的doSomething()方法还需要一把锁吗?

不需要的!

因为锁的持有者是“线程”,而不是“调用”。线程A已经是有了LoggingWidget实例对象的锁了,当再需要的时候可以继续“开锁”进去的!

这就是内置锁的可重入性。

1.6释放锁的时机

  1. 当方法(代码块)执行完毕后会自动释放锁,不需要做任何的操作。
  2. 当一个线程执行的代码出现异常时,其所持有的锁会自动释放。
    • 不会由于异常导致出现死锁现象~

二、Lock显式锁

2.1Lock显式锁简单介绍

Lock显式锁是JDK1.5之后才有的,之前我们都是使用Synchronized锁来使线程安全的~

Lock显式锁是一个接口,我们来看看:

随便翻译一下他的顶部注释,看看是干嘛用的:

可以简单概括一下:

  • Lock方式来获取锁支持中断、超时不获取、是非阻塞的
  • 提高了语义化,哪里加锁,哪里解锁都得写出来
  • Lock显式锁可以给我们带来很好的灵活性,但同时我们必须手动释放锁
  • 支持Condition条件对象
  • 允许多个读线程同时访问共享资源

2.2synchronized锁和Lock锁使用哪个

前面说了,Lock显式锁给我们的程序带来了很多的灵活性,很多特性都是Synchronized锁没有的。那Synchronized锁有没有存在的必要??

必须是有的!!Lock锁在刚出来的时候很多性能方面都比Synchronized锁要好,但是从JDK1.6开始Synchronized锁就做了各种的优化(毕竟亲儿子,牛逼)

所以,到现在Lock锁和Synchronized锁的性能其实差别不是很大!而Synchronized锁用起来又特别简单。Lock锁还得顾忌到它的特性,要手动释放锁才行(如果忘了释放,这就是一个隐患)

所以说,我们绝大部分时候还是会使用Synchronized锁,用到了Lock锁提及的特性,带来的灵活性才会考虑使用Lock显式锁~

2.3公平锁

公平锁理解起来非常简单:

  • 线程将按照它们发出请求的顺序来获取锁

非公平锁就是:

  • 线程发出请求的时可以“插队”获取锁

Lock和synchronize都是默认使用非公平锁的。如果不是必要的情况下,不要使用公平锁

  • 公平锁会来带一些性能的消耗的

四、最后

本文讲了synchronized内置锁和简单描述了一下Lock显式锁,总得来说:

  • synchronized好用,简单,性能不差
  • 没有使用到Lock显式锁的特性就不要使用Lock锁了。

Lock锁这里只是介绍了一些些,明天会详解它的相关子类和需要注意的地方,敬请期待~

之前在学习操作系统的时候根据《计算机操作系统-汤小丹》这本书也做了一点点笔记,都是比较浅显的知识点。或许对大家有帮助

参考资料:

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y。为了大家方便,刚新建了一下qq群:742919422,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友

文章的目录导航:

别让IO口保护二极管坑了你!实测讲解单片机电流倒灌的三种典型症状与防护 本文深入解析单片机电流倒灌的三大典型症状与防护方案,重点分析STM32等单片机在ADC采样异常、系统假死和通信速率异常中的表现。通过实测数据和电路设计建议,提供从基础二极管隔离到专业隔离器件的分级防护策略,帮助工程师有效预防和解决电流倒灌问题。 阅读详情

相关推荐

单细胞数据分析实战:用scDblFinder快速搞定异质性双细胞检测(附完整R代码)

本文详细介绍了如何使用scDblFinder工具在单细胞RNA测序数据中精准识别异质性双细胞。通过完整的R代码示例,从环境准备、数据加载到核心分析流程和结果可视化,帮助研究人员高效解决双细胞检测问题,提升单细胞数据分析的准确性。scDblFinder的集成算法和鲁棒性设计使其成为处理单细胞数据中技术噪音的理想选择。

weixin_29098117的博客 424

Java 锁机制详解

Java中的锁机制是使用最广泛、最基础的多线程同步技术之一,也是保证线程安全的重要手段。

兴趣是最好的老师,勤能补拙 5234

GSM的“时间管理大师”:TDMA时隙、突发脉冲与逻辑信道映射实战图解

本文深入解析GSM网络中的TDMA时隙调度与逻辑信道映射技术,揭示2G网络如何通过精密的时间管理和突发脉冲设计实现高效通信。从TDMA帧结构到GMSK调制优化,详细介绍了GSM的工程艺术及其在现代通信中的延续价值。

weixin_30631587的博客 514

java中各类锁的机制详细解析(全)

目录前言1. 乐观锁与悲观锁2. 公平锁与非公平锁3. 可重入锁4. 读写锁(共享锁与独占锁)5. 互斥锁6. 自旋锁7. 无锁 / 偏向锁 / 轻量级锁 / 重量级锁 前言 总结java常见的锁 区分各个锁机制以及如何使用 使用方法 锁名 考察线程是否要锁住同步资源 乐观锁和悲观锁 锁住同步资源后,要不要阻塞 不阻塞可以使用自旋锁 一个线程多个流程获取同一把锁 可重入锁 多个线程公用一把锁 读写锁(写的共享锁) 多个线程竞争要不要排队 公平锁与非公平锁 具体其他补充还可

码农研究僧的博客 7815

人脸情绪识别(使用深度学习和OpenCV)

基于opencv与深度学习的人脸情绪识别系统

mx0390304的博客 8251

方案设计之亿级排行榜方案设计

该方案通过 “Redis Zset 为核心 + 分治策略为骨架 + 多级优化为细节”,在亿级用户场景下实现了:本质是将 “亿级大问题” 拆解为 “千万级小问题”,用分布式思想和精细化设计平衡性能、可用性与成本。

shine0312的专栏 1100

java锁机制详解

java锁机制 1. 乐观锁 VS 悲观锁 ​ 悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。Java中,synchronized关键字和Lock的实现类都是悲观锁。 ​ 乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重

weixin_47071411的博客 6851

Node.js内存溢出?一招搞定JavaScript heap out of memory错误(附永久配置方案)

本文深入解析Node.js内存溢出问题,提供从临时调整到永久配置的完整解决方案。重点介绍通过设置`max-old-space-size`参数和`NODE_OPTIONS`环境变量来扩展JavaScript堆内存限制,适用于Webpack、Vite等构建工具的大型项目场景,并涵盖Windows、Linux/macOS多平台配置方法。

cake8的博客 1057

【Java】锁的原理以及应用最详细教程

一、锁锁是一种互斥的机制,在多线程环境中实现对资源的协调与控制,凡是有资源被多线程共享,涉及到修改的情况就要考虑锁的加持。(0)Java锁原理0)引申:Java对象结构Java对象结构分为3部分:①对象头(包括:Mark Word(存储了当前对象运行时的状态信息,如HashCode、指向锁记录的指针等)、Class Pointer(指针,指向当前对象类型所在方法区中的Class信息));如图,MarkWord结构(jdk1.8)HotSpot 64位操作系统(一个对象的markWord在内存占用8字节)锁标

程序源日志的博客 2146

“三进制”bonsai27b+NInfer=6g显存闪电侠(token真的自由了!)

本文将1.58-bit三元大模型Bonsai27B成功移植至NInfer推理引擎,并在RTX 4080 SUPER上实现100+ token/s的极速推理。通过自研补丁扩展NInfer支持Ada架构与三元量化格式,解决激活布局、行置换等关键bug,实测PPL 6.445,单流吞吐达96.7–130.8 t/s,20题基准得分16/20。附完整复现包(含文档、工具、源码补丁),零权重分发,助力垃圾显卡跑通27B级模型。

shensanshu的博客 1万+

vue接入海康4G版摄像头通过萤石云平台推送视频流教程

vue-video-player接入海康视频,videojs-contrib-hls,vue播放视频

renkai721的专栏 7278

java锁详解

java当中的锁、是在多线程环境下为保证共享资源健康、线程安全的一种手段。 线程操作某个共享资源之前,先对资源加一层锁,保证操作期间没有其他线程访问资源,当操作完成后,再释放锁。

SRY12240419的博客 1万+

Vue2 - 详细实现安装西瓜视频 xgplayer 插件详细配置及使用教程,利用西瓜视频播放器播放 mp4、flv、hls 格式视频文件及视频推流(直播或监控摄像头画面),支持移动端H5及PC网站!

vue2,nuxt2,西瓜视频,xgplayer,插件组件,xgplayer-hls,xgplayer-flv,xgplayer-mp4,播放视频点播和直播视频流,自定义播放器样式,截屏,监控视频,vue使用西瓜播放器播放flv、hls、mp4视频,截屏按钮,直播流,报错,黑屏,加载失败,苹果ios不兼容,重连,怎么改样式,很卡,详细教程,监控实时播放推流,倍速播放,在线flv在线推流地址,HLS播放,vue西瓜播放器,vue如何使用西瓜视频插件做直播推流回流,视频点播,flv在线视频流播放,截图功能,利用

🏆 前端领域优质创作者 1万+

深入了解Java中的锁机制

是Java.util.concurrent包中提供的一种更灵活的锁机制。与不同,允许线程在获得锁之后再次进入同步代码块,即支持重入。try {// 线程安全的操作是Java 8引入的新锁机制,结合了读写锁和乐观锁的特点。它引入了"stamp"的概念,用来标记锁的状态。try {// 乐观读操作if (!// 转为悲观读try {// 执行悲观读操作try {// 执行写操作。

qq_52645134的博客 4056

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Java 锁机制了解一下

在多线程环境下,程序往往会出现一些线程安全问题,为此,Java提供了一些线程的同步机制来解决安全问题,比如:synchronized锁和Lock锁都能解决线程安全问题。 悲观锁和乐观锁 我们可以将锁大体分为两类: 悲观锁 乐观锁 顾名思义,悲观锁总是假设最坏的情况,每次获取数据的时候都认为别的线程会修改,所以每次在拿数据的时候都会上锁,这样其它线程想要修改这个数据的时候都会被阻塞直到获取锁。比如MySQL数据库中的表锁、行锁、读锁、写锁等,Java中的synchronized和Ree

zjjcchina的博客 267

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Java锁机制详细了解一下

Java锁一、Synchronized1、代码块中修饰该类的实例对象2、代码块中修饰任意实例对象3、代码块中修饰该类的类对象4、修饰一个普通的成员方法5、修饰一个静态的成员方法 说道Java的锁,肯定会涉及到Sychronized和ReentrantLock这两种锁,接下来分别谈谈这两种锁。 一、Synchronized synchronized 是 Java 中的关键字,是利用锁的机制来实现同步的,它修饰的对象有以下几种: 代码块中,修饰该类的实例对象(this) 代码块中,修饰任意实例对象(Objec

sinat_40380134的博客 1022

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Java中的锁机制,你真的了解吗?

学到锁说明你已经学过多线程了,只有在多线程并发的情况下才会涉及到锁,相信大家用的最多的要数synchronized了,因为这个也是最简单的,直接加在方法上就可以使一个方法同步。那么除了synchronized之外,还有没有其他的锁呢,这个还真有。我们来看看: 这个是Java里边锁相关的一些类,顶级接口有三个, Lock Condition ReadWriteLock 我们来看Lock接...

坐看云起时_雨宣 7034

再次了解Java 并发编程中的锁机制与线程安全

通过灵活应用这些锁机制,能够有效地控制线程安全问题,提高程序的并发性能。今天的学习将帮助你更好地应对复杂的并发编程任务,提升系统的稳定性与性能。死锁是指两个或多个线程在执行过程中,由于争夺资源而造成一种相互等待的状态,导致线程无法继续执行。的优势在于,如果有多个读线程,能显著提高并发性能,因为它允许多个线程同时读操作,从而降低了锁的争用。它可以保证在同一时刻,只有一个线程能够访问共享资源,从而避免数据的竞争条件。提供了一种优化策略,允许多个线程同时读取共享资源,但在写操作时,只有一个线程能够修改数据。

卖蘑菇滴小男孩得博客 852

Java 并发包与锁机制:深入了解Java的并发包,锁,以及异步编程,了解并使用ConcurrentHashMap, CountDownLatch, CyclicBarrier等并发工具类

一、Java并发编程基础 Java线程模型 Java 内存模型 Java 并发包的设计目标与原则 二、Java 并发包中的重要类 ConcurrentHashMap:用于高并发的HashMap CountDownLatch:倒数计数器 CyclicBarrier: 循环阻塞同步工具 Semaphore: 多个线程控制访问共享资源的工具类 Phaser:更加灵活的CyclicBarrier 三、Java中的锁 Synchronized锁及其实现机制 Lock接口与实现类,与synchronized锁的比较

哎 你看的博客 1150

深入了解 Java JUC(二)之 从JUC锁机制AQS到重入锁、读写锁和CountDownLatch

目录 一、什么是AQS? 二、AQS的两种模式(共享模式与独占模式) 三、同步队列 四、独占锁和共享锁的获取释放流程 4.1 独占锁的 获取和释放流程 4.2 共享锁的 获取和释放流程 五、重入锁ReentrantLock 六、读写锁ReentrantReadWriteLock 七、闭锁CountDownLatch 谈到JUC中的锁机制,不得不说一下AQS。希望大家耐心阅读,最...

古小龙 990

Java锁机制:从synchronized到StampedLock

在本文中,我们介绍了Java中的多种锁机制,包括synchronized关键字、ReentrantLock、ReadWriteLock、Semaphore和StampedLock。每种锁机制都有各自的优缺点和适用场景,我们需要根据具体的业务场景来选择合适的锁机制。同时,我们还介绍了各种锁机制的实现原理,并给出了示例代码。通过学习本文,我们可以更加深入地理解Java中的锁机制,提高并发编程的技能水平。

grandachn的博客 208
上一篇: 多线程基础必要知识点!看了学习多线程事半功倍
下一篇: AQS简简单单过一遍
Java3y
Java3y 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄9年 1万+粉丝 509原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值