多线程-syncronized的应用与原理分析

synronized许多开发人员看到就认为是重量级锁,其实不然,JVM对它定义有很多种状态。偏向锁->轻量锁->重量锁。这其中有很多的优化过程,业务概念被称为锁膨胀,锁消除,锁撤销。下面来具体分析一下。

首先syncronized本质作用于对象上,修饰普通方法锁this对象,修饰静态方法锁类对象。以下代码说明。

@Slf4j
public class SyncronThread03 {
    //以下代码效果一样
    public synchronized void say(){
        log.info("say");
    }
    public  void say1(){
        synchronized (this) {
            log.info("say");
        }
    }

    //以下代码效果一样
    public synchronized static void write(){
        log.info("write");
    }
    public static void write1(){
        synchronized (SyncronThread03.class) {
            log.info("write");
        }
    }
}

既然作用于对象,那么肯定与对象有一定关系。java对象分为对象头和对象体。syncronized底层是C实现,通过字节码指令monitorenter,monitorexit执行。syncronized关键字在操作系统中会有一个monitor对象的概念 ,然后通过monitor对象与java对象头中的markword进行关联。monitor对象是由owner,EntryList,WaitSet三部分组成,三部分的作用可以举个实例。当有多个线程同时对某个对象加锁,一个时间点只能有一个能成功,此时设置owner为当前线程ID,其它线程尝试加锁时发现owner已经被持有,那么就会进入阻塞(其实在JVM层面还有个自旋尝试获取锁的过程,尝试几次才会阻塞),这些线程就会进入EntryList。如果有线程在执行过程中调用了wait方法(会释放锁),那么这些线程会进入WaitSet,这部分线程需要notify唤醒。当持有锁线程结束,会将owner置为null,然后唤醒EntryList线程进行锁竞争。以下为加锁图示。

了解完sycronized的monitor,然后再看java对象头中的markword。markword主要为以下图中,简单说明,第一行属于正常对象,第二行属于偏向锁。区别就是biased_lock不一致。第三行属于轻量级锁,此时存储的是栈帧中的锁指针+两位标识符(00)。第四行属于重量级锁,存储的是monitor中的锁指针+两位标识符(10)。在加锁和解锁过程中java对象就是通过改变信息这些来控制锁状态。

 以上分开介绍monitor与markword,下面举个实际例子说明一下。线程T0现在需要进行加锁操作,作用对象为object。当调用syncronized(object)前,object属于一个正常对象,锁标识后三位001,State为Normal,当调用syncronized(object),object会有两种情况,如果没有禁用偏向锁则State为Biased,即后三位为101,如果禁用偏向锁State为Lightweight Locked。此时object的对象头中就会存储栈帧锁记录及对应的指针,锁标识后两位00。如果是属于锁重入,则会在当前栈帧中新增一份lock record。重入次数为lock记录数。以下图示说明。

 最后一个是重量级锁。重量级锁是发生在锁竞争时锁升级而成,并没有说一开始的重量级。继上续例子,在T0加锁的同时,另一线程T1也来争抢object对象锁。那此时object的对象头中就会存储monitor对应的指针,锁标识后两位10。此时的解锁流程T0也是从monitor对象中释放到owner的占用。然后恢复到无锁状态。再由其它线程争抢锁。

OK!以上均是理论分析,下面上代码实际看到效果。

扫描二维码关注公众号,回复: 15817038 查看本文章

偏向锁 在VM参数加-XX:BiasedLockingStartupDelay=0:

import com.example.demostartertest.common.SleepUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.openjdk.jol.info.ClassLayout;

@Slf4j
public class SyncronThread02 {
    public static void main(String[] args) {
        Dog dog = new Dog(); 
        log.info("markword{}",ClassLayout.parseInstance(dog).toPrintable());//原始对象,开了偏向锁,最后位为101
   
        synchronized (dog){
            log.info("markword{}",ClassLayout.parseInstance(dog).toPrintable());//第一次上锁,偏向锁,最后位101
        }

        log.info("markword{}",ClassLayout.parseInstance(dog).toPrintable());
//正常状态,最后位101
    }
}

@Data
class Dog{
    private String name;
}

轻量级锁 (不开偏向锁)

 重量级锁 (不开偏向锁)

@Slf4j
public class SyncronThread02 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        log.info("markword{}",ClassLayout.parseInstance(dog).toPrintable());//原始 不偏向 001
        new Thread(()->{
            synchronized (dog){
                log.info("thread markword{}",ClassLayout.parseInstance(dog).toPrintable());//锁竞争,升级重量级 010
                SleepUtils.sleep(2);

            }
        },"t1").start();

        synchronized (dog){
            log.info("markword{}",ClassLayout.parseInstance(dog).toPrintable()); //轻量级 000
        }

        SleepUtils.sleep(3);
        log.info("markword{}",ClassLayout.parseInstance(dog).toPrintable()); //无锁 不偏向 001

    }
}

@Data
class Dog{
    private String name;
}

 重量级锁 (开偏向锁)

猜你喜欢

转载自blog.csdn.net/weixin_42740540/article/details/124178343