多线程 - java 关键字 synchronized 详解

总结

  • 一个锁对象只能同时被一个线程持有,分为对象锁和类锁
    • 对象锁:每个对象都可以作为锁(几个不同的对象就是几个锁)
    • 类锁:字节码对象也能作为锁(全局唯一)
  • 同步方法不能自定义锁,只能是默认的锁(非静态: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 各种锁

posted @ 2023-05-24 16:11  CyrusHuang  阅读(34)  评论(0编辑  收藏  举报