synchronized锁膨胀(附代码分析)

news2024/11/28 3:39:56

synchronized锁膨胀

1. 基本概念

Java对象头

Java对象的对象头信息中的 Mark Word 主要是用来记录对象的锁信息的。

现在看一下 Mark Word 的对象头信息。如下:
在这里插入图片描述

其实可以根据 mark word 的后3位就可以判断出当前的锁是属于哪一种锁。注意:表格中的正常锁其实就是无锁状态了。

2. 几种锁以及原理

无锁

正常创建的对象,状态为无锁。对象头的Mark Word 中主要记录了 对象的年龄,也就是经历了多少次GC还存活下来。

偏向锁

对象头的Mark Word 中记录的信息比正常锁多的是 记录了线程的信息,也就是线程的id。偏向锁,字面意思就是比较偏心,偏向于某个线程。

偏向锁加锁原理

经过分析可以看到,thread 字段刚开始的时候为0。工作原理是 cas(thread,0,当前线程), 也就是判断当前的 thread的值如果是0的话,就赋值为当前线程。如果cas 拿锁失败了,那么有俩种情况。

  • 重入

    当前线程已经是偏向锁的持有者了,那么只需要判断 thread 字段是否就是当前线程,如果是的话,其实就是当前线程已经拿到这把锁了。那么此时就会在栈中创建lockRecord。因为栈是线程私有的。lockRecord 其实包含俩部分的内容,第一部分内容和锁对象的 markword中的对象是完全一样的。记为M区。第二部分是记录了当前对象,也就是加锁对象的指针。记为O区。在第一次加锁的时候,是不需要创建 lockRecord的。只有在重入的时候,才需要创建 lockRecord的。

  • 其它线程持有锁

    那么此时就会升级为轻量级锁。

偏向锁解锁原理

解锁过程非常简单,只需要在当前线程的栈上删除最近的lockRecord对象就可以了。因为重入的时候是不断的创建这些lockRecord对象的。

轻量级锁

主要是将锁对象的Mark Word更新为指向Lock Record的指针,也就是锁记录。注意,这个锁记录是在栈上的。

轻量级锁加锁原理

  1. 线程在自己的栈桢中创建锁记录 LockRecord。
    在这里插入图片描述

  2. 将锁对象的对象头中的MarkWord复制到线程的刚刚创建的锁记录中。

  3. 将锁对象的对象头的MarkWord替换为指向锁记录的指针。也就是进行cas操作。cas(ptr, null, lockRecord)。也就是如果对象头的ptr指针如果为空,那么就赋值为当前的lockRecord对象。并将线程栈帧中的Lock Record里的owner指针指向Object的 Mark Word。如果赋值成功了,那么就可以认为加锁成功了。
    在这里插入图片描述

如果加锁失败了,也有俩种情况。

  • 重入。

    就是ptr 指向的是另外的一个lockRecord对象,但是也是当前线程创建的。也就是在当前线程的栈上是可以找到这个lockRecord对象。首先在栈上检查是否可以找到这个锁对象。如果可以找到,就是重入。如果是重入,那么就需要将锁记录中的Owner指针指向锁对象。

  • 其它线程持有锁

    那么就进入重量级锁。

轻量级锁解锁原理

释放锁的时候,从栈上进行扫描,从后往前 找到最近的lockRecord,删除。

重量级锁

主要是指向一个monitor 对象。分析如下:
Synchronized重量级锁分析

3. 看一段程序,看一下锁是如何进行演变的。

package org.example;



import sun.misc.Unsafe;

import java.lang.reflect.Field;
/**
 * @author frank wy170862@alibaba-inc.com
 * @date 2020-02-24
 */
public class A {
    public static void main(String[] args) throws Exception {
        test2();
    }
    /**
     * 一、正常创建的对象,状态为无锁,观察hashcode和age的变化。hashcode 的计算是惰性的。刚开始的时候,对象的hashcode是0。只有在计算了hashcode以后,才会将hashcode存储到对象头,下次取hashcode的时候,就可以直接从对象头中取出hashcode了。
     *
     * 锁状态:无锁,hashCode:0,age: 0
     * ---------------
     *
     * 运行hashcode方法,得到hashcode:648129364
     * 锁状态:无锁,hashCode:648129364,age: 0
     * ---------------
     *
     * [GC (System.gc()) [PSYoungGen: 6568K->1095K(76288K)] 6568K->1103K(251392K), 0.0017301 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
     * [Full GC (System.gc()) [PSYoungGen: 1095K->0K(76288K)] [ParOldGen: 8K->932K(175104K)] 1103K->932K(251392K), [Metaspace: 3084K->3084K(1056768K)], 0.0054386 secs] [Times: user=0.02 sys=0.01, real=0.01 secs]
     * 运行一次gc,obj的age+1
     * 锁状态:无锁,hashCode:648129364,age: 1
     * ---------------
     *  @throws Exception
     */
    private static void test1() throws Exception {
        Object a = new Object();
        printLockHeader(a);
        System.out.println("运行hashcode方法,得到hashcode:" + a.hashCode());;
        printLockHeader(a);
        System.gc();
        System.out.println("运行一次gc,obj的age+1");
        // sleep 1s 让gc完成,但是不一定能100%触发gc,可以配合添加运行参数 -XX:+PrintGCDetails,观察确实gc了
        Thread.sleep(1000);
        printLockHeader(a);
    }
    /**
     * 二、正常创建的对象,状态为无锁,无锁状态直接加锁会变成轻量锁
     *
     * 锁状态:无锁,hashCode:0,age: 0
     * ---------------
     *
     * 对a加锁后
     * 锁状态:轻量级锁,LockRecord地址:1c00010c6e28
     * ---------------
     * @throws Exception
     */
    private static void test2() throws Exception {
        Object a = new Object();
        printLockHeader(a);
        synchronized (a){
            System.out.println("对a加锁后");
            printLockHeader(a);
        }
    }
    /**
     * 三、程序启动一定时间后,正常创建的对象,状态为偏向锁且thread为0,此时加锁默认为偏向锁
     * 一段时间一般是几秒,-XX:BiasedLockingStartupDelay=0可以指定默认就使用偏向锁,而不是无锁
     *
     * 锁状态:偏向锁,thread:0,epoch: 0,age: 0
     * ---------------
     *
     * 对a加锁后
     * 锁状态:偏向锁,thread:137069895700,epoch: 0,age: 0
     * ---------------
     *
     * 偏向锁重入后
     * 锁状态:偏向锁,thread:137069895700,epoch: 0,age: 0
     * ---------------
     * @throws Exception
     */
    private static void test3() throws Exception {
        Thread.sleep(5*1000);
        Object a = new Object();
        printLockHeader(a);
        synchronized (a){
            System.out.println("对a加锁后");
            printLockHeader(a);
            System.out.println("偏向锁重入后");
            synchronized (a){
                printLockHeader(a);
            }
        }
    }
    /**
     * 四、基于三,当另一个线程尝试使用对象锁的时候,升级为轻量锁
     *
     * 锁状态:偏向锁,thread:0,epoch: 0,age: 0
     * ---------------
     *
     * 线程1对a加锁后
     * 锁状态:偏向锁,thread:137122299998,epoch: 0,age: 0
     * ---------------
     *
     * 锁释放了
     * 线程2对a加锁后
     * 锁状态:轻量级锁,LockRecord地址:1c00015e6a18
     * ---------------
     * @throws Exception
     */
    private static void test4() throws Exception {
        Thread.sleep(5*1000);
        Object a = new Object();
        printLockHeader(a);
        new Thread(
                ()->{
                    synchronized (a){
                        System.out.println("线程1对a加锁后");
                        try {
                            printLockHeader(a);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
        ).start();
        // 中间sleep1s,保证锁释放掉,使两个线程不会有竞争关系
        Thread.sleep(1000);
        System.out.println("锁释放了");
        new Thread(
                ()->{
                    synchronized (a){
                        System.out.println("线程2对a加锁后");
                        try {
                            printLockHeader(a);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
        ).start();
    }
    /**
     * 五、基于四,当产生竞争的时候偏向锁直接升级为重量级锁
     *
     * 锁状态:偏向锁,thread:0,epoch: 0,age: 0
     * ---------------
     *
     * 线程1对a加锁后
     * 锁状态:偏向锁,thread:137025283340,epoch: 0,age: 0
     * ---------------
     *
     * 线程2对a加锁后
     * 锁状态:重量级锁,Monitor地址:1fe758800a02
     * ---------------
     * @throws Exception
     */
    private static void test5() throws Exception {
        Thread.sleep(5*1000);
        Object a = new Object();
        printLockHeader(a);
        new Thread(
                ()->{
                    synchronized (a){
                        System.out.println("线程1对a加锁后");
                        try {
                            printLockHeader(a);
                            Thread.sleep(1000);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
        ).start();
        new Thread(
                ()->{
                    synchronized (a){
                        System.out.println("线程2对a加锁后");
                        try {
                            printLockHeader(a);
                            Thread.sleep(1000);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
        ).start();
    }
    /**
     * 六、四+五 演示偏向-轻量-重量过程
     *
     * 锁状态:偏向锁,thread:0,epoch: 0,age: 0
     * ---------------
     *
     * 线程1对a加锁后
     * 锁状态:偏向锁,thread:137272648594,epoch: 0,age: 0
     * ---------------
     *
     * 锁释放
     * 线程2对a加锁后
     * 锁状态:轻量级锁,LockRecord地址:1c0001b43e18
     * ---------------
     *
     * 锁释放
     * 线程3对a加锁后
     * 锁状态:轻量级锁,LockRecord地址:1c0001b43e18
     * ---------------
     *
     * 线程4对a加锁后
     * 锁状态:重量级锁,Monitor地址:1ff616600f02
     * ---------------
     * @throws Exception
     */
    private static void test6() throws Exception {
        Thread.sleep(5*1000);
        Object a = new Object();
        printLockHeader(a);
        new Thread(
                ()->{
                    synchronized (a){
                        System.out.println("线程1对a加锁后");
                        try {
                            printLockHeader(a);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
        ).start();
        // 中间sleep1s,使线程不会有竞争关系
        Thread.sleep(1000);
        System.out.println("锁释放");
        new Thread(
                ()->{
                    synchronized (a){
                        System.out.println("线程2对a加锁后");
                        try {
                            printLockHeader(a);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                }
        ).start();
        // 中间sleep1s,使线程不会有竞争关系
        Thread.sleep(1000);
        System.out.println("锁释放");
        new Thread(
                ()->{
                    synchronized (a){
                        System.out.println("线程3对a加锁后");
                        try {
                            printLockHeader(a);
                            Thread.sleep(1000);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
        ).start();
        // 此时不再sleep,使线程必然发生竞争,升级为重量级锁
        new Thread(
                ()->{
                    synchronized (a){
                        System.out.println("线程4对a加锁后");
                        try {
                            printLockHeader(a);
                            Thread.sleep(1000);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
        ).start();
    }
    private static Unsafe getUnsafe() throws Exception {
        Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
        Field field = unsafeClass.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        return  (Unsafe) field.get(null);
    }
    private static void printLockHeader(Object obj) throws Exception {
        Unsafe us = getUnsafe();
        StringBuilder sb = new StringBuilder();
        int status = us.getByte(obj, 0L) & 0B11;
        // 0 轻量级 1 无锁或偏向 2 重量级 3 GC标记
        switch (status){
            case 0:
                // ptr_to_lock_record:62|lock:2
                long ptrToLockRecord =
                        (byteMod(us.getByte(obj, 0L))>>2) +
                                (byteMod(us.getByte(obj, 1L))<<6) +
                                (byteMod(us.getByte(obj, 2L))<<14) +
                                (byteMod(us.getByte(obj, 3L))<<22) +
                                (byteMod(us.getByte(obj, 4L))<<30) +
                                (byteMod(us.getByte(obj, 5L))<<38) +
                                (byteMod(us.getByte(obj, 6L))<<46) +
                                (byteMod(us.getByte(obj, 7L))<<54);
                sb.append("锁状态:轻量级锁,LockRecord地址:")
                        .append(Long.toHexString(ptrToLockRecord))
                ;
                break;
            case 1:
                boolean biased = (us.getByte(obj, 0L)&4) == 4;
                if(!biased){
                    // unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2
                    int hashCode = (int)(byteMod(us.getByte(obj, 1L))
                            + (byteMod(us.getByte(obj, 2L))<<8)
                            + (byteMod(us.getByte(obj, 3L))<<16)
                            + ((byteMod(us.getByte(obj, 4L))&Integer.MAX_VALUE) <<24))
                            ;
                    int age = (us.getByte(obj,0L)>>3)&0B1111;
                    sb.append("锁状态:无锁,hashCode:")
                            .append(hashCode)
                            .append(",age: ")
                            .append(age);
                }else{
                    //thread:54|epoch:2|unused:1| age:4 | biased_lock:1 | lock:2
                    long thread = (byteMod(us.getByte(obj, 1L))>>2) +
                            (byteMod(us.getByte(obj, 2L))<<6) +
                            (byteMod(us.getByte(obj, 3L))<<14) +
                            (byteMod(us.getByte(obj, 4L))<<22) +
                            (byteMod(us.getByte(obj, 5L))<<30) +
                            (byteMod(us.getByte(obj, 6L))<<38) +
                            (byteMod(us.getByte(obj, 7L))<<46);
                    ;
                    int epoch = us.getByte(obj, 1L) & 0B11;
                    int age = (us.getByte(obj,0L)>>3)&0B1111;
                    sb.append("锁状态:偏向锁,thread:")
                            .append(thread)
                            .append(",epoch: ")
                            .append(epoch)
                            .append(",age: ")
                            .append(age);
                }
                break;
            case 2:
                // ptr_to_heavyweight_monitor:62| lock:2
                long ptrToMonitor =
                        (byteMod(us.getByte(obj, 0L))>>2) +
                                (byteMod(us.getByte(obj, 1L))<<6) +
                                (byteMod(us.getByte(obj, 2L))<<14) +
                                (byteMod(us.getByte(obj, 3L))<<22) +
                                (byteMod(us.getByte(obj, 4L))<<30) +
                                (byteMod(us.getByte(obj, 5L))<<38) +
                                (byteMod(us.getByte(obj, 6L))<<46) +
                                (byteMod(us.getByte(obj, 7L))<<54);
                sb.append("锁状态:重量级锁,Monitor地址:")
                        .append(Long.toHexString(ptrToMonitor))
                ;
                break;
            case 3:
                sb.append("锁状态:GC标记");
                break;
            default:
                break;
        }
        if(obj instanceof Object[]){
            int arrLen = us.getInt(obj, 3L);
            sb.append("对象为数组类型,数组长度:")
                    .append(arrLen);
        }
        sb.append("\n").append("---------------").append("\n");
        System.out.println(sb.toString());
    }
    private static long byteMod(byte b){
        if(b>=0){
            return b;
        }
        return b + 256;
    }
}


4. 总结

刚开始,程序开始运行的时候,创建的对象都属于无锁对象。程序运行一段时间后,一段时间一般指的是4秒钟,创建的对象属于偏向锁对象。无锁状态下直接加锁会变为轻量级锁。偏向锁状态下的对象,加锁的话,会对当前线程有一个偏向。如果此时再有另外的一个线程过来申请锁,那么就会升级为轻量级锁对象。轻量级锁下,如果存在竞争,那么一定会升级为重量级锁。当然,偏向锁状态下,如果存在竞争,也会升级为重量级锁。

总结起来就是:如果存在竞争,那么一定会升级为重量级锁。如果存在另外一个线程想要拿到这把锁,就会升级为轻量级锁。偏向锁和轻量级锁的区别是:轻量级锁是可以另外一个线程来拿锁,也就是一个线程释放掉锁以后,另外一个线程可以来拿锁,这就是轻量级锁。如果一个线程释放掉锁以后,另外一个线程拿不到这把锁,那么就属于偏向锁。

鸣谢

java 偏向锁、轻量级锁及重量级锁synchronized原理

synchronized和锁优化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/153229.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

shell脚本练习2023年下岗版

shell脚本练习 1.判断指定进程的运行情况 #!/bin/bash NAMEhttpd #这里输入进程的名称 NUM$(ps -ef |grep $NAME |grep -vc grep) if [ $NUM -eq 1 ]; thenecho "$NAME running." elseecho "$NAME is not running!" fi2.判断用户是否存在 #!/bin/bash r…

【RabbitMQ】安装、启动、配置、测试一条龙

一、基本环境安装配置 1.英文RabbitMQ是基于erlang开发的所以需要erlang环境,点击以下链接下载安装 Downloads - Erlang/OTP 2.官网下载RabbitMQ安装包并安装 Installing on Windows — RabbitMQ 3.配置erlang本地环境变量(和JAVAHOME类似) 4.cmd查看erlang版本 5.点击以下…

自己看的操作系统

计算机网络冯诺依曼体系进程线程内核和虚拟内存os管理线程冯诺依曼体系 计算机五大组成&#xff1a;输入设备、输出设备、控制器、运算器、存储器 进程线程 这些应用都是进程 进程相当于一个菜谱&#xff0c;读取到内存中去使用。 电脑一时间能运行很多进程。 进程中为什么要…

excel函数技巧:MAX在数字查找中的应用妙招

大家都知道VLOOKUP可以按给定的内容去匹配到我们所需的数据&#xff0c;正因为如此&#xff0c;它在函数界有了很大的名气。但是今天要分享的这三个示例&#xff0c;如果使用VLOOKUP去匹配数据的话&#xff0c;就有些麻烦了。就在VLOOKUP头疼不已的时候&#xff0c;MAX函数二话…

2022 年度总结

1、CSDN 年度总结 2022年的粉丝涨幅比较明显竟然超过了之前几年的总和&#xff0c;这是比较意外的。应该是因为今年研究了一些云原生、元宇宙的原因&#xff0c;方向比努力真的重要的多。 1500的阅读确实没想到~~~说明低头一族还是没白当 涨粉稍微明细&#xff0c;不过还需…

English Learning - L1-11 时态 + 情态动词 2023.1.9 周一

English Learning - L1-11 时态 情态动词 2023.1.9 周一8 时态8.4 完成进行时&#xff08;一&#xff09;现在完成进行时核心思维&#xff1a;动作开始于现在之前&#xff0c;并有限地持续下去&#xff0c;动作到目前为止尚未完成1. 动作从过去某时开始一直持续到现在并可能继…

【Python】如何使用python将一个py文件变成一个软件?

系列文章目录 这个系列文章将写一些python中好玩的小技巧。 第一章 使用Python 做一个软件 目录 系列文章目录 前言 一、第一步&#xff1a;写好文件 二、第二步&#xff1a;生成程序 1.安装库 2.使用安装的库进行转化 总结 前言 本文重点说如何将py文件转化为exe文件…

回溯法--符号三角形(杂记)

回溯法说来简单&#xff0c;写起来难&#xff0c;真的是要愁死。回溯法有两种模板--子集树、排列树5.4符号三角形--dfs计算多少个满足条件的符号三角形&#xff0c;同号下面为“”&#xff0c;异号下面为“-”。根据异或的规则我们令“”0&#xff0c;“-”1&#xff0c;(异或的…

postgresql 启用ssl安全连接方式

SSL的验证流程 利用openssl环境自制证书 CA 证书签发 创建私钥ca.key,使用des3算法,有效期2048天 openssl genrsa -des3 -out ca.key 2048生成根CA证书请求&#xff08;.csr&#xff09; openssl req -new -key ca.key -out ca.csr -subj "/CCN/STGuangDong/LGuangZhou…

Cloudflare免费版不支持cname解析解决办法

最近调整CDN&#xff0c;使用云盾CDN的话基本上节点都在国内&#xff0c;国外访问就比较难了&#xff0c;虽然我们的站国外用户基本没有&#xff0c;但作为一个有大抱负的站长&#xff0c;眼界必须得宽&#xff0c;必须得支持国外访问才行&#xff01;说起国外免费CDN&#xff…

iOS开发之Code:-402653103,Code:5

问题一&#xff1a;Code&#xff1a;-402653103 Demo中添加了第三方库&#xff0c;然后运行Demo时&#xff0c;总是运行不起来&#xff0c;现象如下&#xff1a; 遇到这种问题常见的几种方式&#xff1a; 方式一&#xff1a;command shift K&#xff0c;清理Xcode缓存&…

常用的字符串与内存操作函数(1)

Tips 1. 2. 3. 在进行数值计算的时候&#xff0c;补码能算对&#xff0c;因此计算机里面放的都是补码&#xff0c;运算的对象都是补码 但是与真实数值吻合的是原码&#xff0c;因此打印&#xff0c;求值等都要转化为原码 4. for (exp1 ; exp2 ; exp3)&#xff0c;是先…

从0到1完成一个Vue后台管理项目(十九、地图区域样式设置、区域文字和立体设置)

往期 从0到1完成一个Vue后台管理项目&#xff08;一、创建项目&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;二、使用element-ui&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;三、使用SCSS/LESS&#xff0c;安装图标库&#xff09; 从0到1完成一个Vu…

上半年要写的博客文章25

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…

分享86个NET源码,总有一款适合您

NET源码 分享86个NET源码&#xff0c;总有一款适合您 链接&#xff1a;https://pan.baidu.com/s/1JOY-9pJIM7sUhafxupMaZw?pwdfs2y 提取码&#xff1a;fs2y 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c;大家下载…

Blender里的三种绑定:(一)主从绑定

文章目录Blender里的三种绑定.主从绑定.进行物体绑定.进行顶点绑定.解除绑定.保持变换.无反向.进行晶格绑定.Blender里的三种绑定. 1 Blender中一共有三种绑定模式&#xff0c;分别是 主从绑定&#xff0c;约束&#xff0c;骨骼 主从绑定. 1 主从绑定即父子关系&#xff0c;…

【Spark常用算子合集】一文搞定spark中的常用转换与行动算子

&#x1f680; 作者 &#xff1a;“大数据小禅” &#x1f680;文章简介&#xff1a;本篇文章属于Spark系列文章&#xff0c;专栏将会记录从spark基础到进阶的内容 &#x1f680; 内容涉及到Spark的入门集群搭建&#xff0c;核心组件&#xff0c;RDD&#xff0c;算子的使用&…

【数据结构与算法】——第六章:图

文章目录1、图的定义1.1 图的其他定义1.2 图的顶点与边之间的关系1.3 连通图相关术语2、图的存储结构2.1 邻接矩阵2.2 邻接表3、图的遍历3.1 深度优先遍历3.2 广度优先遍历4、最小生成树4.1 普利姆算法(Prim)4.2 克鲁斯卡尔(kruskal)5、最短路径5.1 迪杰斯特拉(Dijkstra)算法5.…

Sentinel限流-@SentinelResource注解配置

SentinelResource 配置-上 &#xff08;按资源名配置限流规则&#xff09; 1&#xff09; Sentinel 控制台配置流控规则&#xff1a; 2&#xff09;java 代码&#xff1a; GetMapping("/byResource")SentinelResource(value "byResource", blockHandler …

Django项目——通过APIView实现API访问

前提 该文章在已有项目的基础上进行修改 https://blog.csdn.net/qq_38122800/article/details/128583379?spm1001.2014.3001.5502 1、配置序列化器 序列化器包含序列化和反序列化两个过程,简单点理解就是 序列化 : 将从数据库中查的数据变为前端页面可以接受的json数据 反…