第十四章: ReentrantLock、ReentrantReadWriteLock、StampedLock

news2024/10/7 14:31:49

  • 相关面试题
  • 锁的演变
  • ReentrantReadWriteLock
    • 锁降级
    • 案例演示一
    • 案例演示二
    • 总结
  • 为什么要有锁降级
  • 邮戳锁 StampedLock
    • 邮戳锁的特点
    • 案例演示一
    • 案例演示二
    • StampedLock 缺点

相关面试题

  • 你说你用过读写锁,锁饥饿问题是什么?
  • 有没有比读写锁更快的锁?
  • StampedLock知道吗?(邮戳锁/票据锁)
  • ReentrantReadWriteLock有锁降级机制策略你知道吗?

锁的演变

无锁 ——> 独占锁 ——> 读写锁 ——> 邮戳锁

无锁: 不用多说,存在线程安全问题

独占锁: ==(synchronized,ReentrantLock)==同一时刻只能有一个线程访问,在读多写少的场景下,显然效率并不高。

读写锁:ReentrantReadWriteLock , 适应在多读写少的场景下,读锁可以被线程共享(共享锁),写锁只能有一个线程获取(排它锁)。读的时候不允许写,写的时候不允许读。

读写锁缺点

  • 写锁饥饿问题
  • 锁降级问题

邮戳锁StampedLock 由于读写锁写锁饥饿问题,出现了邮戳锁。下面详细讲解…

ReentrantReadWriteLock

一个资源能够被多个读线程访问,或者被一个写线程访问,但是不能同时存在读写线程。

想象成菜刀,刀刃切菜,刀背拍蒜,但是你不能同时拍蒜又切菜

image-20221207214625614

读写锁特点

  • 读写锁并不是真正意义上的读写分离,它只允许读读共存,而读写和写写依然是互斥的,大多实际场景是“读/读”线程间并不存在互斥关系,只有"读/写"线程或"写/写"线程间的操作需要互斥的。因此引入ReentrantReadWriteLock。

  • 一个ReentrantReadWriteLock同时只能存在一个写锁但是可以存在多个读锁,但不能同时存在写锁和读锁(切菜还是拍蒜选一个)。

  • 也即一个资源可以被多个读操作访问或一个写操作访问,但两者不能同时进行。

只有在读多写少情境之下,读写锁才具有较高的性能体现

ReentrantReadWriteLock 架构图

实现了ReadWriteLock 接口,其中俩个方法,readLock获取读锁,writeLock 获取写锁

image-20221207220748126

代码演示

模仿多个线程进行读写的场景。演示独占锁、读写锁的区别

使用 ReentrantLock 独占锁

public class ReentrantReadWriteLockTest {
    public static void main(String[] args) {
        MapResource mapResource = new MapResource();
        for (int i = 1; i <= 10; i++) {
            //  lambda 表达式使用变量应为 final 类型
            final int finalI = i;
            new Thread(() -> {
                mapResource.put(finalI,finalI);
            },String.valueOf(i)).start();

            new Thread(() -> {
                mapResource.get(finalI);
            },String.valueOf(i)).start();
        }
    }
}

class MapResource {
    private  volatile Map<Integer,Integer> map = new HashMap<>();
    // 独占锁==synchronized
     ReentrantLock lock = new ReentrantLock();
    // 读写锁
     ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    // 写操作
    public  void put(Integer key,Integer value) {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在进行写操作....");
            TimeUnit.MILLISECONDS.sleep(200);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName() + " 写操作完成....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    // 读操作
    public  Integer get(Integer key) {
        lock.lock();
        Integer result = null ;
        try {
            System.out.println(Thread.currentThread().getName() + " 正在进行读操作....");
            TimeUnit.MILLISECONDS.sleep(100);
            result = map.get(key);
            System.out.println(Thread.currentThread().getName() + " 读操作完成, 读取的值为: " + result);
            return  result ;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return  null;
        }finally {
            lock.unlock();
        }
    }
}

输出结果

无论是读还是写,都只允许一个线程进入。都是成双成对的出现。这样不仅效率降低 ,并且读操作没有达到共享

比如:电影院看电影,看电影相当于读操作,我们不能每一个电影只有一个人看,而是多个顾客都能够看,因此也就引入了读写锁

image-20221207222232228

演示读写锁

public class ReentrantReadWriteLockTest {
    public static void main(String[] args) {
        MapResource mapResource = new MapResource();
        for (int i = 1; i <= 10; i++) {
            //  lambda 表达式使用变量应为 final 类型
            final int finalI = i;
            new Thread(() -> {
                mapResource.put(finalI,finalI);
            },String.valueOf(i)).start();

            new Thread(() -> {
                mapResource.get(finalI);
            },String.valueOf(i)).start();
        }
    }
}

class MapResource {
    private  volatile Map<Integer,Integer> map = new HashMap<>();
    // 独占锁==synchronized
     ReentrantLock lock = new ReentrantLock();
    // 读写锁
     ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    // 写操作
    public  void put(Integer key,Integer value) {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在进行写操作....");
            TimeUnit.MILLISECONDS.sleep(500);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName() + " 写操作完成....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            readWriteLock.writeLock().unlock();
        }
    }

    // 读操作
    public  Integer get(Integer key) {
        readWriteLock.readLock().lock();
        try {
            Integer result = null ;
            System.out.println(Thread.currentThread().getName() + " 正在进行读操作....");
            TimeUnit.MILLISECONDS.sleep(100);
            result = map.get(key);
            System.out.println(Thread.currentThread().getName() + " 读操作完成, 读取的值为: " + result);
            return  result ;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return  null;
        }finally {
            readWriteLock.readLock().unlock();
        }
    }
}

输出结果

使用读写锁之后,读操作允许多个线程同时进入。

image-20221207222822207

注意点:在读操作没有完成之前,写锁无法获得。

新增加三个线程执行写操作,并延迟读操作的完成时间.

image-20221207225118676

image-20221207225135783

输出结果

只有当读操作完成后,写锁才会被获得。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ECzhrdeY-1670509253301)(https://images-1313160403.cos.ap-beijing.myqcloud.com/MarkDown/202212082219373.png)]

锁降级

将写入锁降级为读锁(类似Linux文件读写权限理解,就像写权限要高于读权限一样)

image-20221208192236151

写锁的降级,降级成为了读锁

  • 如果同一个线程持有了写锁,在没有释放写锁的情况下,它还可以继续获得读锁。这就是写锁的降级,降级成为了读锁。
  • 规则惯例,先获取写锁,然后获取读锁,再释放写锁的次序。
  • 如果释放了写锁,那么就完全转换为读锁。

获取写锁 ——> 获取读锁 ——> 释放写锁 ——> 释放读锁

案例演示一

演示锁降级: 写锁——> 读锁

main线程在获取写锁的同时,也能够获取读锁。

public class ReadWriteLockTest2 {
    public static void main(String[] args) {
        
        ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();

        writeLock.lock();
        System.out.println("写锁...");

        readLock.lock();
        System.out.println("读锁...");
        readLock.unlock();

        writeLock.unlock();
    }
}

结果

image-20221208202501179

案例演示二

如果有线程在读,那么写线程是无法获取写锁的,是悲观锁的策略

public class ReadWriteLockTest2 {
    public static void main(String[] args) {

        ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();



        readLock.lock();
        System.out.println("读锁...");

        writeLock.lock();
        System.out.println("写锁...");
        
        readLock.unlock();
        writeLock.unlock();


    }
}

结果

写线程被阻塞

image-20221208202748092

总结

  • 线程获取读锁是不能直接升级为写入锁的。不可锁升级

image-20221208203039978

在ReentrantReadWriteLock中,当读锁被使用时,如果有线程尝试获取写锁,该写线程会被阻塞。
所以,需要释放所有读锁,才可获取写锁,

image-20221208203114064

即ReadWriteLock读的过程中不允许写,只有等待线程都释放了读锁,当前线程才能获取写锁,
也就是写入必须等待,这是一种悲观的读锁

为什么要有锁降级

锁降级 下面的示例代码摘自ReentrantWriteReadLock源码中:
ReentrantWriteReadLock支持锁降级,遵循按照获取写锁,获取读锁再释放写锁的次序,写锁能够降级成为读锁,不支持锁升级。

image-20221208204622656

1 代码中声明了一个volatile类型的cacheValid变量,保证其可见性。

2 首先获取读锁,如果cache不可用,则释放读锁,获取写锁,在更改数据之前,再检查一次cacheValid的值,然后修改数据,将cacheValid置为true,然后在释放写锁前获取读锁;此时,cache中数据可用,处理cache中数据,最后释放读锁。这个过程就是一个完整的锁降级的过程,目的是保证数据可见性。

如果违背锁降级的步骤
如果当前的线程C在修改完cache中的数据后,没有获取读锁而是直接释放了写锁,那么假设此时另一个线程D获取了写锁并修改了数据,那么C线程无法感知到数据已被修改,则数据出现错误。

如果遵循锁降级的步骤
线程C在释放写锁之前获取读锁,那么线程D在获取写锁时将被阻塞,直到线程C完成数据处理过程,释放读锁。这样可以保证返回的数据是这次更新的数据,该机制是专门为了缓存设计的。

邮戳锁 StampedLock

StampedLock是JDK1.8中新增的一个读写锁,也是对JDK1.5中的读写锁ReentrantReadWriteLock的优化。邮戳锁也叫票据锁。

邮戳锁解决了ReentrantReadWriteLock 的写锁饥饿问题。什么是写锁饥饿?

ReentrantReadWriteLock实现了读写分离,但是一旦读操作比较多的时候,想要获取写锁就变得比较困难了
假如当前1000个线程,999个读,1个写,有可能999个读取线程长时间抢到了锁,那1个写线程就悲剧了
因为当前有可能会一直存在读锁,而无法获得写锁,根本没机会写.这就是写锁饥饿问题

如何缓解锁饥饿问题?

  • 使用公平锁,但是公平锁是以牺牲系统吞吐量为代价的。
  • 使用邮戳锁

ReentrantReadWriteLock
允许多个线程同时读,但是只允许一个线程写,在线程获取到写锁的时候,其他写操作和读操作都会处于阻塞状态,读锁和写锁也是互斥的,所以在读的时候是不允许写的,读写锁比传统 的synchronized速度要快很多,原因就是在于ReentrantReadWriteLock支持读并发

StampedLock横空出世
ReentrantReadWriteLock 的读锁被占用的时候,其他线程尝试获取写锁的时候会被阻塞。
但是,StampedLock采取乐观获取锁后,其他线程尝试获取写锁时不会被阻塞,这其实是对读锁的优化,所以,在获取乐观读锁后,还需要对结果进行校验。

邮戳锁的特点

  • 所有获取锁的方法,都返回一个邮戳(Stamp),Stamp为零表示获取失败,其余都表示成功;
  • 所有释放锁的方法,都需要一个邮戳(Stamp),这个Stamp必须是和成功获取锁时得到的 Stamp一致;
  • StampedLock是不可重入的,如果一个线程已经持有了写锁,再去获取写锁的话就会造成死锁

邮戳锁的三种访问方式:

Reading(读模式):功能和ReentrantReadWriteLock的读锁类似

Writing(写模式):功能和ReentrantReadWriteLock的写锁类似

Optimistic reading(乐观读模式):无锁机制,类似于数据库中的乐观锁,支持读写并发,很乐观认为读取时没人修改,假如被修改再实现升级为悲观读模式

官方描述

image-20221208211316754

案例演示一

使用传统读、写锁,演示: 读锁没有释放时,写锁无法获取

public class StampedLockTest {
    public static void main(String[] args) {
        MyResource myResource = new MyResource();
        new Thread(myResource::read, "readThread").start();
        try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
        new Thread(myResource::write, "writeThread").start();
    }
}

class MyResource {
    int num =  30 ;
    StampedLock stampedLock = new StampedLock();

    public void write() {

        long stamp = stampedLock.writeLock();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在进行写操作....");
            num = 30 + 20 ;
            System.out.println(Thread.currentThread().getName() + " 写操作完成....");
        } finally {
            // 通过邮戳释放锁
            stampedLock.unlock(stamp);
        }
    }

    public void read() {
        long stamp = stampedLock.readLock();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在进行读操作....");
            // 将读操作睡眠4s,为了演示出结果
            for (int i = 0; i < 4; i++) {
                System.out.println(Thread.currentThread().getName()  + " 正在读...");
                try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
            }
            System.out.println(Thread.currentThread().getName() + " 写操作完成....,读取的值: " + num);
        } finally {
            stampedLock.unlock(stamp);
        }

    }
}

结果

只有当读操作释放锁之后,写操作才会获取锁

image-20221208213703533

案例演示二

使用 StampedLock提供的乐观读 tryOptimisticRead , 此种方式允许在读的过程中获取写锁。

一般会通过 validate 方法判断 从获取 stamp 标记以来是否被其他线程获取,如果在读的时候有写操作介入,那么会返回 false,将乐观读 升级为 悲观读。

public class StampedLockTest {
    public static void main(String[] args) {
        MyResource myResource = new MyResource();
        new Thread(myResource::byTryOptimisticRead, "readThread").start();

        try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}

        new Thread(() -> {
            System.out.println("writeThread want to come in ....");
            myResource.write();
        }, "writeThread").start();
    }
}

class MyResource {
    int num =  30 ;
    StampedLock stampedLock = new StampedLock();

    public void write() {

        long stamp = stampedLock.writeLock();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在进行写操作....");
            num = 30 + 20 ;
            System.out.println(Thread.currentThread().getName() + " 写操作完成....");
        } finally {
            // 通过邮戳释放锁
            stampedLock.unlock(stamp);
        }
    }

    public void read() {
        long stamp = stampedLock.readLock();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在进行读操作....");
            // 将读操作睡眠4s,为了演示出结果
            for (int i = 0; i < 4; i++) {
                System.out.println(Thread.currentThread().getName()  + " 正在读...");
                try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
            }
            System.out.println(Thread.currentThread().getName() + " 写操作完成....,读取的值: " + num);
        } finally {
            stampedLock.unlock(stamp);
        }
    }

    // 乐观读
    public  void byTryOptimisticRead() {
        // 获取乐观读的邮戳
        long stamp = stampedLock.tryOptimisticRead();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在进行读操作....");
            for (int i = 0; i < 4; i++) {
                try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
                System.out.println(Thread.currentThread().getName()  + " 正在读..."+ "第 " + i + "秒, " + "stamp的状态: " + stampedLock.validate(stamp));
            }
            if (!stampedLock.validate(stamp)){
                // 返回 false 表示有线程获取锁,乐观锁 --> 悲观锁
                stamp = stampedLock.readLock();
                try {
                    System.out.println("乐观读 升级为 悲观读");
                    System.out.println("升级到悲观读,重新读取到的值: " + num);
                } finally {
                    stampedLock.unlock(stamp);
                }
            }

        } finally {
            System.out.println("最终读到的值: " + num);
        }
    }
}

结果:

首先读操作,沉睡4s,每过1s,都将 stamp 的状态输出。

当写操作沉睡2s中,stamp状态为true,没有线程获取写锁,唤醒后,stamp的状态为 false,表示写操作介入了,因此在 if 语句里 将乐观锁升级为了悲观锁,并重新读取值。

image-20221208220933525

StampedLock 缺点

  • StampedLock 不支持重入,没有Re开头
  • StampedLock 的悲观读锁和写锁都不支持条件变量(Condition),这个也需要注意。
  • 使用 StampedLock一定不要调用中断操作,即不要调用interrupt() 方法

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

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

相关文章

域名解析函数 gethostbyname

在实现TCP/UDP编程的时候&#xff0c;服务端绑定的IP地址是点分十进制的&#xff0c;如124.222.215.205&#xff0c;如果提供的是域名&#xff0c;此时我们需要将域名转换成点分十进制的IP地址&#xff0c;这个过程就是 “ 域名解析 ”&#xff0c;实现域名转换的函数就是 geth…

Python Aws Ubuntu20 配置多IP

1. 业务场景&#xff1a; 高频次交易需要多次访问接口&#xff0c;配置单服务器多IP绕过币安单个IP频率限制 2. AWS实例选择&#xff1a; t3系列&#xff0c;东京A区&#xff0c;ubuntu20 这是我测试的币安延迟最低的配置&#xff08;平均6ms&#xff09; 换一个区域、一个系…

【王道计算机网络笔记】数据链路层-数据链路层设备

文章目录物理层扩展以太网链路层扩展以太网网桥透明网桥源路由网桥以太网交换机直通式交换机存储转发式交换机冲突域和广播域物理层扩展以太网 主机和集线器不能超过100m&#xff0c;超过100m会失真严重&#xff0c;无法恢复&#xff0c;扩展以太网的方法&#xff1a; 通过光纤…

欢迎报名Rust China Hackathon 2022 达坦科技组

12月4日下午&#xff0c;DatenLord就2022Rust China Hackathon大赛活动企业组&#xff08;达坦科技组&#xff09;的赛题进行了空中宣讲会。不仅对赛事流程进行了全面的讲解&#xff0c;同时对赛题背景以及完赛标准和要点进行了深入的剖析。会后更是设置问答环节&#xff0c;细…

目标检测算法——图像分割数据集汇总(附下载链接)

>>>深度学习Tricks&#xff0c;第一时间送达<<< &#x1f384;&#x1f384;近期&#xff0c;小海带在空闲之余&#xff0c;收集整理了一批图像分割数据集供大家参考。 整理不易&#xff0c;小伙伴们记得一键三连喔&#xff01;&#xff01;&#xff01;&am…

给visCode编辑器添加背景图

看见很多大牛的visCode编辑器上有个背景图&#xff0c;感觉挺有逼格的&#xff0c;要想跟大牛一样有逼格&#xff0c;那先把编辑器设置的跟大牛的逼格一样高 话不多说&#xff0c;开始 第一步&#xff1a;先安装背景扩展 第二步&#xff1a;点击选择扩展设置 第三步&#xff1…

原码、反码、补码的互相转换

原码、反码、补码的互相转换 简介&#xff1a;本文是为了计算机组成原理复习&#xff0c;本文以具体题目的方式来对原码、反码、补码的互相转换进行讲解。 概述 原码 &#xff1a;最高位是符号位&#xff0c;0代表正数&#xff0c;1代表负数&#xff0c;非符号位为该数字绝对…

实战web漏洞挖掘小技巧

几个月前看到国外的bug bounty tips在传Host攻击技巧&#xff0c;前阵子又看到国内有不少文章总结&#xff0c;冷渗透就不再复述了&#xff0c;直接介绍实际业务场景中碰到的案例。 0x01 自定义Host—窃取Token 1. 找到重置密码处&#xff0c;填写任意一个受害者邮箱帐号&#…

记录--微信小程序获取用户信息(附代码、流程图)

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 本篇文章主要总结了微信小程序开发&#xff0c;获取用户信息的整个流程步骤。补充了网上很多碎片化的代码&#xff0c;本人梳理了思路写下了这篇文章。 思路 1、在js文件中&#xff0c;设置userinfo、…

华丰科技将于12月13日上会:业绩波动明显,海通证券等为股东

近日&#xff0c;四川华丰科技股份有限公司&#xff08;下称“华丰科技”&#xff09;在上海证券交易所科创板递交招股书&#xff08;上会稿&#xff09;。据贝多财经了解&#xff0c;华丰科技将于2022年12月13日上会&#xff0c;接受科创板上市委的现场审议。 根据公开信息&am…

【嵌入式硬件芯片开发笔记】LP87702/LP87524电源芯片配置流程

【嵌入式硬件芯片开发笔记】LP87702/LP87524电源芯片配置流程 LP8752x-Q1 10-A Buck Converter With Integrated Switche 具有诊断功能的 LP87702-Q1 双路降压转换器和 5V 升压转换器 LP87702/LP87524从机地址都是0x60 LP87702/LP87524中的设备ID等寄存器&#xff0c;是根据OT…

维护4年的组件化框架 Component 升级到 KComponent 啦

前言 KComponent 它来啦. 从 2018.8 开始, Component 项目正式开源. 到目前已经 4 年的时间了. 这几年中, Component 不断升级优化. 最后趋于稳定. 为了组件化框架更好的发展, KComponent 继承了 Component 的优点, 升级为一个纯 Kotlin 的项目. 不再支持 Java, 注解驱动器也…

Java招生报名咨询系统毕业设计,Java招生咨询问答系统设计与实现,毕业设计论文怎么写毕设源码开题报告需求分析怎么做

功能清单 【系统管理员功能】 关于我们设置&#xff1a;设置学校简介、联系我们、加入我们、法律声明、学校详情 广告管理&#xff1a;设置小程序首页轮播图广告和链接 留言列表&#xff1a;所有用户留言信息列表&#xff0c;支持删除 会员列表&#xff1a;查看所有注册会员信…

20.前端笔记CSS-精灵图

1、为啥要精灵图 一个网页中往往会应用很多小的背景图像作为修饰&#xff0c;当网页中的图像过多时&#xff0c;服务器就会频繁的接收和发生请求图片&#xff0c;造成服务器的请求压力过大&#xff0c;这会大大降低页面的加载速度 为了有效减少服务器接收和发生请求的次数&…

[附源码]JAVA毕业设计图书借阅系统演示录像(系统+LW)

[附源码]JAVA毕业设计图书借阅系统演示录像&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目…

你不知道的goland技巧

很多人使用 Goland 有很长时间的&#xff0c;却没有好好利用上 Goland 工具带给我们的遍历&#xff0c;今天咱们就来解锁一下新技巧 过去我们使用 Goland 就是简单的配置一下 go 的 proxy &#xff0c;以及配置一下 ssh &#xff0c;与服务器进行文件的上传和下载&#xff0c;其…

基于Hadoop的用户购买行为的商品推荐系统

目录 一 绪论 1 1.1 编写目的 1 1.2 背景及意义 1 1.3 开发及运行环境 2 二 需求分析 3 2.1 系统概述 3 2.3 系统功能需求 4 2.3.1 收集原始数据 4 2.3.2 计算物品相似度矩阵 4 2.3.3 计算用户购买向量 5 2.3.4 计算推荐向量并去重和排序 5 2.3.4 数据入库 5 2.3.5 作业控制 5 …

百数:用报表搭建生产进度看板,让生产更高效

现如今我国已经步入了全新的现代化科技时代&#xff0c;国内各个行业对于数字技术也在进行着不断地应用和研究。目前数字技术在企业生产管理系统的运用中具备较为显著的优势&#xff0c;其中生产进度看板就是数字化管理系统的重要应用之一。 生产进度看板是一种可视化管理系统…

计及电转气协同的含碳捕集与垃圾焚烧虚拟电厂优化调度附Matlab代码和论文复现

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

Mysql性能测试主要内容

Mysql性能测试主要内容 相信很多做性能测试的朋友都知道&#xff0c;性能测试并不单单只是看服务器cpu、IO、内存、网络等&#xff0c;我们还需要了解Mysql性能&#xff0c;那么我们看看Mysql性能主要内容有哪些呢&#xff1f; MySql数据库介绍 Mysql相信大家做测试的&#…