ReentrantReadWriteLock 与 Condition:读写锁与精准唤醒机制

自学并发编程之:Lock与Condition Lock与Condition 8.1 互斥锁 8.1.1 锁的可重入性 “可重入锁”是指当一个线程调用 object.lock()获取到锁,进入临界区后,再次调用object.lock(),仍然可以获取到该锁。显然,通常的锁都要设计成可重入的,否则就会发生死锁。 synchronized关键字,就是可重入锁。在一个synchronized方法method1()里面调用另外一个synchronized方法method2()。如果synchronized关键字不可重入,那么在method2()处就会发生 阅读详情

「Java 进阶之路」系列 Day06

写在前面

Day05 讲了 ReentrantLock 这种独占锁——同一时刻只能有一个线程持有。但现实里有大量"读多写少"的场景:一个缓存被上千个请求并发读取,写操作却一天也遇不到几次。用独占锁保护这种缓存,相当于把并发读也全部串行化了,性能白白浪费。

这篇讲的 ReentrantReadWriteLock(读写锁)就是为这种场景而生的;顺带讲讲 Condition——Lock 体系里替代 wait/notify 的精准通知机制。两者都在同一份 AQS 基础上构建,Day05 打的底子这篇会直接用上。


一、是什么:读写锁解决了什么问题

核心动机:读操作之间互不冲突,没必要互斥;只有写操作之间、以及读写之间才需要互斥。ReentrantReadWriteLock 把这两种场景拆成两把锁:

  • 读锁(共享锁):多个线程可以同时持有,不互斥
  • 写锁(独占锁):同时只能有一个线程持有,且和任何读锁都互斥

读写规则一张表看懂

当前状态读锁请求写锁请求
无锁允许允许
有读锁允许(共享)阻塞
有写锁(别的线程持有)阻塞阻塞
有写锁(自己持有)允许(锁降级)允许(重入)

二、为什么能做到"读共享写独占":state 的高低位设计

还记得 Day05 讲的 AQS 核心是一个 state 变量吗?ReentrantReadWriteLock 复用了同一个 state,只是把这个 32 位整数拆成了两半:

int state(32位)

高16位:读锁持有数
低16位:写锁重入次数

读锁数 = state 无符号右移16位
写锁数 = state 与 0xFFFF 做按位与

这是一个很巧妙的设计:不需要两个独立的计数器,用位运算就能同时表达"当前有几个线程持有读锁"和"写锁被重入了几次",读写状态的变化都通过对同一个 state 做 CAS 操作完成,复用了 AQS 现成的排队/唤醒机制。


三、怎么用:一个缓存场景的标准写法

private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock readLock  = rwl.readLock();
private final Lock writeLock = rwl.writeLock();
private Map<String, Object> cache = new HashMap<>();

public Object get(String key) {
    readLock.lock();
    try { return cache.get(key); }
    finally { readLock.unlock(); }
}

public void put(String key, Object value) {
    writeLock.lock();
    try { cache.put(key, value); }
    finally { writeLock.unlock(); }
}

用法和普通 Lock 一模一样,区别只是从 rwl 上分别拿"读锁"和"写锁"两把独立的 Lock 对象。

锁降级:持有写锁时可以直接获取读锁

readWriteLock.writeLock().lock();
try {
    data = newValue;                     // 先更新数据
    readWriteLock.readLock().lock();     // 降级:在释放写锁之前先拿到读锁
} finally {
    readWriteLock.writeLock().unlock();  // 释放写锁,降级完成
}
try {
    use(data);                           // 此时只持有读锁,其他读线程也能进来
} finally {
    readWriteLock.readLock().unlock();
}

为什么要锁降级:如果写完数据后直接释放写锁、再重新申请读锁,中间会有一个"完全没有锁"的空隙,别的写线程可能会插进来把数据再改一遍,导致你后面读到的不是自己刚写的值。锁降级能保证从"写"到"读"这个过程中,数据的可见性和一致性不被打断。

注意反过来不行:不支持锁升级(先持有读锁,再申请写锁),持有读锁时申请写锁会导致死锁——因为写锁需要等所有读锁释放,而你自己就持有着一个读锁,永远不会释放,自己把自己锁死了。

一个容易被忽略的风险:写线程饥饿

如果读请求源源不断地涌入,非公平模式下写线程可能一直抢不到锁(因为读锁之间不互斥,新来的读请求可以持续插队),这就是写饥饿。缓解办法是用公平锁模式,或者控制读锁的持有时间不要太长。


四、Condition:Lock 体系里的精准唤醒机制

为什么需要 Condition,wait/notify 还不够用吗

Object.wait()/notify() 有个明显限制:每个对象只有一个等待队列。如果一个场景里有两类完全不同的等待条件(比如"队列满了,生产者要等"和"队列空了,消费者要等"),用 wait/notify 只能把两类线程混在同一个等待队列里,notify() 唤醒谁完全随缘,容易出现"该醒的没醒,不该醒的被吵醒"。

Condition 就是为了解决这个问题:一个 Lock 可以创建多个 Condition,每个 Condition 有自己独立的等待队列,唤醒可以做到"精准打击"。

对比项Object.wait/notifyCondition.await/signal
依赖synchronizedLock
等待队列数1 个多个(每次 newCondition() 一个)
超时等待wait(timeout)await(time, unit)
中断响应wait() 可中断await() 可中断
等到指定时间点不支持支持 awaitUntil(Date)

原理:两个队列之间的转移

每个 Condition 内部维护一个独立的等待队列,与 Day05 讲过的 AQS 同步队列相互配合:

调用await

当前线程加入
Condition等待队列

完全释放锁
state归零
唤醒同步队列后继

挂起线程

被signal唤醒

转移到AQS
同步队列

重新竞争锁

拿到锁后
从await返回

一句话理解:await() 相当于"先把自己从抢锁的队伍里挪到专门的等候室,同时把锁让出来";signal() 则是"把等候室里排最前面的人重新放回抢锁的队伍"。

signal 和 signalAll

condition.signal();     // 只唤醒等待队列的队头节点(一个)
condition.signalAll();  // 唤醒等待队列中的所有节点

有界缓冲区:生产者消费者的标准实现

class BoundedBuffer<T> {
    private final Lock lock = new ReentrantLock();
    private final Condition notFull  = lock.newCondition();  // 生产者等待队列
    private final Condition notEmpty = lock.newCondition();  // 消费者等待队列
    private final Queue<T> queue = new LinkedList<>();
    private final int capacity;

    BoundedBuffer(int capacity) { this.capacity = capacity; }

    public void put(T item) throws InterruptedException {
        lock.lock();
        try {
            while (queue.size() == capacity)
                notFull.await();       // 满了,生产者进notFull队列等待
            queue.offer(item);
            notEmpty.signal();         // 精准唤醒notEmpty队列里的消费者
        } finally { lock.unlock(); }
    }

    public T take() throws InterruptedException {
        lock.lock();
        try {
            while (queue.isEmpty())
                notEmpty.await();      // 空了,消费者进notEmpty队列等待
            T item = queue.poll();
            notFull.signal();          // 精准唤醒notFull队列里的生产者
            return item;
        } finally { lock.unlock(); }
    }
}

关键细节:用 while 而不是 if 检查条件——线程被唤醒后,可能有别的线程抢先一步把数据消费/填满了,条件已经又不满足了,所以醒来后必须重新检查一遍,而不能想当然地认为条件一定成立。这个坑在 wait/notify 里同样存在,是并发编程里最容易被面试官抓出来的细节之一。

这个例子也直观展示了 Condition 相比 wait/notify 的优势:生产者只会被"队列不满"这个条件精准唤醒,消费者只会被"队列不空"精准唤醒,两类线程互不干扰,不会出现用 notifyAll() 时"把所有等待线程都吵醒、结果大部分又发现条件不满足只能重新睡回去"的无效唤醒问题。

一个容易被忽略的限制

读写锁的读锁不支持 Condition——调用 readLock.newCondition() 会直接抛异常,因为"多个线程共享持有的锁"和"某一个线程专属的等待/唤醒"这两个语义本身是冲突的。


五、面试追问

Q1:ReentrantReadWriteLock 解决了什么问题,适合什么场景?

解决"读多写少"场景下用独占锁导致并发读被不必要串行化的问题。它把锁拆成读锁(共享,多个线程可同时持有)和写锁(独占)两把,读操作之间不互斥,只有写操作之间、以及读写之间才互斥。典型场景是缓存:大量并发读、偶尔才有一次写。

Q2:ReentrantReadWriteLock 底层是怎么用一个 state 表示两种锁状态的?

把 32 位的 state 拆成高 16 位和低 16 位:高 16 位记录当前持有读锁的线程数,低 16 位记录写锁的重入次数。读锁数 = state 无符号右移 16 位,写锁数 = state 与 0xFFFF 做按位与。这样只用一个整数配合位运算,就能同时表达两种锁的状态,复用了 AQS 现成的 CAS 和排队机制。

Q3:为什么支持锁降级(写转读),不支持锁升级(读转写)?

锁降级(持有写锁时,先获取读锁再释放写锁)能保证从写到读这段过程数据的可见性不被其他写线程打断,是安全的。锁升级(持有读锁时申请写锁)则会导致死锁:写锁需要等所有读锁释放才能获取,而自己持有的这个读锁又不会主动释放,相当于自己等自己,永远无法完成。

Q4:Condition 相比 Object 的 wait/notify 有什么优势?

wait/notify 依赖 synchronized,且每个对象只有一个等待队列,多种等待条件的线程会混在一起,notify() 唤醒谁是不确定的,容易出现无效唤醒。Condition 依赖 Lock,一个 Lock 可以创建多个 Condition,每个都有独立的等待队列,可以针对不同的等待条件做"精准唤醒"(比如生产者消费者场景里 notFull/notEmpty 分开),避免无关线程被无效唤醒。

Q5:BoundedBuffer 示例里为什么要用 while 而不是 if 判断条件?

因为线程被唤醒后不代表条件一定还成立——可能在它被唤醒、真正抢到锁执行之前,条件又被其他线程改变了(比如队列刚被唤醒说"不满",结果被另一个更快的生产者抢先塞满了)。用 while 能保证每次从等待中恢复执行后都重新检查一遍条件,条件不满足就继续 await(),这是写等待通知模型时必须遵守的写法,用 if 是经典的并发 bug 来源。


下一篇预告

Day07 讲 CAS 和原子类——AtomicInteger 这类无锁数据结构到底是怎么在不加锁的情况下保证线程安全的,以及 CAS 经典的 ABA 问题是怎么回事。

Java 并发锁体系全解:从 synchronized、Lock 到 AQS 底层原理与源码深度剖析 在 Java 并发编程体系中,锁是解决多线程共享资源竞争、保证线程安全的核心机制。从 JVM 内置的隐式锁,到 JUC 包提供的Lock显式锁,再到支撑整个并发工具集的底层基石 AbstractQueuedSynchronizer(AQS),构成了一套层层递进、设计精妙的锁实现体系。本文将从上层 API 到底层源码,完整拆解 Java 锁的核心原理,覆盖独占模式与共享模式、公平锁与非公平锁、条件队列与双队列联动、读写锁实现等核心知识点,结合 JDK 源码逐层剖析其设计思想与执行流程。 阅读详情

相关推荐

SpringBoot+Vue+MySQL 高校学科竞赛平台 完整可运行版(源码+文档)

本项目基于SpringBoot+Vue+MySQL构建高校学科竞赛管理平台,覆盖学生、教师、领队教师、管理员四大角色,实现竞赛发布、报名、刷题、评分、公示等全流程闭环管理。系统架构清晰,技术主流,配套完整源码、数据库脚本、万字论文、开题报告、答辩PPT及部署文档,支持一键运行,适用于毕业设计、课程设计及院系二次开发,真正实现“开箱即用”。

招风的黑耳优快云 173

并发编程从零开始(十二)-Lock与Condition

并发编程从零开始(十二)-Lock与Condition 8 Lock与Condition 8.1 互斥锁 8.1.1 锁的可重入性 “可重入锁”是指当一个线程调用 object.lock()获取到锁,进入临界区后,再次调用object.lock(),仍然可以获取到该锁。显然,通常的锁都要设计成可重入的,否则就会发生死锁。 synchronized关键字,就是可重入锁。在一个synchronized方法method1()里面调用另外一个synchronized方法method2()。如果synchronize

qq_47815451的博客 346

Windows本地部署Qwen3-14B实战指南:Ollama+Open WebUI零Docker方案

大语言模型本地部署是AI工程化落地的关键环节,其核心在于推理框架兼容性、硬件加速适配性与端到端稳定性。Ollama作为轻量级模型运行时,原生支持Windows DirectML与CUDA,规避了Docker/WSL2虚拟化开销和llama.cpp编译风险;Qwen3-14B则凭借128K上下文、Tool Calling原生支持及中文长文本结构化能力,成为政务、金融等场景首选。本文聚焦Windows平台真实约束——显存阈值、驱动版本匹配、路径编码兼容、离线知识库集成(RAG)等工程细节,提供经63台Windo

weixin_32136203的博客 413

【大白话说Java面试题 第111题】【并发篇】第11题:谈谈你对 AQS 的理解?

本文深入解析了Java并发编程中的核心框架AQS(AbstractQueuedSynchronizer),它是ReentrantLock等同步器的底层实现基础。AQS采用状态(state)+队列(同步队列与条件队列)+CAS的架构,通过模板方法模式将具体锁逻辑交由子类实现。重点分析了Node节点的waitStatus状态机(5种状态流转)及其在CLH变体队列中的作用,包括独占/共享模式下的线程阻塞与唤醒机制。文章通过结构示意图和代码示例,揭示了AQS如何通过无锁CAS与阻塞park的混合策略实现高效线程同步

Java后端 | AI智能体应用实战派 | 电脑技能行家 791

AlphaFold-multimer实战:从零开始预测STAT1-STAT2蛋白复合物结构

本文详细介绍了使用AlphaFold-multimer从零开始预测STAT1-STAT2蛋白复合物结构的完整实战流程。内容涵盖环境配置、数据库准备、序列处理、参数优化及结果可信度评估,为研究者利用这一AI工具进行复合物结构预测提供了系统指南和深度解析。

weixin_29094301的博客 889

AlphaFold3实战:用它预测抗体结构,我的CDR H3环RMSD降到了2Å以内

本文详细介绍了如何利用AlphaFold3(AF3)高精度预测抗体结构,特别是CDR H3环的建模,其RMSD可降至2Å以内。通过实战演示从序列输入到结构优化的全流程,包括参数配置、置信度指标解读及结果分析,帮助研究人员快速掌握AF3在抗体药物研发中的应用。

weixin_29224529的博客 187

理解 AQS 核心原理:从排队模型到自定义同步器实战

并发编程中线程阻塞和锁竞争是经常遇到的问题,其底层往往涉及 AQS(AbstractQueuedSynchronizer)这一核心框架。AQS 是 ReentrantLock、Semaphore 等同步器的共同基石,通过 CLH 变种双向队列、volatile 状态位和 LockSupport 阻塞原语,提供了一套通用的排队与唤醒机制。理解 AQS 的关键在于掌握 state 的灵活语义、公平与非公平锁的差异,以及 Condition 条件队列如何与主队列协同工作。此外,基于 AQS 的模板方法编写自定义同

weixin_34357928的博客 264

网络流量监测技术

NetFlow是Cisco公司提出的网络数据包交换技术,该技术首先被用于网络设备对数据交换进行加速,并可同步实现对高速转发的IP数据流(Flow)进行测量和统计。  随着宽带互联网在中国 的迅速发展,全国各大电信运营商的网络规模都在不断扩张,网络结构日渐复杂,网络业务日趋丰富,网络流量高速增长。电信运营商需要通过可靠、有效的网络业 务流量监测系统对其网络以及网络所承载的各类业务进行及时、准

红杉树(ecitnet)的博客 5540

Java:102-并发编程详解(中篇)

特性2:层次Phaser 多个Phaser可以组成如下图所示的树状结构,可以通过在构造方法中传入父Phaser来实现 可以发现,在Phaser的内部结构中,每个Phaser记录了自己的父节点,但并没有记录自己的子节点列表,所 以,每个 Phaser 知道自己的父节点是谁,但父节点并不知道自己有多少个子节点,对父节点的操作,是通过子节 点来实现的 树状的Phaser怎么使用呢?考虑如下代码,会组成下图的树状Phaser: 这个64位(long的)的state变量被拆成4部分,下图为state变量各部分

qq_59609098的博客 1098

大厂校招:星宸科技嵌入式面试及参考答案(5万字长文)

当一个新的硬件设备被插入系统时,内核会发送一个设备事件通知 udev,udev 会根据设备的属性和规则,为该设备创建一个合适的设备文件。通过去除不必要的驱动程序、禁用不需要的功能、选择合适的内核配置选项、优化内核编译选项和定制内核模块等方法,可以减小内核的大小和复杂性,提高系统的性能和稳定性。上半部通常是在中断关闭的情况下执行的,以确保中断处理的原子性。处理碎片化内存是内存管理中的一个重要问题,可以采用内存压缩、内存池管理、伙伴系统、首次适应算法和最佳适应算法等方法来减少内存碎片的产生,提高内存的利用率。

大模型大数据攻城狮的专栏 2929

某东x-api-eid-token风控参数分析与破解

x-api-eid-token是京东的风控参数,在接口当中,如果没有该参数,有一定的概率能请求成功,但是在请求一段时间后,会报403权限失败的,该参数主要通过jsTk.do这个接口生成,并且会将其存储到cookie当中。

zhouzhou_98的博客 1552

Java高并发系统 + 安全监控 完整知识体系

本文系统梳理了Java高并发开发的全方位知识体系,涵盖单机并发基础、分布式架构、系统安全、监控体系和故障治理等核心模块。主要内容包括: 单机并发基础 CPU多级缓存与内存模型 JMM内存模型与happens-before原则 锁机制与CAS原子操作 并发容器与线程池原理 JVM调优与性能优化 分布式高并发架构 分布式锁与一致性算法 缓存体系与典型问题解决方案 消息队列的削峰填谷 负载均衡与分库分表 微服务通信与流量控制 生产级解决方案 高并发业务安全防护 全链路监控体系搭建 典型故障排查与治理 应急预案与性

专注 Java 后端开发,分享基础语法、JVM、并发、Spring 全家桶、微服务、分布式、性能优化等实战干货。坚持输出高质量技术笔记,用简单语言讲清复杂原理,助力每一位 Java 开发者稳步进阶。 575

还在纠结二层交换机和三层交换机的区别?这篇文章帮你讲明白!

你好,这里是网络技术联盟站,我是瑞哥。公众号后台有小伙伴想要写一篇文章具体介绍一下二层交换机和三层交换机,此类的文章,之前写过几篇,今天瑞哥再写一篇,希望对大家有所帮助!

网络技术联盟站 4442

InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer

本文系统梳理InnoDB内存结构中的ChangeBuffer、Adaptive Hash Index(AHI)与LogBuffer。ChangeBuffer缓存二级索引页不在内存时的变更,减少随机IO,仅在页加载时合并;聚簇索引修改立即执行,因数据本身不可延后。AHI基于高频等值查询自动构建哈希索引,加速定位,仅支持等值操作。LogBuffer为redo log内存缓冲,配合磁盘日志实现崩溃恢复。三者均服务于性能优化,其中ChangeBuffer与AHI共享BufferPool空间,而LogBuffer独立

bksczm的博客 293

PINN物理信息网络 | 非线性薛定谔方程的物理信息神经网络

PINN物理信息网络 | 非线性薛定谔方程的物理信息神经网络

算法如诗的博客 1299

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

信创环境下PageOffice文档控件适配与实践

信创适配做到文档控件这一层,拼的已经不是"有没有国产版本",而是同一个功能在两类终端上是否真的等价。等价的标准很具体:同一次点击能得到同样的结果,同一份公文能得到同样的版式,同一个项目不需要维护两套代码。这背后是三件并不轻松的工作:服务端用一套接口同时支撑两类终端,客户端把两种系统与四种芯片的安装包分发清楚,文档内核层由产品侧跟着 WPS 的版本走。浏览器这一层的压力,则被收进了产品自己的窗口里——升级节奏由产品掌握,不再交给运气。

zi_wu_xian的专栏 323

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

V8 机制、手写核心函数与 TypeScript 类型体操

或固定初始化(如数组先定长、对象先给全字段)保持类型与形状稳定;这些都比"换框架"更基础。只能处理纯数据(丢函数、Date、RegExp、循环引用报错)。生产级深拷贝要处理:原始值短路、循环引用、V8(Chrome / Node 的 JS 引擎)不是"解释一行跑一行"那么简单,它用。图:V8 执行流程——解释执行 + 热点优化,优化失败会 Deopt 回退。JS 内存自动管理,但"自动"不等于"不会漏"。是"固定间隔最多执行一次"。手写 Promise 是"懂异步"的试金石。是"停下后才执行最后一次",

qq_38468358的博客 350

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

第三篇 第 6 课

前置:第三篇第 1 课(stdx 引入),第二篇第 1 课(std.regex)。环境:仓颉 SDK 1.2.0。本文所有代码与输出均逐行实测。

weixin_41618057的博客 510

【从零理解 Spring AI · 12】MCP 的两种角色:做客户端还是做服务端

上一篇讲了 MCP 的概念:标准协议、三大角色、M+N 替代 M×N。

nice_lcj520的博客 521

Java 集合容器高频面试题全解:HashMap 树化阈值、1.7 死循环与 ConcurrentHashMap 演进(2026 招聘 JD 真题复盘)

HashMap 树化阈值 8 的泊松分布依据、退化阈值 6 的防抖设计、JDK 1.7 头插成环死循环的排查与修复、1.8 尾插后为何仍线程不安全、ConcurrentHashMap 放弃分段锁的原因与 ForwardingNode 协作扩容、computeIfAbsent 死锁陷阱、大集合找重复的场景题选型,附三层追问链自测。题目来源为 38 期公开招聘 JD 样本与字节、拼多多、建行、美团、快手公开面经。

蒙眼过河 414

spdlog 同步日志库使用详解

spdlog是一个轻量级 C++ 日志库,支持同步日志、异步日志、多线程日志、日志文件轮转和自定义日志格式。对于嵌入式 Linux 应用开发,我建议先使用。它可以帮我们完成:日志级别过滤。时间戳和线程 ID 输出。日志格式化。终端和文件输出。多线程安全写入。日志文件按大小或时间轮转。我们不需要自己实现互斥锁、文件写入、日志格式化和轮转等底层功能。

zengjiajun2006的博客 996

JavaEE通关手册:数据类型与变量

代码无需额外处理,编译时编译器自动完成转换。

xys_678的博客 177

Java 多态 学习笔记

多态是面向对象编程的重要特性,指同一操作作用于不同对象时产生不同行为。其核心在于“父类引用指向子类对象”,结合方法重写实现运行时动态绑定。多态使代码更灵活、可扩展,如用Person p = new Student();统一处理各类人员注册,无需修改调用逻辑。但多态无法直接调用子类特有方法,需通过instanceof判断并强制转换。编译时看左边(类型),运行时看右边(实际对象),实现解耦与维护便利。

AA_AA114514的博客 367

IDEA 简介:从零认识 IntelliJ IDEA

IDEA 全称,是由 JetBrains 公司开发的一款主流 Java 集成开发环境(IDE)。它集代码编辑、编译、调试、测试、版本控制、构建管理等功能于一体,被大量 Java 开发者视为日常开发的首选工具。所谓 IDE(Integrated Development Environment,集成开发环境),就是把写代码所需的各类工具整合到同一个界面中。相比只用文本编辑器加命令行编译,IDE 能显著减少重复操作,让开发者把精力集中在业务逻辑上。

ztI10的博客 166

Java实现批量线段相交检测(跨积判定+扫描线优化与踩坑复盘)

【代码】Java实现批量线段相交检测(跨积判定+扫描线优化与踩坑复盘)

m0_64476639的博客 338

c++ explicit关键字详解

explicit。

拒绝AI文,努力在AI时代写出感动人心的好文章 48
上一篇: Lock 接口与 AQS 核心原理:理解一把可重入锁是怎么运作的
下一篇: @property 到底是怎么把方法伪装成属性的?一文吃透 property、staticmethod、classmethod
Zane199412
博客等级 码龄9年 71粉丝 99原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值