内置锁(或监视器锁)
概述:
- 每一个Java对象都可以用做一个实现同步的锁
- 线程获取内置锁的唯一途径:线程进入被synchronized修饰的方法或代码块会自动获取锁,退出自动释放锁(隐式)
- 该锁具有
- 互斥属性:最多只能有一个线程能持有内置锁,当其他线程想获取锁只能等待或者阻塞
- 对内存可见的属性:确保某个线程写入变量的值对于其他线程都是可见的(实现:所有执行读操作或写操作的线程都必须在同一个锁上同步)
- 优点:
- 多个线程执行该锁保护的同步代码块时也不会相互干扰实现了原子性,此时这段代码是线程安全的
- 简化编码工作
- 能够执行一些优化(锁消除等等)
- 缺点:
- 程序性能会大幅度下降
- 无法实现非阻塞结构的加锁规则
- 无法中断一个正在等待内置锁的线程
- 无法在请求一个锁时无限地等待下去
底层原理
对象会关联一个monitor对象
- 如果monitor为0,线程就可以获取该对象的锁
- 如果该线程重复获取锁,则monitor+1
- 当线程释放锁monitor-1,当monitor为0 其他线程可以继续获取锁
两种使用:
通过synchronized修饰方法
概述:
- 其实就是一种横跨整个方法体的同步代码块
- 调用该方法的对象就是该方法的锁;如果该方法是静态方法,则该方法的锁则是对象的class
示例代码:
public synchronized void method(int args){操作共享资源的代码 }
通过synchronized修饰代码块
概述:
- 代码块里配置的对象就是该代码块的锁
示例代码
synchronized(Obj){ 操作共享资源的代码 };
内置锁的可重入性
概述:
- 内置锁是可重入的,如果某个线程去获取一个有它自己持有的锁,那么这个请求就会成功。
- 重入原理:为每一个锁关联一个获取计数值和一个所有者线程。当计数值为0代表该锁没有被任何线程持有,同一个线程重复获取锁,计数器+1,当线程退出同步代码块时,计数器会相应地递减
意义:
- 提示加锁行为的封装性,因此简化了面向对象并发代码的开发
- 避免重复获取锁资源导致死锁:在线程执行一个需要同步带锁的方法,该方法内部又调用了另一个需要相同锁的方法。(可重入则不需要重复获取锁资源)