SOLID 面向对象设计原则

SOLID 是面向对象设计的五大基本原则的首字母缩写,由 Robert C. Martin(Uncle Bob)整理归纳。这五个原则是编写可维护、可扩展代码的基础。 S — 单一职责原则 (Single Responsibility Principle, SRP) 一个类应该只有一个引起它变化的原因。 一个类只做一件事。如果一个类承担了多个职责,那么每个职责的变化都可能影响这个类,导致它越来越难以维护。 反例: 简单工厂模式中的工厂类 Driver 同时负责判断车型和创建所有车对象,违反了单一职责。 O — 开闭原则 (Open-Closed Principle, OCP) 软件实体应该对扩展开放,对修改封闭。 添加新功能时,应该通过新增代码实现,而不是修改已有代码。详见开闭原则。 反例: 简单工厂的 if/else 判断链,每新增一种产品都要修改工厂方法。 L — 里氏替换原则 (Liskov Substitution Principle, LSP) 子类必须能够替换其父类,且程序行为不变。 任何使用父类的地方,换成子类后程序应该仍然正确运行。子类不应该破坏父类的契约。 示例: Benze 和 Bmw 都实现了 Car 接口,客户端代码使用 Car 类型,无论实际是哪种车,drive() 都能正确调用。 I — 接口隔离原则 (Interface Segregation Principle, ISP) 客户端不应该被迫依赖它不使用的方法。 接口应该尽量细化,不要把不相关的方法放在同一个接口里。大接口应该拆分成多个小接口,让实现类只需要关心自己用到的方法。 反例: 一个包含 fly()、swim()、run() 的 Animal 接口,鱼类被迫实现 fly() 和 run()。 D — 依赖倒置原则 (Dependency Inversion Principle, DIP) 高层模块不应该依赖低层模块,两者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。 ...

2026-04-16 · 1 min · 87 words · -

开闭原则

开闭原则 开闭原则(Open-Close Principle/OCP)是面向对象设计中"可复用设计"的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。 1988年,Bertrand Meyer在他的著作《Object Oriented Software Construction》中提出了开闭原则,它的原文是这样: “Software entities should be open for extension,but closed for modification”。翻译过来就是: “软件实体应当对扩展开放,对修改关闭”。这句话说得略微有点专业,我们把它讲得更通俗一点,也就是: 软件系统中包含的各种组件,例如模块 (Modules) 、类 (Classes) 以及功能 (Functions) 等等,应该在不修改现有代码的基础上,引入新功能。开闭原则中"开",是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中"闭",是指对于原有代码的修改是封闭的,即不应该修改原有的代码。实现开闭原则的关键就在于"抽象"。把系统的所有可能的行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法的特征。作为系统设计的抽象层,要预见所有可能的扩展,从而使得在任何扩展情况下,系统的抽象底层不需修改;同时,由于可以从抽象底层导出一个或多个新的具体实现,可以改变系统的行为,因此系统设计对扩展是开放的。 我们在软件开发的过程中,一直都是提倡需求导向的。这就要求我们在设计的时候,要非常清楚地了解用户需求,判断需求中包含的可能的变化,从而明确在什么情况下使用开闭原则。 关于系统可变的部分,还有一个更具体的对可变性封装原则 (Principle of Encapsulation of Variation, EVP) ,它从软件工程实现的角度对开闭原则进行了进一步的解释。EVP要求在做系统设计的时候,对系统所有可能发生变化的部分进行评估和分类,每一个可变的因素都单独进行封装。 我们在实际开发过程的设计开始阶段,就要罗列出来系统所有可能的行为,并把这些行为加入到抽象底层,根本就是不可能的,这么去做也是不经济的,费时费力。另外,在设计开始阶段,对所有的可变因素进行预计和封装也不太现实,也是很难做得到。所以,开闭原则描绘的愿景只是一种理想情况或是极端状态,现实世界中是很难被完全实现的。我们只能在某些组件,在某种程度上符合开闭原则的要求。 通过以上的分析,对于开闭原则,我们可以得出这样的结论: 虽然我们不可能做到百分之百的封闭,但是在系统设计的时候,我们还是要尽量做到这一点。 对于软件系统的功能扩展,我们可以通过继承、重载或者委托等手段实现。以接口为例,它对修改就是是封闭的,而对具体的实现是开放的,我们可以根据实际的需要提供不同的实现,所以接口是符合开闭原则的。如果一个软件系统符合开闭原则的,那么从软件工程的角度来看,它至少具有这样的好处: 可复用性好。 我们可以在软件完成以后,仍然可以对软件进行扩展,加入新的功能,非常灵活。因此,这个软件系统就可以通过不断地增加新的组件,来满足不断变化的需求。 可维护性好。 由于对于已有的软件系统的组件,特别是它的抽象底层不去修改,因此,我们不用担心软件系统中原有组件的稳定性,这就使变化中的软件系统有一定的稳定性和延续性。开闭原则具有理想主义的色彩,它是面向对象设计的终极目标。因此,针对开闭原则的实现方法,一直都有面向对象设计的大师费尽心机,研究开闭原则的实现方式。后面要提到的里氏代换原则 (LSP) 、依赖倒转原则 (DIP) 、接口隔离原则 (ISP) 以及抽象类 (Abstract Class) 、接口(Interface)等等,都可以看作是开闭原则的实现方法。 http://baike.baidu.com/view/866233.htm

2026-04-16 · 1 min · 53 words · -

设计模式 — 工厂模式, Factory

设计模式 — 工厂模式, Factory 一、引子 话说十年前,有一个老板,他家有三辆汽车 —— Benz奔驰、Bmw宝马、Audi奥迪,还雇了司机为他开车。不过,老板坐车时总是怪怪的: 上Benz车后跟司机说"开奔驰车!",坐上Bmw后他说"开宝马车!",坐上Audi说"开奥迪车!"。你一定说: 这人有病!直接说开车不就行了?! 而当把这个老板的行为放到我们程序设计中来时,会发现这是一个普遍存在的现象。幸运的是,这种有病的现象在 OO (面向对象) 语言中可以避免了。下面就以 Java 语言为基础来引入我们本文的主题: 工厂模式。 二、分类 工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。 工厂模式在《Java与模式》中分为三类: 简单工厂模式 (Simple Factory) 工厂方法模式 (Factory Method) 抽象工厂模式 (Abstract Factory) 这三种模式从上到下逐步抽象,并且更具一般性。 GOF在《设计模式》一书中将工厂模式分为两类: 工厂方法模式 (Factory Method) 与抽象工厂模式 (Abstract Factory) 。将简单工厂模式 (Simple Factory) 看为工厂方法模式的一种特例,两者归为一类。 两者皆可,在本文使用《Java与模式》的分类方法。下面来看看这些工厂模式是怎么来"治病"的。 简单工厂模式, 简单工厂 (Simple Factory) 简单工厂模式又称静态工厂方法模式。从命名上就可以看出这个模式一定很简单。它存在的目的很简单: 定义一个用于创建对象的接口。 先来看看它的组成: 工厂类角色: 这是本模式的核心,含有一定的业务逻辑和判断逻辑。在 java 中它往往由一个具体类实现。 抽象产品角色: 它一般是具体产品继承的父类或者实现的接口。在 java 中由接口或者抽象类来实现。 具体产品角色: 工厂类所创建的对象就是此角色的实例。在 java 中由一个具体类实现。 来用类图来清晰的表示下的它们之间的关系 (如果对类图不太了解,请参考我关于类图的文章) : 那么简单工厂模式怎么来使用呢?我们就以简单工厂模式来改造老板坐车的方式——现在老板只需要坐在车里对司机说句: “开车"就可以了。 // 抽象产品 interface Car { void drive(); } // 具体产品 class Benze implements Car { public void drive() { System.out.println("Driving Benz"); } } // 具体产品 class Bmw implements Car { public void drive() { System.out.println("Driving Bmw"); } } // 工厂类 class Driver { // 工厂方法,注意返回类型为抽象产品 public static Car createCar(String s) throws Exception { // 判断逻辑,返回具体的产品角色给 Client if (s.equalsIgnoreCase("Benz")) { return new Benze(); } else if (s.equalsIgnoreCase("Bmw")) { return new Bmw(); } else { throw new Exception(); } } } // 客户端 public class Magnate { public static void main(String[] args) throws Exception { // 告诉司机我今天坐奔驰 Car car = Driver.createCar("benz"); // 下命令: 开车 car.drive(); car = Driver.createCar("bmw"); car.drive(); } } 将本程序空缺的其他信息填充完整后即可运行。如果你将所有的类放在一个文件中,请不要忘记只能有一个类被声明为public。本程序在jdk1.4 下运行通过。 ...

2026-04-16 · 3 min · 440 words · -

AI agent development

AI Agent 开发的本质 开发 AI Agent 的核心是将已验证的知识和流程固化成可执行的规则和指令。 具体来说: 知识固化 - 把专家经验、最佳实践、业务规则编码成 Agent 可以遵循的指令 流程自动化 - 将重复性的决策流程转化为确定性的执行步骤 质量保障 - 通过固化的规则确保每次执行的一致性,避免人为疏忽 迭代优化 - 每次发现新问题或更好的做法,就更新这些"固化的知识",Agent 的能力随之提升 本质上是把隐性知识显性化,把经验驱动变成规则驱动。 这也意味着 Agent 的质量上限取决于你固化进去的知识质量。垃圾进,垃圾出;好的经验进,稳定的高质量输出。 为什么需要固化知识 LLM 的知识覆盖面极广,解决同一个问题可能有多条路径,但其中只有少部分是最佳实践。具体输出什么内容,很大程度上取决于提示词的质量。 把已验证的解决方案固化到 Agent 里,能让 Agent 在特定领域有更稳定、更优质的表现——相当于给 LLM 划定了一条"黄金路径",避免它在众多可能性中随机游走。 常用技术栈 编程语言 Python(主流,生态丰富) JavaScript/TypeScript(Web/Node.js Agent) Go、Java(高性能/企业级) 大语言模型与 API OpenAI GPT-4/3.5、Claude、Llama Hugging Face Transformers LangChain、LlamaIndex(Agent 框架) Web 框架与服务 FastAPI、Flask(Python) Express.js(Node.js) Django 数据存储 Redis、MongoDB、PostgreSQL、SQLite 向量数据库:Milvus、Pinecone、Weaviate 消息队列与异步任务 Celery、RabbitMQ、Kafka 容器与部署 Docker、Kubernetes 云服务:AWS、Azure、GCP 前端交互 React、Vue.js WebSocket、RESTful API 其他 Prompt 工程、工具插件系统 OAuth2、JWT(安全认证) 日志与监控:Prometheus、Grafana Python 调用模型方式 AI Agent 用 Python 开发时,可以调用本地模型或云端模型: ...

2026-03-31 · 27 min · 5749 words · -

Exception Handling

异常捕捉的确是对性能有影响的,那是因为一旦异常被抛出,函数也就跟着 return 了。而程序在执行时需要处理 函数栈 的上下文,这会导致性能变得很慢,尤其是线程栈比较深的时候。但从另一方面来说,异常的抛出基本上表明程序的错误。程序在绝大多数情况下,应该是在没有异常的情况下运行的, 所以,有异常的情况应该是少数的情况,不会影响正常处理的性能问题。 对于我们并不期望会发生的事,我们可以使用异常捕捉;对于我们觉得可能会发生的事,使用返回码。 https://time.geekbang.org/column/article/675 你觉得自己是一个Java专家吗?是否肯定自己 已经全面掌握了Java的异常处理机制?在下面这段代码中,你能够迅速找出异常处理的六个问题吗? 1 OutputStreamWriter out = … 2 java.sql.Connection conn = … 3 try { // ⑸ 4 Statement stat = conn.createStatement(); 5 ResultSet rs = stat.executeQuery( 6 “select uid, name from user”); 7 while (rs.next()) 8 { 9 out.println(“ID: " + rs.getString(“uid”) // ⑹ 10 “,姓名: " + rs.getString(“name”)); 11 } 12 conn.close(); // ⑶ 13 out.close(); 14 } 15 catch(Exception ex) // ⑵ 16 { 17 ex.printStackTrace(); //⑴,⑷ ...

2026-03-23 · 6 min · 1146 words · -

JMM, Java Memory Model, java 内存模型

JMM, Java Memory Model, java 内存模型 Java内存模型,是指Java程序在运行时内存的模型,而Java代码是运行在Java虚拟机之上的,由Java虚拟机通过解释执行(解释器)或编译执行(即时编译器)来完成, 所以Java内存模型,也就是指Java虚拟机的运行时内存模型。 Java内存比较流行的说法便是堆和栈,这其实是非常粗略的一种划分,这种划分的"堆"对应内存模型的Java堆,“栈"是指虚拟机栈,然而Java内存模型远比这更复杂 @startuml skinparam componentStyle rectangle skinparam nodesep 10 skinparam ranksep 10 component "Java Run-time Data Areas" { [heap] component thread0 { [PC Register] as pc0 [thread stack] as ts0 [Native Method Stack] as nms0 pc0 -[hidden]-> ts0 ts0 -[hidden]-> nms0 } component thread1 { [PC Register] as pc1 [thread stack] as ts1 [Native Method Stack] as nms1 pc1 -[hidden]-> ts1 ts1 -[hidden]-> nms1 } thread0 --[hidden]--> heap thread1 --[hidden]--> heap component "method area" as ma { [constant pool] as cp } heap -[hidden]-> ma } @enduml Java 虚拟机的内存空间 (运行时数据区)分为 5 个部分: 程序计数器, PC register Java 虚拟机栈, JVM stack, 线程栈, thread stack 本地方法栈, native method stack 堆, heap 方法区, method area,方法区里面还有一个常量池 线程私有的数据区: 程序计数器,虚拟机栈,本地方法栈 所有线程共有的数据区: Java堆,方法区 ...

2021-09-21 · 3 min · 596 words · -

Unsafe

“Unsafe” unsafe Unsafe在sun.misc 下,顾名思义,这是一个不安全的类,因为Unsafe类所操作的并不属于Java标准,Java的一系列内存操作都是交给jvm的,而Unsafe类却能有像C语言的指针一样直接操作内存的能力,同时也会带来了指针的问题。过度使用Unsafe类的话,会使出错率变得更大,因此官方才命名为Unsafe,并且不建议使用,连注释的没有。 而为了安全使用Unsafe,Unsafe类只允许jdk自带的类使用,从下面的代码中可以看出 public static Unsafe getUnsafe() { Class<?> caller = Reflection.getCallerClass(); if (!VM.isSystemDomainLoader(caller.getClassLoader())) throw new SecurityException("Unsafe"); return theUnsafe; } 如果当前Class是非系统加载的(也就是caller.getClassLoader()不为空),直接抛出SecurityException 。 在java9之后,又出现了一个jdk.internal.misc.Unsafe类,其功能与sun.misc.Unsafe类是一样的,唯一不一样的是在 getSafe() 的时候,jdk.internal.misc.Unsafe是没有做校验的,但是jdk包下的代码,应用开发时是不能直接调用的,而且在java9之后,两个Unsafe类都有充足的注释。 JUC紧密使用了Unsafe的功能。 功能简介 Unsafe类的功能主要分为内存操作、CAS、Class相关、对象操作、数组相关、内存屏障、系统相关、线程调度等功能。 内存操作 堆外(native memory)内存操作 //分配内存,并返回内存地址 public native long allocateMemory(long bytes); //扩充内存,address可以是allocateMemory方法返回的地址,bytes是扩充的大小 public native long reallocateMemory(long address, long bytes); //释放内存 public native void freeMemory(long address); //在给定的内存块设置默认值 public native void setMemory(long address, long bytes, byte value); //获取指定地址值的byte类型 public native byte getByte(long address); //设置堆外指定值的byte类型的值 public native void putByte(long address, byte x); ...

2021-09-20 · 3 min · 440 words · -

自动装箱 拆箱

自动装箱 拆箱 Java作为面向对象语言,有人认为所看到的都是对象,事实上,在Java SE 5之前,基本类型默认并不是采用对象存在的如果您想要把基本类型作为对象来处理,就必须自行转换,不过,在Java SE 5之后,为基本数据类型提供了自动装箱和拆箱功能,使得将基本类型转换为对象变得极其便捷。 在这里来捋一捋java的基本数据类型,不理不要紧,一理才发现俺也掌握的不是那么明确,在这里俺也再次学习下 总计有八个,分别是 byte字节型 (一个字节) ,char字符型 (两个字节) ,short短整型 (两个字节) ,int整型 (四个字节) , long长整型 (八个字节) ,float浮点型 (四个字节) ,double 双精度浮点型 (八个字节) ,boolean型 (一个字节) 在javase5之前,如果想要把基本数据类型作为对象来操作,就需要采用对应的对象,来把它们打包才行 现在虽然不用这样了,但其中也有一些要注意的地方,俺揪出来晒晒。 先说说类和对象,建立个概念吧先 类-可以认为是对象的设计图 对象-是按照设计图实现了的具体工具 先这么简单理解吧,如果真要扯开了说,那可是软件工程里面的一门专业课,我们有个概念就好 之所以要将基本类型数据打包成为对象,原因很简单,是因为对象可以携带更多的数据。 手动、自动装箱拆箱示例 Long,Integer,Double,Float,Boolean等等的类就是所谓的wrapper类,就跟wrapper这个单词所代表的意思一样,就是提供一个"包装,加壳",把基本数据类型放在里面,来看代码,体会下先 public class WrapperDemo{ public staticvoid main(String[] args){ int data1=21; int data2=24; //打包成为对象 Integer data1Wrapper = new Integer(data1); Integer data2Wrapper = new Integer(data2); //原始数据直接除以3 System.out.println(data1/3); //将数据打包,转换为double型,除以3 System.out.println(data1Wrapper.doubleValue()/3); //比较 System.out.println(data1Wrapper.compareTo(data2Wrapper)); } } 图1-1 WrapperDemo的运行结果 通过上面的代码和运行结果,看到了将基本数据类型打包成为对象带来的好处了吧,别着急,这还只是javase5之前的做法,在javase5之后就已经支持自动装箱和拆箱了,在这,就不再单独写代码出来了,只写几个能说明问题的语句就可以了,相信很容易理解的。 javase5之前,手动打包 Integer data1 = new Integer(10); ...

2020-06-30 · 1 min · 201 words · -

idea plugin

idea plugin java 编码规范检查 Alibaba Java Coding Guidelines idea 中统计代码行数 plugin: Statistic https://blog.csdn.net/liuchaoxuan/article/details/81270341

2020-04-07 · 1 min · 13 words · -

java 模块系统, Jigsaw

java 模块系统, Jigsaw Java9 之前的版本 .class 文件是 JVM 看到的最小可执行文件,而一个大型程序需要编写很多 Class,并生成一堆 .class 文件,不便于管理,所以,jar 文件就是 class 文件的容器。 在 Java9 之前,一个大型 Java 程序会生成自己的 jar 文件,同时引用依赖的第三方 jar 文件,而 JVM 自带的 Java 标准库,实际上也是以 jar 文件形式存放的,这个文件叫 rt.jar,一共有 60 多 M。 如果是自己开发的程序,除了一个自己的 app.jar 以外,还需要一堆第三方的 jar 包,运行一个 Java 程序,一般来说,命令行写这样: java -cp app.jar:a.jar:b.jar:c.jar com.liaoxuefeng.sample.Main jar 只是用于存放 class 的容器,它并不关心 class 之间的依赖。 从 Java9 开始,原有的 Java 标准库已经由一个单一巨大的 rt.jar 分拆成了几十个模块,这些模块以 .jmod 扩展名标识,可以在 $JAVA_HOME/jmods 目录下找到它们: java.base.jmod java.compiler.jmod java.datatransfer.jmod java.desktop.jmod ... 这些.jmod文件每一个都是一个模块,模块名就是文件名 如果把 Java 8 比作单体应用,那么引入模块系统之后,从 Java 9 开始,Java 就华丽的转身为微服务。模块系统,项目代号 Jigsaw,最早于 2008 年 8 月提出(比 Martin Fowler 提出微服务还早 6 年),2014 年跟随 Java 9 正式进入开发阶段,最终跟随 Java 9 发布于 2017 年 9 月。 ...

2020-03-08 · 4 min · 756 words · -

java 开发环境

java 开发环境 jdk wiloon.com/jdk 安装 maven https://maven.apache.org/download.cgi apache-maven-3.6.3-bin.zip 安装 idea https://www.jetbrains.com/idea/download/#section=windows https://download.jetbrains.8686c.com/idea/ideaIC-2019.3.3.exe 安装 eclipse https://www.eclipse.org/downloads/packages/ Eclipse IDE for Java Developers, Windows 64-bit subclipse https://github.com/subclipse/subclipse/wiki Help>Install New Software>Add latest: https://dl.bintray.com/subclipse/releases/subclipse/latest/

2020-02-28 · 1 min · 29 words · -

netty Reactor 模式

netty Reactor模式 Reactor模式的角色构成(Reactor模式一共有5中角色构成): Handle (句柄或描述符,在Windows下称为句柄,在Linux下称为描述符):本质上表示一种资源(比如说文件描述符,或是针对网络编程中的socket描述符),是由操作系统提供的;该资源用于表示一个个的事件,事件既可以来自于外部,也可以来自于内部;外部事件比如说客户端的连接请求,客户端发送过来的数据等;内部事件比如说操作系统产生的定时事件等。它本质上就是一个文件描述符,Handle是事件产生的发源地。 Synchronous Event Demultiplexer (同步事件分离器):它本身是一个系统调用,用于等待事件的发生(事件可能是一个,也可能是多个)。调用方在调用它的时候会被阻塞,一直阻塞到同步事件分离器上有事件产生为止。对于Linux来说,同步事件分离器指的就是常用的I/O多路复用机制,比如说select、poll、epoll等。在Java NIO领域中,同步事件分离器对应的组件就是Selector;对应的阻塞方法就是select方法。 Event Handler (事件处理器):本身由多个回调方法构成,这些回调方法构成了与应用相关的对于某个事件的反馈机制。在Java NIO领域中并没有提供事件处理器机制让我们调用或去进行回调,是由我们自己编写代码完成的。Netty相比于Java NIO来说,在事件处理器这个角色上进行了一个升级,它为我们开发者提供了大量的回调方法,供我们在特定事件产生时实现相应的回调方法进行业务逻辑的处理,即,ChannelHandler。ChannelHandler中的方法对应的都是一个个事件的回调。 Concrete Event Handler (具体事件处理器):是事件处理器的实现。它本身实现了事件处理器所提供的各种回调方法,从而实现了特定于业务的逻辑。它本质上就是我们所编写的一个个的处理器实现。 Initiation Dispatcher (初始分发器):实际上就是Reactor角色。它本身定义了一些规范,这些规范用于控制事件的调度方式,同时又提供了应用进行事件处理器的注册、删除等设施。它本身是整个事件处理器的核心所在,Initiation Dispatcher会通过 Synchronous Event Demultiplexer 来等待事件的发生。一旦事件发生,Initiation Dispatcher 首先会分离出每一个事件,然后调用事件处理器,最后调用相关的回调方法来处理这些事件。Netty中ChannelHandler 里的一个个回调方法都是由 bossGroup 或 workGroup 中的某个EventLoop来调用的。 Reactor模式流程 ① 初始化Initiation Dispatcher,然后将若干个Concrete Event Handler注册到Initiation Dispatcher中。当应用向Initiation Dispatcher注册Concrete Event Handler时,会在注册的同时指定感兴趣的事件,即,应用会标识出该事件处理器希望Initiation Dispatcher在某些事件发生时向其发出通知,事件通过Handle来标识,而Concrete Event Handler又持有该Handle。这样,事件 ————> Handle ————> Concrete Event Handler 就关联起来了。 ② Initiation Dispatcher 会要求每个事件处理器向其传递内部的Handle。该Handle向操作系统标识了事件处理器。 ③ 当所有的Concrete Event Handler都注册完毕后,应用会调用handle_events方法来启动Initiation Dispatcher的事件循环。这是,Initiation Dispatcher会将每个注册的Concrete Event Handler的Handle合并起来,并使用Synchronous Event Demultiplexer(同步事件分离器)同步阻塞的等待事件的发生。比如说,TCP协议层会使用select同步事件分离器操作来等待客户端发送的数据到达连接的socket handler上。 比如,在Java中通过Selector的select()方法来实现这个同步阻塞等待事件发生的操作。在Linux操作系统下,select()的实现中 a)会将已经注册到Initiation Dispatcher的事件调用epollCtl(epfd, opcode, fd, events)注册到linux系统中,这里fd表示Handle,events表示我们所感兴趣的Handle的事件;b)通过调用epollWait方法同步阻塞的等待已经注册的事件的发生。不同事件源上的事件可能同时发生,一旦有事件被触发了,epollWait方法就会返回;c)最后通过发生的事件找到相关联的SelectorKeyImpl对象,并设置其发生的事件为就绪状态,然后将SelectorKeyImpl放入selectedSet中。这样一来我们就可以通过Selector.selectedKeys()方法得到事件就绪的SelectorKeyImpl集合了。 ④ 当与某个事件源对应的Handle变为ready状态时(比如说,TCP socket变为等待读状态时),Synchronous Event Demultiplexer就会通知Initiation Dispatcher。 ⑤ Initiation Dispatcher会触发事件处理器的回调方法,从而响应这个处于ready状态的Handle。当事件发生时,Initiation Dispatcher会将被事件源激活的Handle作为『key』来寻找并分发恰当的事件处理器回调方法。 ⑥ Initiation Dispatcher会回调事件处理器的handle_event(type)回调方法来执行特定于应用的功能(开发者自己所编写的功能),从而相应这个事件。所发生的事件类型可以作为该方法参数并被该方法内部使用来执行额外的特定于服务的分离与分发。 ...

2019-10-03 · 1 min · 186 words · -

Java诊断工具 - btrace

Java诊断工具 - btrace https://github.com/btraceio/btrace/releases 打印慢调用 创建一个java类 // MethodDuration_redis.java import com.sun.btrace.annotations.*; import static com.sun.btrace.BTraceUtils.*; import java.util.Date; @BTrace public class MethodDuration_redis{ private static int i=0; @OnMethod(clazz = "com.wiloon.package0.Class0",method = "method0",location=@Location(Kind.RETURN)) public static void printMethodRunTime(@ProbeClassName String probeClassName,@Duration long duration){ long d=duration / 1000000; if (d>9){ //大于9毫秒的调用 i++; println("index: "+ i +", timestamp:"+timestamp("HH:mm:ss")+", "+probeClassName + ", duration: " + d + " ms"); } } } 找到 java 进程并执行 btrace # 打印java进程 jcmd -l # 执行btrace, ctrl-c 退出 /bin/btrace <PID> MethodDuration_redis.java Btrace BTrace 是检查和解决线上的问题的杀器,BTrace 可以通过编写脚本的方式,获取程序执行过程中的一切信息,并且,注意了,不用重启服务,是的,不用重启服务。写好脚本,直接用命令执行即可,不用动原程序的代码。 ...

2018-06-02 · 2 min · 258 words · -

netty ByteBuf

netty ByteBuf Netty ByteBuf 优势 Netty 提供了ByteBuf,来替代Java NIO的 ByteBuffer,操作内存缓冲区。 与Java NIO的 ByteBuffer 相比,ByteBuf的优势如下: Pooling (池化,这点减少了内存复制和GC,提升效率) 可以自定义缓冲类型 通过一个内置的复合缓冲类型实现零拷贝 扩展性好,比如 StringBuffer 不需要调用 flip()来切换读/写模式 读取和写入索引分开 方法链 引用计数 ———————————————— 版权声明:本文为CSDN博主「架构师-尼恩」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/crazymakercircle/article/details/84198042 https://caorong.github.io/2017/01/16/head-first-netty-3/ https://segmentfault.com/a/1190000007560884 根据 Wiki 对 Zero-copy 的定义: “Zero-copy” describes computer operations in which the CPU does not perform the task of copying data from one memory area to another. This is frequently used to save CPU cycles and memory bandwidth when transmitting a file over a network. ...

2018-03-21 · 5 min · 855 words · -

jetty-maven-plugin, jetty maven plugin

jetty-maven-plugin, jetty maven plugin maven plugin <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.4.33.v20201020</version> <configuration> <stopKey>stop</stopKey> <stopPort>5599</stopPort> <webApp> <!-- <contextPath>/app0</contextPath> --> <contextPath>/</contextPath> <defaultsDescriptor>src/main/resources/webdefault.xml</defaultsDescriptor> </webApp> <scanIntervalSeconds>2</scanIntervalSeconds> [httpConnector](httpConnector) <port>8080</port> </httpConnector> </configuration> </plugin> webdefault.xml 可以去maven的本地仓库找到 .m2\repository\org\eclipse\jetty\jetty-webapp\9.4.20.v20190813 解压后在这里可以找到webdefault.xml jetty-webapp-9.4.20.v20190813\org\eclipse\jetty\webapp run mvn jetty:run mvnDebug jetty:run # 默认调试端口8000 debug - mvnDebug https://blog.wiloon.com/?p=15212 mvnDebug -suspend默认为n, https://www.eclipse.org/jetty/documentation/jetty-11/programming-guide/index.html#jetty-maven-plugin http://www.blogjava.net/fancydeepin/archive/2015/06/23/maven-jetty-plugin.html https://my.oschina.net/jackieyeah/blog/524556 https://stackoverflow.com/questions/7875002/setting-debug-configuration-for-mavenjettyeclipse

2018-03-06 · 1 min · 48 words · -

ConcurrentLinkedQueue

ConcurrentLinkedQueue http://www.infoq.com/cn/articles/ConcurrentLinkedQueue 一个基于链接节点的无界线程安全队列。此队列按照 FIFO(先进先出)原则对元素进行排序。队列的头部 是队列中时间最长的元素。队列的尾部 是队列中时间最短的元素。 新的元素插入到队列的尾部,队列获取操作从队列头部获得元素。当多个线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择。此队列不允许使用 null 元素。 https://www.cnblogs.com/yangzhenlong/p/8359875.html ConcurrentLinkedQueue是Queue的一个安全实现.Queue中元素按FIFO原则进行排序.采用CAS操作,来保证元素的一致性。 https://blog.51cto.com/u_15259710/3193985 https://juejin.cn/post/6844903602427805704

2018-01-03 · 1 min · 16 words · -

java grpc

java grpc maven依赖 <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>1.31.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.31.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.31.0</version> </dependency> <dependency> <!-- necessary for Java 9+ --> <groupId>org.apache.tomcat</groupId> <artifactId>annotations-api</artifactId> <version>6.0.53</version> <scope>provided</scope> </dependency> create proto file in src/main/proto/foo.proto option java_package = “com.wiloon.foo”; maven 执行 mvn compile, 就可以在target/generated-sources 下看到生成的源码了 gradle build find generated source in build/generated/source/proto/main/grpc/com/wiloon/foo/foo.java https://grpc.io/docs/quickstart/java.html https://github.com/google/protobuf-gradle-plugin https://www.jianshu.com/p/59ac036b0d7b

2017-09-08 · 1 min · 55 words · -

Parallel Scavenge

Parallel Scavenge Parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CMS等收集器的关注点尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量 (Throughput) 。所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间) ,虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。 停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户的体验;而高吞吐量则可以最高效率地利用CPU时间,尽快地完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。 Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数及直接设置吞吐量大小的 -XX:GCTimeRatio参数。 MaxGCPauseMillis参数允许的值是一个大于0的毫秒数,收集器将尽力保证内存回收花费的时间不超过设定值。不过大家不要异想天开地认为如果把这个参数的值设置得稍小一点就能使得系统的垃圾收集速度变得更快,GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的: 系统把新生代调小一些,收集300MB新生代肯定比收集500MB快吧,这也直接导致垃圾收集发生得更频繁一些,原来10秒收集一次、每次停顿100毫秒,现在变成5秒收集一次、每次停顿70毫秒。停顿时间的确在下降,但吞吐量也降下来了。 GCTimeRatio参数的值应当是一个大于0小于100的整数,也就是垃圾收集时间占总时间的比率,相当于是吞吐量的倒数。如果把此参数设置为19,那允许的最大GC时间就占总时间的5% (即1 / (1+19) ) ,默认值为99,就是允许最大1% (即1 / (1+99) ) 的垃圾收集时间。 由于与吞吐量关系密切,Parallel Scavenge收集器也经常被称为"吞吐量优先"收集器。除上述两个参数之外,Parallel Scavenge收集器还有一个参数-XX:+UseAdaptiveSizePolicy值得关注。这是一个开关参数,当这个参数打开之后,就不需要手工指定新生代的大小 (-Xmn) 、Eden与Survivor区的比例 (-XX:SurvivorRatio) 、晋升老年代对象年龄 (-XX:PretenureSizeThreshold) 等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量,这种调节方式称为GC自适应的调节策略 (GC Ergonomics) 。如果读者对于收集器运作原理不太了解,手工优化存在困难的时候,使用Parallel Scavenge收集器配合自适应调节策略,把内存管理的调优任务交给虚拟机去完成将是一个很不错的选择。只需要把基本的内存数据设置好 (如-Xmx设置最大堆) ,然后使用MaxGCPauseMillis参数 (更关注最大停顿时间) 或GCTimeRatio参数 (更关注吞吐量) 给虚拟机设立一个优化目标,那具体细节参数的调节工作就由虚拟机完成了。自适应调节策略也是Parallel Scavenge收集器与ParNew收集器的一个重要区别。 HotSpot VM里多个GC有部分共享的代码。有一个分代式GC框架,Serial/Serial Old/ParNew/CMS都在这个框架内;在该框架内的young collector和old collector可以任意搭配使用,所谓的"mix-and-match"。 而ParallelScavenge与G1则不在这个框架内,而是各自采用了自己特别的框架。这是因为新的GC实现时发现原本的分代式GC框架用起来不顺手。请参考官方文档的Collector Styles一段。 ParallelScavenge (PS) 的young collector就如其名字所示,是并行的拷贝式收集器。本来这个young collector就是"Parallel Scavenge"所指,但因为它不兼容原本的分代式GC框架,为了凸显出它是不同的,所以它的young collector带上了PS前缀,全名变成PS Scavenge。对应的,它的old collector的名字也带上了PS前缀,叫做PS MarkSweep。 这个PS MarkSweep默认的实现实际上是一层皮,它底下真正做mark-sweep-compact工作的代码是跟分代式GC框架里的serial old (这个collector名字叫做MarkSweepCompact) 是共用同一份代码的。也就是说实际上PS MarkSweep与MarkSweepCompact在HotSpot VM里是同一个collector实现,包了两张不同的皮;这个collector是串行的。 ...

2017-06-05 · 1 min · 153 words · -

java, jvm GC

java, jvm GC STW (Stop the World) 悬挂指针 查看当前使用的 GC jmx JMX 查看当前JVM使用的GC MBean java.lang.GarbageCollector java code 输出结果跟JMX查看的结果相同. import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.util.List; public class TestGC { public static void main(String args[]) { List<GarbageCollectorMXBean> l = ManagementFactory.getGarbageCollectorMXBeans(); for (GarbageCollectorMXBean b : l) { System.out.println(b.getName()); } } } prod gc: Copy MarkSweepCompact // 输出 Copy, MarkSweepCompact 代表正在使用单线程的垃圾回收器 -XX:+UseSerialGC 名词解释: 在 GC 的世界里对象指的是通过应用程序利用的数据的集合。是 GC 的基本单位。一般由头 (header) 和域 (field) 构成。 ...

2017-06-04 · 5 min · 866 words · -

openjdk

openjdk https://openjdk.org/projects/jdk/ https://jdk.java.net windows install openjdk https://github.com/ojdkbuild/ojdkbuild winget install Microsoft.OpenJDK.21 linux https://github.com/ojdkbuild/contrib_jdk8u-ci https://github.com/ojdkbuild/ojdkbuild/releases/download/java-1.8.0-openjdk-1.8.0.242-1.b08/java-1.8.0-openjdk-1.8.0.242-1.b08.ojdkbuild.windows.x86_64.msi ## archlinux install openjdk # openjdk sudo pacman -S jdk-openjdk sudo pacman -S openjdk-src # jdk8 sudo pacman -S jdk8-openjdk openjdk8-src sudo pacman -S jdk11-openjdk # 查看已经安装的 jdk archlinux-java status sudo archlinux-java set java-11-openjdk sudo pacman -S jdk17-openjdk openjdk17-src 切换 jdk 版本 archlinux-java help archlinux-java status archlinux-java set java-14-openjdk 查看当前 java 版本 java -version sudo archlinux-java status ubuntu openjdk # 安装默认版本, 最新的 TLS 版本 sudo apt update sudo apt install default-jdk # 验证安装 java -version javac -version sudo apt install openjdk-21-jdk sudo apt install openjdk-8-jdk sudo apt install openjdk-8-source # openjdk 17, depends libc6 (>= 2.33) sudo sudo apt update && sudo apt install openjdk-17-jdk openjdk-17-source # 默认目录 ls -l /usr/lib/jvm/ # 查看 sudo update-java-alternatives -l # 切换 jdk sudo update-alternatives --config java 手动安装 openjdk download jdk JDK ...

2017-04-21 · 2 min · 275 words · -