总结
- 一个锁对象只能同时被一个线程持有,分为对象锁和类锁
- 对象锁:每个对象都可以作为锁(几个不同的对象就是几个锁)
- 类锁:字节码对象也能作为锁(全局唯一)
- 同步方法不能自定义锁,只能是默认的锁(非静态:this,静态:class);同步代码块默认的锁和方法一样(非静态:this,静态:class,普通方法里面可以有同步代码块),但是可以自定义锁
- 同步方法,执行完毕或抛出异常都会释放锁
- 是重入锁
- 可以保证 原子性、可见性、有序性
使用
//这个用法就是使用了obj的锁,来锁定一个代码块。
synchronized(obj){
// some code...
}
//对整个方法上锁,这个时候它使用的是当前实例this的锁,和下面的效果等同
public synchronized void aMethod(){
// some code...
}
// 普通方法包含同步代码块,这种情况下等同于同步方法
public void aMethod(){
synchronized(this){
// some code...
}
}
加锁原理
每个对象锁或类锁都会有一个 监视器,这个是关键,在对象头中,每个监视器同一时间只能被一个线程获取,根据监视器的锁计数器控制(如果计数器大于1,别的线程就获取不到锁的监视器,当前线程可以重复获取,重入锁)
- monitor 计数器为0,意味着当前还没有线程获得,当前线程就会立刻获得然后把锁计数器+1,一旦+1,别的线程再想获取,就需要等待
- 如果这个monitor已经拿到了这个锁的所有权,又重入了这把锁,那锁计数器就会累加,变成2,并且随着重入的次数,会一直累加
- 这把锁已经被别的线程获取了,等待锁释放
monitorenter
指令让计数器+1,monitorexit
指令让计数器-1
多线程并发执行,怎么使多个线程获取到的监视器的锁计数器是一致的呢?其实就是可见性的实现原理,不清楚,下面有一些说明
原子性
在一次或多次操作中,要么所有的操作都执行,并且不会受其他因素干扰而中断,要么所有的操作都不执行
- 因为互斥性,同一时间只能一个线程执行这段代码,天然具有原子性
有序性
是指程序代码在执行过程中的先后顺序,由于java在编译器以及运行期的优化,导致了代码的执行顺序未必就是开发者编写代码的顺序
- 因为互斥性,同一时间只能一个线程执行这段代码,压根不会有成指令重排的问题
可见性
- https://www.pdai.tech 说是 内存模型和 happens-before
- 有的说是 lock 指令(写完共享变量后发送一条 lock 的指令,把工作内存的值回写到主内存中)
- 有的说是内存屏障
jdk1.6 对 synchronized 的优化
其实就是锁膨胀,1.6 之前初始都是重量级锁,1.6 之后做了优化,先是无锁,最后才是重量级锁,具体请看前一篇文章 java 各种锁
标签:java,synchronized,代码,计数器,线程,监视器,多线程 From: https://www.cnblogs.com/hangychn/p/17218555.html