Java锁机制了解一下(一)

简介: 笔记

前言


回顾前面:

多线程三分钟就可以入个门了!

Thread源码剖析

多线程基础必要知识点!看了学习多线程事半功倍

本文章主要讲的是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){
        }
    }
}

来反编译看一下:

10.jpg

同步代码块

  • 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是不可行的

11.jpg

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

12.jpg

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

13.jpg


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();
    }
}

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

14.png


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. 当一个线程执行的代码出现异常时,其所持有的锁会自动释放
  • 不会由于异常导致出现死锁现象~
目录
打赏
0
0
0
0
1281
分享
相关文章
深入理解Java锁升级:无锁 → 偏向锁 → 轻量级锁 → 重量级锁(图解+史上最全)
锁状态bits1bit是否是偏向锁2bit锁标志位无锁状态对象的hashCode001偏向锁线程ID101轻量级锁指向栈中锁记录的指针000重量级锁指向互斥量的指针010尼恩提示,讲完 如减少锁粒度、锁粗化、关闭偏向锁(-XX:-UseBiasedLocking)等优化手段 , 可以得到 120分了。如减少锁粒度、锁粗化、关闭偏向锁(-XX:-UseBiasedLocking)等‌。JVM锁的膨胀、锁的内存结构变化相关的面试题,是非常常见的面试题。也是核心面试题。
深入理解Java锁升级:无锁 → 偏向锁 → 轻量级锁 → 重量级锁(图解+史上最全)
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
143 2
探索Java中的异常处理机制
【10月更文挑战第35天】在Java的世界中,异常是程序运行过程中不可避免的一部分。本文将通过通俗易懂的语言和生动的比喻,带你了解Java中的异常处理机制,包括异常的类型、如何捕获和处理异常,以及如何在代码中有效地利用异常处理来提升程序的健壮性。让我们一起走进Java的异常世界,学习如何优雅地面对和解决问题吧!
|
3天前
|
Java静态代码块深度剖析:机制、特性与最佳实践
在Java中,静态代码块(或称静态初始化块)是指类中定义的一个或多个`static { ... }`结构。其主要功能在于初始化类级别的数据,例如静态变量的初始化或执行仅需运行一次的初始化逻辑。
19 4
|
3月前
|
Java中的异常处理机制深度剖析####
本文深入探讨了Java语言中异常处理的重要性、核心机制及其在实际编程中的应用策略,旨在帮助开发者更有效地编写健壮的代码。通过实例分析,揭示了try-catch-finally结构的最佳实践,以及如何利用自定义异常提升程序的可读性和维护性。此外,还简要介绍了Java 7引入的多异常捕获特性,为读者提供了一个全面而实用的异常处理指南。 ####
110 20
深入理解Java异常处理机制
Java的异常处理是编程中的一块基石,它不仅保障了代码的健壮性,还提升了程序的可读性和可维护性。本文将深入浅出地探讨Java异常处理的核心概念、分类、处理策略以及最佳实践,旨在帮助读者建立正确的异常处理观念,提升编程效率和质量。
156 1
深入探索Java中的异常处理机制##
本文将带你深入了解Java语言中的异常处理机制,包括异常的分类、异常的捕获与处理、自定义异常的创建以及最佳实践。通过具体实例和代码演示,帮助你更好地理解和运用Java中的异常处理,提高程序的健壮性和可维护性。 ##
81 2
Java 反射机制:动态编程的强大利器
Java反射机制允许程序在运行时检查类、接口、字段和方法的信息,并能操作对象。它提供了一种动态编程的方式,使得代码更加灵活,能够适应未知的或变化的需求,是开发框架和库的重要工具。
96 5
Java中的异常处理机制:深入解析与最佳实践####
本文旨在为Java开发者提供一份关于异常处理机制的全面指南,从基础概念到高级技巧,涵盖try-catch结构、自定义异常、异常链分析以及最佳实践策略。不同于传统的摘要概述,本文将以一个实际项目案例为线索,逐步揭示如何高效地管理运行时错误,提升代码的健壮性和可维护性。通过对比常见误区与优化方案,读者将获得编写更加健壮Java应用程序的实用知识。 --- ####
Java 异常处理:机制、策略与最佳实践
Java异常处理是确保程序稳定运行的关键。本文介绍Java异常处理的机制,包括异常类层次结构、try-catch-finally语句的使用,并探讨常见策略及最佳实践,帮助开发者有效管理错误和异常情况。
206 6

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等