synchronized与volatile关键字的实现原理

2024-09-18 10:18:55 浏览数 (4)

synchronized

作用域

  • 作用于实例方法,静态方法(类方法),与代码块上,采用悲观锁,当有线程执行到相关资源是,其他线程会被阻塞
  • 保证原子性,有序性,可见性

实现原理

synchronized修饰于代码片段和静态方法,对于带代码片段或者非静态方法,synchronized是通过头对象中的MarkWord的信息来判断,MarkWord中有锁的类型,锁的地址等信息,对于静态方法,通过.Class文件来判断是否加锁.

在jdk1.6之前synchronized实现的方式就是重量锁,即通过操作系统的互斥量来实现加锁,在1.6之后出现了偏向锁,轻量锁,重量锁仍保留,作为等级最高的锁,锁的升级通过由JVM根据多线程下,等待线程的等待时间来判断是否升级,该参数可以进行手动修改

偏向锁,MarkWord字段存储的是线程ID,当有线程占用资源时,将线程id记录到MarkWord,其他线程访问资源时,发现MarkWord字段有值就会执行空循环等待(空循环不会导致阻塞,线程执行起来效率更高),当同一线程多次请求时,无需任何操作,效率得到提升,如果多线程情况下,锁的竞争激烈,那么该锁不适用

轻量锁,MarkWord字段储存的是锁的地址,锁存储在线程的栈帧中,如果其他线程等待时间过长,那么将会进入队列中,锁升级为重量级锁,同时采用非公平锁,其他线程过来之后也是等待一段时间在进队列,如果在等待的时间中,锁被释放,那么该线程将会优先执行,因为总体效率更高

自旋锁,轻量级锁失败后,虚拟机为了避免线程真实地在操作系统层面挂起,还会进行一项称为自旋锁的优化手段。这是基于在大多数情况下,线程持有锁的时间都不会太长,如果直接挂起操作系统层面的线程可能会得不偿失,毕竟操作系统实现线程之间的切换时需要从用户态转换到核心态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,因此自旋锁会假设在不久将来,当前的线程可以获得锁,因此虚拟机会让当前想要获取锁的线程做几个空循环(这也是称为自旋的原因),一般不会太久,可能是50个循环或100循环,在经过若干次循环后,如果得到锁,就顺利进入临界区。如果还不能获得锁,那就会将线程在操作系统层面挂起,这就是自旋锁的优化方式,这种方式确实也是可以提升效率的。最后没办法也就只能升级为重量级锁了。

重量级锁,通过操作系统层面的互斥量来实现

volatile

作用域

作用于变量上

实现原理

volatile的特性为可见性和有序性,当一个线程对volatile修饰变量进行写操作时,jvm会立即将该变量强制刷入主内存中,当其他变量读取值时,会直接从主内存中读取

通过禁止指令重排保证有序性,具体实现是通过内存屏障来实现的,在每一条指令前后加上屏障指令,只有前面的指令执行完后才能执行后面的指令

0 人点赞