摘要:它俩是不冲突的,也就是说获取了类锁的线程和获取了对象锁的线程是不冲突的可重入锁住了锁住了当线程进入到的方法时,此时拿到了实例对象的锁。当一个线程执行的代码出现异常时,其所持有的锁会自动释放。
Java锁机制 synchronized锁
synchronized 简介
synchronized是Java的一个关键字,它能够将代码块(方法)锁起来
synchronized是一种互斥锁
一次只能允许一个线程进入被锁住的代码块
synchronized是一种内置锁/监视器锁
Java中每个对象都有一个内置锁(监视器,也可以理解成锁标记),而synchronized就是使用对象的内置锁(监视器)来将代码块(方法)锁定的!
synchronized保证了线程的原子性。
被保护的代码块是一次被执行的,没有任何线程会同时访问
synchronized还保证了可见性。
当执行完synchronized之后,修改后的变量对其他的线程是可见的
synchronized底层原理
同步代码块:
monitorenter和monitorexit指令实现的
同步方法(在这看不出来需要看JVM底层实现)
方法修饰符上的ACC_SYNCHRONIZED实现。
Java中的synchronized,通过使用内置锁,来实现对变量的同步操作,进而实现了对变量操作的原子性和其他线程对变量的可见性,从而确保了并发情况下的线程安全。
synchronized一般我们用来修饰三种东西
修饰普通方法:用的是 该对象(内置锁)
// 修饰普通方法,此时用的锁是 该对象(内置锁) public synchronized void test() { }
修饰代码块:用的是 该对象(内置锁)
// 修饰代码块,此时用的锁是 该对象(内置锁)--->this synchronized (this){ }当然了,我们使用synchronized修饰代码块时未必使用this,还可以使用其他的对象(随便一个对象都有一个内置锁)
称之为-->客户端锁,这是不建议使用的
修饰静态方法:用的是 类锁(类的字节码文件对象):XX.class
// 修饰静态方法,此时用的锁是 类锁(类的字节码文件对象): XX.class public static synchronized void test() { }
synchronized修饰静态方法获取的是类锁(类的字节码文件对象),synchronized修饰普通方法或代码块获取的是对象锁。
它俩是不冲突的,也就是说:获取了类锁的线程和获取了对象锁的线程是不冲突的!
可重入
public class Widget { // 锁住了 public synchronized void doSomething() { } } public class LoggingWidget extends Widget { // 锁住了 public synchronized void doSomething() { System.out.println(toString() + ": calling doSomething"); super.doSomething(); } }
当线程A进入到LoggingWidget的doSomething()方法时,此时拿到了LoggingWidget实例对象的锁。
随后在方法上又调用了父类Widget的doSomething()方法,它又是被synchronized修饰。
那现在我们LoggingWidget实例对象的锁还没有释放,进入父类Widget的doSomething()方法还需要一把锁吗?
不需要的!
因为锁的持有者是“线程”,而不是“调用”。线程A已经是有了LoggingWidget实例对象的锁了,当再需要的时候可以继续“开锁”进去的!
这就是内置锁的可重入性。
怎么释放
Lock显式锁当方法(代码块)执行完毕后会自动释放锁,不需要做任何的操作。
当一个线程执行的代码出现异常时,其所持有的锁会自动释放。
不会由于异常导致出现死锁现象
Lock显示锁简介
Lock显式锁是JDK1.5之后才有的,之前我们都是使用Synchronized锁来使线程安全的
Lock显式锁是一个接口
Lock方式来获取锁支持中断、超时不获取、是非阻塞的
提高了语义化,哪里加锁,哪里解锁都得写出来
Lock显式锁可以给我们带来很好的灵活性,但同时我们必须手动释放锁
支持Condition条件对象
允许多个读线程同时访问共享资源
synchronized锁和Lock锁使用哪个
Lock锁在刚出来的时候很多性能方面都比Synchronized锁要好,但是从JDK1.6开始Synchronized锁就做了各种的优化
优化操作:适应自旋锁,锁消除,锁粗化,轻量级锁,偏向锁。
所以,到现在Lock锁和Synchronized锁的性能其实差别不是很大!而Synchronized锁用起来又特别简单。Lock锁还得顾忌到它的特性,要手动释放锁才行(如果忘了释放,这就是一个隐患)
所以说,我们绝大部分时候还是会使用Synchronized锁,用到了Lock锁提及的特性,带来的灵活性才会考虑使用Lock显式锁
公平锁ReentrantLock锁公平锁理解起来非常简单:
线程将按照它们发出请求的顺序来获取锁
非公平锁就是:
线程发出请求的时可以“插队”获取锁
Lock和synchronize都是默认使用非公平锁的。如果不是必要的情况下,不要使用公平锁
公平锁会来带一些性能的消耗的
AQS是ReentrantLock的基础,AQS是构建锁、同步器的框架
比synchronized更有伸缩性(灵活)
支持公平锁(是相对公平的),支持非公平锁
使用时最标准用法是在try之前调用lock方法,在finally代码块释放锁
class X { private final ReentrantLock lock = new ReentrantLock(); public void m() { lock.lock(); try { // ... method body } finally { lock.unlock() } } }ReentrantReadWriteLock锁
synchronized内置锁和ReentrantLock都是互斥锁(一次只能有一个线程进入到临界区(被锁定的区域))
ReentrantReadWriteLock是一个读写锁:
在读取数据的时候,可以多个线程同时进入到到临界区(被锁定的区域)
在写数据的时候,无论是读线程还是写线程都是互斥的
在读的时候可以共享,在写的时候是互斥的
读锁不支持条件对象,写锁支持条件对象
读锁不能升级为写锁,写锁可以降级为读锁
读写锁也有公平和非公平模式
读锁支持多个读线程进入临界区,写锁是互斥的
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/73515.html
摘要:底层是是通过对象,对象有自己的对象头,存储了很多信息,其中一个信息标示是被哪个线程持有。当一个线程执行的代码出现异常时,其所持有的锁会自动释放。 前言 回顾前面: 多线程三分钟就可以入个门了! Thread源码剖析 多线程基础必要知识点!看了学习多线程事半功倍 只有光头才能变强! 本文章主要讲的是Java多线程加锁机制,有两种: Synchronized 显式Lock 不得不唠...
摘要:当一个线程持有重量级锁时,另外一个线程就会被直接踢到同步队列中等待。 java代码先编译成字节码,字节码最后编译成cpu指令,因此Java的多线程实现最终依赖于jvm和cpu的实现 synchronized和volatile 我们先来讨论一下volatile关键字的作用以及实现机制,每个线程看到的用volatile修饰的变量的值都是最新的,更深入的解释就涉及到Java的内存模型了,我们...
摘要:并发机制与底层实现原理是轻量级的它在多处理器开发中保证了共享变量的可见性,因为它不会引起线程上下文的切换和调度,所以比的使用和执行成本更底。如果线程间存在锁竞争,会带来额外的锁撤销的消耗。轻量级锁竞争的线程不会阻塞,提高了程序的响应速度。 java并发机制与底层实现原理 volatile volatile是轻量级的synchronize,它在多处理器开发中保证了共享变量的可见性,因为它...
摘要:在这个等待通知机制中,我们需要考虑以下四个要素。何时等待线程要求的条件不满足就等待。是会随机地通知等待队列中的一个线程,而会通知等待队列中的所有线程。 由上一篇文章你应该已经知道,在 破坏占用且等待条件 的时候,如果转出账本和转入账本不满足同时在文件架上这个条件,就用死循环的方式来循环等待,核心代码如下: // 一次性申请转出账户和转入账户,直到成功 while(!actr.apply...
阅读 3468·2021-09-02 09:53
阅读 1791·2021-08-26 14:13
阅读 2749·2019-08-30 15:44
阅读 1313·2019-08-30 14:03
阅读 1961·2019-08-26 13:42
阅读 3013·2019-08-26 12:21
阅读 1301·2019-08-26 11:54
阅读 1899·2019-08-26 10:46