Synchronized锁及其原理(内有思维图)

在这里插入图片描述

并发问题

首先我们先来看一个并发问题的例子:老经典卖票例子。这里有100张票,分别定义1-10十个窗口去卖这一百张票。

package com.d.Lock;

import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA
 * Description:
 * Author: sudi
 * Date: 2020/11/6
 * TIME: 15:06
 */
public class SynchronizedTest {
    public static void main(String[] args) {
        Syn1 ticket = new Syn1();
        // @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 1; j < 100; j++) {
                    ticket.sale();
                }
            }, String.valueOf(i)).start();
        }
    }
}

class Syn1 {
    private int number = 100;
    
    // 卖票的方式
    public void sale() {
        if (number>0) {
            System.out.println(Thread.currentThread().getName() + "卖出了第" + number + "票");
            number--;
        }
    }
}

输出结果是:在这里插入图片描述
很显然这是不对的,怎么可能会有四张100号的票呢?这就是所谓线程的并发问题。

为什么多线程并发不安全?

多线程同时操作对象的属性或者状态时,会因为线程之间的信息不同步,B线程读取到的状态已经过时,而B线程并不知道。所以并发安全的本质问题在于线程之间的信息不同步!
在这里插入图片描述
图片来自:多线程并发为什么不安全

1.A已经获取到了queryTimes的值17但还未作修改,此时B正准备去获取query的值。
2.B获取到了queryTimes的值17的时候,A正准备做修改。
3.A修改query的值为18的时候,B正在准备修改queryTimes的值
4.A修改完成了,B正在修改了queryTimes的值,然后稍晚完成。

这就使得AB线程同时操作了同一个数据,但B读取的是脏数据。这也就是为什么会有四张100号票的原因。 根据上述,我们知道当竞态条件存在时,多个线程可能同时或者几乎同时读取到某个状态(值),然后将处理后到值进行写入,此时我们可以说发生了数据的"脏读"
在这里插入图片描述

Synchronized锁

那么既然出现了问题,我们就要去解决问题,那么Synchronized这个关键字出现了。Synchronized就是用来同步代码块的Java关键字,这个关键字属于隐式的锁,是 jvm 层面帮助我们实现的,使用的时候看不见。

Synchronized锁的实现原理(修饰方法和修饰代码块)

在这里插入图片描述

使用 javap -v Syn1.class命令来反编译一下这个类

在这里插入图片描述
从图中我们可以看到一个flag标志ACC_SYNCHRONIZED,JVM可以从方法常量池中的方法表结构(method_info Structure) 中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法。当方法调用时,调用指令将会 检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有monitor, 然后再执行方法,无论方法是正常完成还是非正常完成时释放monitor。
在这里插入图片描述
而同步代码块使用的是monitorenter和monitorexit这两个字节码指令。当jvm执行到monitorenter指令时,当前线程试图获取monitor对象的所有权,如果未加锁或者已经被当前线程所持有,就把锁的计数器+1;当执行monitorexit指令时,锁计数器-1;当锁计数器为0时,该锁就被释放了。如果获取monitor对象失败,该线程则会进入阻塞状态,直到其他线程释放该锁。

Synchronized的特性

  • 有序性
    即一个变量在同一时刻只允许一条线程对其加锁。
  • 可见性
    在解锁之前,会把内存强制刷新,即把获取的变量强制写回主内存。
  • 原子性
    synchronized保证语句块内操作是原子的,要么不执行要么执行,不可中断。
  • 可重入性
    被synchronized修饰的同步代码块对同一条线程来说是可重入的。这意味着同一线程反复进入同步代码块也不会出现自己把自己锁死的情况。如果已经被当前线程所持有,就把锁的计数器+1;当执行monitorexit指令时,锁计数器-1;当锁计数器为0时,该锁就被释放了。

synchronized与Lock锁的区别

  1. sychronized是关键字,是JVM层面的底层。Lock是接口,是JDK层面的,有丰富的API
  2. sychronized会自动释放锁,而Lock必须手动释放锁
  3. synchronized是不可中断的,Lock可以中断也可以不中断
  4. 通过Lock可以知道线程有没有拿到锁,而synchronized不能
  5. synchronized能锁住方法和代码块,而Lock只能锁住代码块
  6. Lock可以使用读锁提高多线程效率
  7. synchronized是非公平锁,ReentrantLock可以控制是否是公平锁
  8. sync的劣势:锁升级过程不可逆

synchronized与Java对象

MarkWord

在这里插入图片描述
JVM一般是这样使用锁和Mark Word的:

1,当没有被当成锁时,这就是一个普通的对象,Mark Word记录对象的HashCode,锁标志位是01,是否偏向锁那一位是0。

2,当对象被当做同步锁并有一个线程A抢到了锁时,锁标志位还是01,但是否偏向锁那一位改成1,前23bit记录抢到锁的线程id,表示进入偏向锁状态。

3,当线程A再次试图来获得锁时,JVM发现同步锁对象的标志位是01,是否偏向锁是1,也就是偏向状态,Mark Word中记录的线程id就是线程A自己的id,表示线程A已经获得了这个偏向锁,可以执行同步锁的代码。

4,当线程B试图获得这个锁时,JVM发现同步锁处于偏向状态,但是Mark Word中的线程id记录的不是B,那么线程B会先用CAS操作试图获得锁,这里的获得锁操作是有可能成功的,因为线程A一般不会自动释放偏向锁。如果抢锁成功,就把Mark Word里的线程id改为线程B的id,代表线程B获得了这个偏向锁,可以执行同步锁代码。如果抢锁失败,则继续执行步骤5。

5,偏向锁状态抢锁失败,代表当前锁有一定的竞争,偏向锁将升级为轻量级锁。JVM会在当前线程的线程栈中开辟一块单独的空间,里面保存指向对象锁Mark Word的指针,同时在对象锁Mark Word中保存指向这片空间的指针。上述两个保存操作都是CAS操作,如果保存成功,代表线程抢到了同步锁,就把Mark Word中的锁标志位改成00,可以执行同步锁代码。如果保存失败,表示抢锁失败,竞争太激烈,继续执行步骤6。

6,轻量级锁抢锁失败,JVM会使用自旋锁,自旋锁不是一个锁状态,只是代表不断的重试,尝试抢锁。从JDK1.7开始,自旋锁默认启用,自旋次数由JVM决定。如果抢锁成功则执行同步锁代码,如果失败则继续执行步骤7。

7,自旋锁重试之后如果抢锁依然失败,同步锁会升级至重量级锁,锁标志位改为10。在这个状态下,未抢到锁的线程都会被阻塞。其中的重量级锁也就是通常说 synchronized 的对象锁,其中指针指向的是 monitor 对象(也称为管程或监视器锁)的起始地址。每个对象都存在着一个 monitor 与之关联,monitor 是由ObjectMonitor 实现的,C++实现。
参考博客:Java的对象头和对象组成详解

实例数据

实例数据部分是对象真正存储的有效信息,即我们在程序代码里面所定义的各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的字段都必须记录起来。

对齐填充

并不是必然存在的,由于 Hotspot 虚拟机的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,如果对象实例数据部分没有对齐的话,就需要通过对齐填充来补全。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值