Java锁机制了解一下

小编 2026-06-30 阅读:771 评论:0
前言回顾前面:多线程三分钟就可以入个门了!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){        }    }}

来反编译看一下:

Java锁机制了解一下

同步代码块

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

Java锁机制了解一下

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

Java锁机制了解一下

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

Java锁机制了解一下

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

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

Java锁机制了解一下

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显式锁是一个接口,我们来看看:

Java锁机制了解一下

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

Java锁机制了解一下

可以简单概括一下:

  • 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,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友

文章的目录导航

更多的文章可往:文章的目录导航
版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表