├── Java基础学习 ├── JVM系列基础图.md ├── Java 异常处理的十个建议.md ├── Java程序员必备基础:JDK 5-15都有哪些经典新特性.md ├── Java程序员必备的基础流程图.md ├── Java程序员必备:异常的十个关键知识点.md ├── Java程序员面试必备:Volatile全方位解析.md ├── README.MD ├── README.MD.bak ├── if-else代码优化的八种方案.md ├── java序列化.md ├── jstack命令解析.md ├── 掌握Java枚举这几个知识点,日常开发就够啦.md ├── 有关于Java Map,应该掌握的8个问题.md ├── 泛型解析.md ├── 线程池解析.md ├── 触发类加载的六大时机.md └── 谈谈Java反射:从入门到实践,再到原理.md ├── Java程序员需要看哪些书 ├── Java程序员需要看哪些书.md └── README.MD ├── Java面试题集结号 ├── Elasticsearch │ └── Elasticsearch面试.md ├── JVM 篇 │ ├── 28. 了解过JVM调优没,基本思路是什么,如何确定它们的大小.md │ ├── 29. 淘宝热门商品信息在JVM哪个内存区域.md │ ├── 30. 字节码的编译过程.md │ ├── 31. Java需要开发人员回收内存垃圾吗?.md │ ├── 32. Java中垃圾回收有什么目的?什么时候进行垃圾回收?.md │ ├── 33. System.gc()和Runtime.gc()会做什么事情?.md │ └── JVM面试题.md ├── Java 并发 && 多线程 │ ├── 26. 线程的生命周期,线程的几种状态。.md │ ├── 40. happen-before原则.md │ ├── 41. 公平锁非公平锁.md │ ├── 42.可重入锁.md │ ├── 43.独享锁共享锁.md │ ├── 44. 偏向锁轻量级锁重量级锁.md │ ├── 45. 如何保证内存可见性.md │ ├── 9. 如何检测死锁?怎么预防死锁?死锁四个必要条件.md │ ├── Java 并发 && 多线程.md │ ├── 个人珍藏的80道多线程并发面试题(1-10答案解析).md │ └── 个人珍藏的80道多线程并发面试题(11-20答案解析).md ├── Java集合 │ └── Java集合面试题答案.md ├── README.MD ├── README.MD.bak ├── dubbo │ └── dubbo面试题 │ │ └── dubbo面试题.md ├── java 基础 │ ├── 1-80题答案解析.md │ ├── Java基础面试题.md │ └── 基础题答案 │ │ ├── 1.equals和==的区别.md │ │ ├── 10. String类能被继承吗,为什么.md │ │ ├── 101. 抽象类必须要有抽象方法吗?.md │ │ ├── 11.说说Java中多态的实现原理.md │ │ ├── 110. 抽象类能使用 final 修饰.md │ │ ├── 111. 接口和抽象类有什么区别.md │ │ ├── 112. Files的常用方法都有哪些.md │ │ ├── 113. 线程和进程的区别?.md │ │ ├── 117. 说说代理的实现原理.md │ │ ├── 118. 了解哪设计模式,举例说说在jdk源码哪些用到了你说的设计模式.md │ │ ├── 119. 什么是BS架构?什么是CS架构.md │ │ ├── 12. Java泛型和类型擦除.md │ │ ├── 120. Java有那些开发平台呢?.md │ │ ├── 121. Java内部类为什么可以访问到外部类呢?.md │ │ ├── 122. Java支持的数据类型有哪些?什么是自动拆装箱呢?.md │ │ ├── 123. 创建线程有几种不同的方式.md │ │ ├── 124. hashCode()和equals()方法的重要性体现在什么地方?.md │ │ ├── 16. &和&&的区别.md │ │ ├── 17. Java 中 IO 流分为几种.md │ │ ├── 2.final, finally, finalize.md │ │ ├── 21. 守护线程是什么?用什么方法实现守护线程.md │ │ ├── 26. String s 与new String的区别.md │ │ ├── 3.重写与重载的区别.md │ │ ├── 31. JDK 和 JRE 有什么区别?.md │ │ ├── 32. String 类的常用方法都有那些.md │ │ ├── 36. 什么是值传递和引用传递?.md │ │ ├── 37. 是否可以在static环境中访问非static变量.md │ │ ├── 4.两个对象的 hashCode()相同,则 equals()是否也一定为 true.md │ │ ├── 49. 访问修饰符public,private,protected,以及不写(默认)时的区别?.md │ │ ├── 5.抽象类和接口有什么区别.md │ │ ├── 50. final 在 java 中有什么作用?.md │ │ ├── 52. String 属于基础的数据类型吗?.md │ │ ├── 53. 如何将字符串反转?.md │ │ ├── 6.BIO、NIO、AIO 有什么区别.md │ │ ├── 7. String,Stringbuffer,StringBuilder的区别.md │ │ ├── 77. Java 中的 Math. round(-1. 5) 等于多少?.md │ │ ├── 8.JAVA中的几种基本数据类型是什么,各自占用多少字节.md │ │ ├── 9. Java 中,Comparator 与Comparable 有什么不同?.md │ │ └── Java锁有哪些种类,以及区别.md ├── linux命 │ └── linux命令面试题.md ├── mybatis │ └── mybatis面试题 │ │ └── mybatis面试题.md ├── netty │ └── netty面试题.md ├── nginx │ └── nginx面试题.md ├── spring cloud │ └── spring cloud面试题.md ├── spring 面试题 │ └── spring相关面试题.md ├── zookeeper │ ├── ZooKeeper的十二连问,你顶得了嘛?.md │ └── zookeeper面试题.md ├── 分布式 │ └── 分布式面试题.md ├── 操作系统 │ ├── 12. 操作系统的页式存储.md │ └── 操作系统面试题.md ├── 数据库 │ ├── 100Mysql答案.md │ └── 数据库面试题.md ├── 数据结构与算法 │ ├── 10. 你了解大O符号(big-O notation)么?你能给出不同数据结构的例子么?.md │ └── 数据结构与算法面试题.md ├── 消息队列 │ └── 消息队列面试题.md ├── 系统设计方案设计 │ ├── 10. 分布式session如何管理,你有哪些方案.md │ ├── 9. 如何设计存储海量数据的存储系统.md │ └── 大厂系统设计方案面试题.md ├── 缓存,Redis │ ├── Redis为什么这么快.md │ └── Redis缓存面试题.md └── 计算机网络 │ ├── 29.你所知道网络协议有那些?.md │ ├── 7. 当你用浏览器打开一个链接(如:httpwww.javastack.cn)到返回结果,发生了什么。.md │ ├── TCP协议15连问.md │ └── 计算机网络面试.md ├── Mysql基础学习 ├── README.md ├── mysql数据库相关流程图原理图.md ├── order by详解.md ├── 一文彻底读懂MySQL事务的四大隔离级别.md ├── 书写高质量SQL的30条建议.md ├── 后端程序员必备:索引失效的十大杂症.md └── 慢慢来,不怕Mysql死锁啦 │ ├── insert on duplicate死锁一次排查分析过程.md │ └── 死锁分析过程.md ├── README.md ├── image ├── 公众号二维码图片.png ├── 夏天的风.jpg └── 赞赏码.jpg ├── letecode解题算法介绍 ├── README.MD ├── 反转链表看一遍就懂.md └── 递归算法详解.md ├── 中间件 └── README.MD ├── 分布式 ├── README.MD └── 后端程序员必备:分布式事务基础篇.md ├── 原创诗集 └── 夏天的风我永远记得.md ├── 后端思维篇 ├── README.md ├── 后端思维一:设计接口的36个锦囊.md └── 后端思维篇二:手把手教你实现一个并行调用模板.md ├── 大厂面试真题 ├── README.MD ├── 腾讯后端面试真题.md ├── 阿里一面,给了几条SQL,问需要执行几次树搜索操作?.md └── 顺丰科技面试真题.md ├── 工作总结 ├── CAS乐观锁解决并发问题的一次实践.md ├── Java日常开发的21个坑.md ├── Java日期处理易踩的十个坑.md ├── Mysql中,21个写SQL的好习惯.md ├── READEME.MD ├── 一次代码优化.md ├── 优化代码的几个小技巧.md ├── 写代码有这16个习惯,规避80%的bug.md ├── 写代码有这些想法,同事才不会认为你是复制粘贴程序员.md ├── 如何设计一个安全对外的接口?加签验签了解一下.md ├── 工作四年,50个让你代码更好的小建议.md ├── 并发环境下,先操作数据库还是先操作缓存?.md ├── 日常工作中最容易犯的几个并发错误.md ├── 日志打印规范.md ├── 程序员必备基础:如何安全传输存储用户密码.md ├── 程序员必备基础:如何安全传输存储用户密码.md.bak ├── 聊聊日常开发中,如何减少bug呢?.md ├── 聊聊日常开发中,如何减少bug呢?.md.bak └── 记一次接口性能优化实践总结:优化接口性能的八个建议.md ├── 方案设计 └── README.MD ├── 生产问题分析 ├── Mysql死锁问题分析.md ├── README.MD ├── 数据库连接池内存泄漏问题分析.md └── 线程池运用不当导致的问题.md ├── 程序人生&面试建议 ├── README.MD └── 金三银四,给面试者的十大建议.md ├── 程序员必备工具篇 ├── README.MD └── 程序员必备基础:Git 命令全方位学习.md ├── 缓存Redis总结 ├── README.MD ├── 七种方案对比分布式锁.md └── 使用Redis的21个注意点.md ├── 茶余饭后谈编程学习周报 ├── 茶余饭后编程周报二.md ├── 茶余饭后谈编程周报一.md └── 茶余饭后谈编程周报三.md └── 高并发 └── README.MD /Java基础学习/JVM系列基础图.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 最近看了深入理解Java虚拟机第三版,整理了一些基础结构图,算是比较全的了,做一下笔记,大家一起学习。 3 | 4 | ### 1.Java虚拟机运行时数据区图 5 | 6 | ![](https://user-gold-cdn.xitu.io/2020/4/29/171c6a4017b17e4a?w=906&h=699&f=png&s=69259) 7 | JVM内存结构是Java程序员必须掌握的基础。 8 | 9 | **程序计数器** 10 | - 程序计数器,可以看作当前线程所执行的字节码的行号指示器 11 | - 它是线程私有的。 12 | 13 | **Java虚拟机栈** 14 | - 线程私有的,生命周期与线程相同。 15 | - 每个方法被执行的时候都会创建一个"栈帧",用于存储局部变量表(包括参数)、操作数栈、动态链接、方法出口等信息。 16 | - 局部变量表存放各种基本数据类型boolean、byte、char、short等 17 | 18 | **本地方法栈** 19 | - 与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的java方法服务,而本地方法栈则是为Native方法服务。 20 | 21 | **Java堆** 22 | 23 | - Java堆是java虚拟机所管理的内存中最大的一块内存区域,也是被各个线程共享的内存区域,在JVM启动时创建。 24 | - 其大小通过-Xms和-Xmx参数设置,-Xms为JVM启动时申请的最小内存,-Xmx为JVM可申请的最大内存。 25 | 26 | **方法区** 27 | - 它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域。 28 | -可以通过-XX:PermSize 和 -XX:MaxPermSize 参数限制方法区的大小。 29 | 30 | 31 | ### 2. 堆的默认分配图 32 | 33 | ![](https://user-gold-cdn.xitu.io/2020/4/30/171c6b5bddadd635?w=955&h=353&f=png&s=30398) 34 | 35 | - Java堆 = 老年代 + 新生代 36 | - 新生代 = Eden + S0 + S1 37 | - 新生代与老年代默认比例的值为 1:2 ,可以通过参数 –XX:NewRatio 配置。 38 | - 默认的,Eden : from : to = 8 : 1 : 1 ,可以通过参数–XX:SurvivorRatio 来设定 39 | 40 | ### 3.方法区结构图 41 | 42 | ![](https://user-gold-cdn.xitu.io/2020/5/1/171cf2f5b90c260e?w=770&h=471&f=png&s=34968) 43 | 44 | 方法区是各个线程共享的内存区域,它用于存储已被虚拟机加载的**类型信息、常量、静态变量、即时编译器编译后的代码缓存**等数据。 45 | 46 | ### 4.对象的内存布局图 47 | 48 | ![](https://user-gold-cdn.xitu.io/2020/5/1/171d01589343138f?w=874&h=574&f=png&s=80878) 49 | 50 | 一个Java对象在堆内存中包括**对象头、实例数据和补齐填充**3个部分: 51 | - 对象头包括Mark Word(存储哈希码,GC分代年龄等) 和 类型指针(对象指向它的类型元数据的指针),如果是数组对象,还有一个保存数组长度的空间 52 | - 实例数据是对象真正存储的有效信息,包括了对象的所有成员变量,其大小由各个成员变量的大小共同决定。 53 | - 对齐填充不是必然存在的,仅仅起占位符的作用。 54 | 55 | 56 | ### 5.对象头的Mark Word图 57 | 58 | ![](https://user-gold-cdn.xitu.io/2020/4/30/171c81fa700b1ecb?w=1089&h=557&f=png&s=233683) 59 | 60 | - Mark Word 用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等。 61 | - 在32位的HotSpot虚拟机中,如果对象处于未被锁定的状态下,那么Mark Word的32bit空间里的25位用于存储对象哈希码,4bit用于存储对象分代年龄,2bit用于存储锁标志位,1bit固定为0,表示非偏向锁。 62 | 63 | ### 6.对象与Monitor关联结构图 64 | ![](https://user-gold-cdn.xitu.io/2020/4/30/171c7d666c9684f7?w=1162&h=960&f=png&s=406313) 65 | 66 | 对象是如何跟monitor有关联的呢? 67 | 68 | 一个Java对象在堆内存中包括对象头,对象头有Mark word,Mark word存储着锁状态,锁指针指向monitor地址。这其实是**Synchronized的底层**哦~ 69 | 70 | ### 7.Java Monitor的工作机理图: 71 | 72 | Java 线程同步底层就是监视锁Monitor~,如下是Java Monitor的工作机理图: 73 | ![](https://user-gold-cdn.xitu.io/2020/4/30/171c7d59c7fb2ff6?w=1280&h=765&f=png&s=271126) 74 | 75 | - 想要获取monitor的线程,首先会进入_EntryList队列。 76 | - 当某个线程获取到对象的monitor后,进入_Owner区域,设置为当前线程,同时计数器_count加1。 77 | - 如果线程调用了wait()方法,则会进入_WaitSet队列。它会释放monitor锁,即将_owner赋值为null,_count自减1,进入_WaitSet队列阻塞等待。 78 | - 如果其他线程调用 notify() / notifyAll() ,会唤醒_WaitSet中的某个线程,该线程再次尝试获取monitor锁,成功即进入_Owner区域。 79 | - 同步方法执行完毕了,线程退出临界区,会将monitor的owner设为null,并释放监视锁。 80 | 。 81 | 82 | ### 8.创建一个对象内存分配流程图 83 | 84 | ![](https://user-gold-cdn.xitu.io/2020/5/1/171d05dec9c7bbfd?w=972&h=943&f=png&s=115538) 85 | 86 | - 对象一般是在Eden区生成。 87 | - 如果Eden区填满,就会触发Young GC。 88 | - 触发Young GC的时候,Eden区实现清除,没有被引用的对象直接被清除。 89 | - 依然存活的对象,会被送到Survivor区,Survivor =S0+S1. 90 | - 每次Young GC时,存活的对象复制到未使用的那块Survivor 区,当前正在使用的另外一块Survivor 区完全清除,接着交换两块Survivor 区的使用状态。 91 | - 如果Young GC要移送的对象大于Survivor区上限,对象直接进入老年代。 92 | - 一个对象不可能一直呆在新生代,如果它经过多次GC,依然活着,次数超过-XX:MaxTenuringThreshold的阀值,它直接进入老年代。简言之就是,对象经历多次滚滚长江,红尘世事,终于成为长者(进入老年代) 93 | 94 | ### 9.可达性分析算法判定对象存活 95 | 可达性分析算法是用来判断一个对象是否存活的~ 96 | ![](https://user-gold-cdn.xitu.io/2020/4/30/171c7e50b8bf0371?w=1322&h=845&f=png&s=96607) 97 | 98 | 算法的核心思想: 99 | - 通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始根据引用关系向下搜索,搜索走过的路径称为“引用链”,当一个对象到 GC Roots 没有任何的引用链相连时(从 GC Roots 到这个对象不可达)时,证明此对象不可能再被使用。 100 | 101 | ### 10.标记-清除算法示意图 102 | 103 | ![](https://user-gold-cdn.xitu.io/2020/5/1/171cef1c308a4f45?w=867&h=753&f=png&s=58714) 104 | 105 | - 标记-清除算法是最基础的垃圾收集算法。 106 | - 算法分为两个阶段,标记和清除。 107 | - 首先标记出需要回收的对象,标记完成后,统一回收掉被标记的对象。 108 | - 当然可以反过来,先标记存活的对象,统一回收未被标记的对象。 109 | - 标记-清除 两个缺点是,执行效率不稳定和内存空间的碎片化问题~ 110 | 111 | ### 11.标记-复制算法示意图 112 | 113 | ![](https://user-gold-cdn.xitu.io/2020/5/1/171cee8899fdf506?w=854&h=762&f=png&s=60398) 114 | 115 | - 1969年 Fenichel提出“半区复制”,将内存容量划分对等两块,每次只使用一块。当这一块内存用完,将还存活的对象复制到另外一块,然后把已使用过的内存空间一次清理掉~ 116 | - 1989年,Andrew Appel提出“Appel式回收”,把新生代划分为较大的Eden和两块较小的Survivor空间。每次分配内存只使用Eden和其中一块Survivor空间。发生垃圾收集时,将Eden和Survivor中仍然存活的对象一次性复制到另外一块Survivor空间上。Eden和Survivor比例是8:1~ 117 | - “半区复制”缺点是浪费可用空间,并且,如果对象存活率高的话,复制次数就会变多,效率也会降低。 118 | 119 | ### 12.标记-整理算法示意图 120 | 121 | ![](https://user-gold-cdn.xitu.io/2020/5/2/171d3312542e805b?w=901&h=765&f=png&s=60562) 122 | - 1974年,Edward 提出“标记-整理”算法,标记过程跟“标记-清除”算法一样,接着让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存~ 123 | - 标记-清除算法和标记整理算法本质差异是:前者是一种非移动式的回收算法,后者是移动式的回收算法。 124 | - 是否移动存活对象都存在优缺点,移动虽然内存回收复杂,但是从程序吞吐量来看,更划算;不移动时内存分配更复杂,但是垃圾收集的停顿时间会更短,所以看收集器取舍问题~ 125 | - Parallel Scavenge收集器是基于标记-整理算法的,因为关注吞吐。CMS收集器是基于标记-清除算法的,因为它关注的是延迟。 126 | 127 | ### 13.垃圾收集器组合图 128 | ![](https://user-gold-cdn.xitu.io/2020/5/2/171d45b536a2c859?w=1010&h=674&f=png&s=59053) 129 | 130 | - 新生代收集器:Serial、ParNew、Parallel Scavenge 131 | - 老年代收集器:CMS、Serial Old、Parallel Old 132 | - 混合收集器:G1 133 | 134 | ### 14.类的生命周期图 135 | 136 | ![](https://user-gold-cdn.xitu.io/2020/4/30/171c843fe9b784cb?w=1364&h=707&f=png&s=67514) 137 | 138 | 一个类从被加载到虚拟机内存开始,到卸载出内存为止,这个生命周期经历了七个阶段:加载、验证、准备、解析、初始化、使用、卸载。 139 | 140 | **加载阶段:** 141 | - 通过一个类的全限定名来获取定义此类的二进制字节流。 142 | - 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。 143 | - 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口 144 | 145 | **验证:** 146 | - 验证的目的是确保Class文件的字节流中包含的信息满足约束要求,保证这些代码运行时不会危害虚拟机自身安全 147 | - 验证阶段有:文件格式校验、元数据校验、字节码校验、符号引用校验。 148 | 149 | **准备** 150 | - 准备阶段是正式为类中定义的变量(静态变量)分配内存并设置类变量初始值的阶段。 151 | 152 | **解析** 153 | - 解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。 154 | 155 | **初始化** 156 | - 到了初始化阶段,才真正开始执行类中定义的Java字节码。 157 | 158 | 159 | ### 15.类加载器双亲委派模型图 160 | 161 | ![](https://user-gold-cdn.xitu.io/2020/4/30/171c84d6f56e220d?w=880&h=857&f=png&s=70024) 162 | 163 | **双亲委派模型构成** 164 | 165 | 启动类加载器,扩展类加载器,应用程序类加载器,自定义类加载器 166 | 167 | **双亲委派模型工作过程是** 168 | 169 | 如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。 170 | 171 | **为什么需要双亲委派模型?** 172 | 173 | 如果没有双亲委派,那么用户是不是可以自己定义一个java.lang.Object的同名类,java.lang.String的同名类,并把它放到ClassPath中,那么类之间的比较结果及类的唯一性将无法保证,因此,双亲委派模型可以防止内存中出现多份同样的字节码。 174 | 175 | ### 16.栈帧概念结构图 176 | 177 | ![](https://user-gold-cdn.xitu.io/2020/5/1/171d0288ac431ad3?w=709&h=814&f=png&s=64282) 178 | 179 | 栈帧是用于支持虚拟机进行方法调用和方法执行背后的数据结构。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址信息。 180 | 181 | **局部变量表** 182 | - 是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。 183 | - 局部变量表的容量以变量槽(Variable Slot)为最小单位。 184 | 185 | **操作数栈** 186 | - 操作数栈,也称操作栈,是一个后入先出栈。 187 | - 当一个方法刚刚开始执行的时候, 该方法的操作数栈也是空的, 在方法的执行过程中, 会有各种字节码指令往操作数栈中写入和提取内容, 也就是出栈与入栈操作。 188 | 189 | **动态连接** 190 | - 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用, 持有引用是为了支持方法调用过程中的动态连接(Dynamic Linking)。 191 | 192 | **方法返回地址** 193 | - 当一个方法开始执行时, 只有两种方式退出这个方法 。一种是执行引擎遇到任意一个方法返回的字节码指令。另外一种退出方式是在方法执行过程中遇到了异常。 194 | 195 | ### 17.Java内存模型图 196 | 197 | ![](https://user-gold-cdn.xitu.io/2020/5/1/171cef6637426e1e?w=823&h=759&f=png&s=251488) 198 | 199 | - Java内存模型规定了所有的变量都存储在主内存中 200 | - 每条线程还有自己的工作内存 201 | - 线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝 202 | - 线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。 203 | - 不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。 204 | 205 | ### 18.线程状态转换关系图 206 | 207 | 208 | ![](https://user-gold-cdn.xitu.io/2020/5/2/171d4191855e2a5a?w=1291&h=622&f=png&s=93088) 209 | Java语言定义了6种线程池状态: 210 | - 新建(New):创建后尚未启动的线程处于这种状态 211 | - 运行(Running):线程开启```start()```方法,会进入该状态。 212 | - 无限等待(Waiting):处于这种状态的线程不会被分配处理器执行时间,一般```LockSupport::park()```,没有设置了Timeoout的```Object::wait()```方法,会让线程陷入无限等待状态。 213 | - 限期等待(Timed Waiting):处于这种状态的线程不会被分配处理器执行时间,在一定时间之后他们会由系统自动唤醒。```sleep()```方法会进入该状态~ 214 | - 阻塞(Blocked):在程序等待进入同步区域的时候,线程将进入这种状态~ 215 | - 结束(Terminated):已终止线程的线程状态,线程已经结束执行 216 | 217 | 218 | ### 19. Class文件格式图 219 | 220 | 221 | ![](https://user-gold-cdn.xitu.io/2020/5/2/171d4bffc662b84b?w=762&h=1013&f=png&s=81290) 222 | - u1、u2、u4、u8 分别代表1个字节、2个字节、4个字节和8个字节的无符号数 223 | - 表是由多个无符号数或者其他表作为数据项构成的复合数据类型 224 | - 每个Class文件的头四个字节被称为魔数(记得以前校招面试,面试官问过我什么叫魔数。。。) 225 | - minor和major version表示次版本号,主版本号 226 | - 紧接着主次版本号之后,是常量池入口,常量池可以比喻为Class文件里的资源仓库~ 227 | 228 | ### 20.JVM参数思维导图 229 | 230 | 231 | ![](https://user-gold-cdn.xitu.io/2020/5/1/171cfb2cb8a29672?w=1479&h=945&f=png&s=154318) 232 | 233 | JVM调优是通往高级开发的必经桥梁,所以好好积累JVM参数配置哈~ 234 | 235 | ### 个人公众号 236 | 237 | ![](https://user-gold-cdn.xitu.io/2019/7/28/16c381c89b127bbb?w=344&h=344&f=jpeg&s=8943) 238 | 239 | - 如果你是个爱学习的好孩子,可以关注我公众号,一起学习讨论。 240 | - 如果你觉得本文有哪些不正确的地方,可以评论,也可以关注我公众号,私聊我,大家一起学习进步哈。 241 | - - github地址:https://github.com/whx123/JavaHome 242 | 243 | 244 | -------------------------------------------------------------------------------- /Java基础学习/Java 异常处理的十个建议.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | Java异常处理的十个建议,希望对大家有帮助~ 3 | 4 | 本文已上传github: 5 | 6 | > https://github.com/whx123/JavaHome 7 | 8 | **公众号:捡田螺的小男孩** 9 | 10 | ### 一、尽量不要使用e.printStackTrace(),而是使用log打印。 11 | **反例:** 12 | ``` 13 | try{ 14 | // do what you want 15 | }catch(Exception e){ 16 | e.printStackTrace(); 17 | } 18 | ``` 19 | **正例:** 20 | ``` 21 | try{ 22 | // do what you want 23 | }catch(Exception e){ 24 | log.info("你的程序有异常啦,{}",e); 25 | } 26 | ``` 27 | **理由:** 28 | - printStackTrace()打印出的堆栈日志跟业务代码日志是交错混合在一起的,通常排查异常日志不太方便。 29 | - e.printStackTrace()语句产生的字符串记录的是堆栈信息,如果信息太长太多,字符串常量池所在的内存块没有空间了,即内存满了,那么,用户的请求就卡住啦~ 30 | 31 | 32 | ### 二、catch了异常,但是没有打印出具体的exception,无法更好定位问题 33 | **反例:** 34 | ``` 35 | try{ 36 | // do what you want 37 | }catch(Exception e){ 38 | log.info("你的程序有异常啦"); 39 | } 40 | ``` 41 | **正例:** 42 | 43 | ``` 44 | try{ 45 | // do what you want 46 | }catch(Exception e){ 47 | log.info("你的程序有异常啦,{}",e); 48 | } 49 | ``` 50 | **理由:** 51 | - 反例中,并没有把exception出来,到时候排查问题就不好查了啦,到底是SQl写错的异常还是IO异常,还是其他呢?所以应该把exception打印到日志中哦~ 52 | 53 | ### 三、不要用一个Exception捕捉所有可能的异常 54 | **反例:** 55 | ``` 56 | public void test(){ 57 | try{ 58 | //…抛出 IOException 的代码调用 59 | //…抛出 SQLException 的代码调用 60 | }catch(Exception e){ 61 | //用基类 Exception 捕捉的所有可能的异常,如果多个层次都这样捕捉,会丢失原始异常的有效信息哦 62 | log.info(“Exception in test,exception:{}”, e); 63 | } 64 | } 65 | ``` 66 | **正例:** 67 | 68 | ``` 69 | public void test(){ 70 | try{ 71 | //…抛出 IOException 的代码调用 72 | //…抛出 SQLException 的代码调用 73 | }catch(IOException e){ 74 | //仅仅捕捉 IOException 75 | log.info(“IOException in test,exception:{}”, e); 76 | }catch(SQLException e){ 77 | //仅仅捕捉 SQLException 78 | log.info(“SQLException in test,exception:{}”, e); 79 | } 80 | } 81 | ``` 82 | 理由: 83 | - 用基类 Exception 捕捉的所有可能的异常,如果多个层次都这样捕捉,会丢失原始异常的有效信息哦 84 | 85 | ### 四、记得使用finally关闭流资源或者直接使用try-with-resource 86 | **反例:** 87 | ``` 88 | FileInputStream fdIn = null; 89 | try { 90 | fdIn = new FileInputStream(new File("/jay.txt")); 91 | //在这里关闭流资源?有没有问题呢?如果发生异常了呢? 92 | fdIn.close(); 93 | } catch (FileNotFoundException e) { 94 | log.error(e); 95 | } catch (IOException e) { 96 | log.error(e); 97 | } 98 | ``` 99 | **正例1:** 100 | 101 | 需要使用finally关闭流资源,如下 102 | ``` 103 | FileInputStream fdIn = null; 104 | try { 105 | fdIn = new FileInputStream(new File("/jay.txt")); 106 | } catch (FileNotFoundException e) { 107 | log.error(e); 108 | } catch (IOException e) { 109 | log.error(e); 110 | }finally { 111 | try { 112 | if (fdIn != null) { 113 | fdIn.close(); 114 | } 115 | } catch (IOException e) { 116 | log.error(e); 117 | } 118 | } 119 | ``` 120 | **正例2:** 121 | 122 | 当然,也可以使用JDK7的新特性try-with-resource来处理,它是Java7提供的一个新功能,它用于自动资源管理。 123 | - 资源是指在程序用完了之后必须要关闭的对象。 124 | - try-with-resources保证了每个声明了的资源在语句结束的时候会被关闭 125 | - 什么样的对象才能当做资源使用呢?只要实现了java.lang.AutoCloseable接口或者java.io.Closeable接口的对象,都OK。 126 | 127 | ``` 128 | try (FileInputStream inputStream = new FileInputStream(new File("jay.txt")) { 129 | // use resources 130 | } catch (FileNotFoundException e) { 131 | log.error(e); 132 | } catch (IOException e) { 133 | log.error(e); 134 | } 135 | ``` 136 | 137 | **理由:** 138 | - 如果不使用finally或者try-with-resource,当程序发生异常,IO资源流没关闭,那么这个IO资源就会被他一直占着,这样别人就没有办法用了,这就造成资源浪费。 139 | 140 | ### 五、捕获异常与抛出异常必须是完全匹配,或者捕获异常是抛异常的父类 141 | 142 | **反例:** 143 | 144 | ``` 145 | //BizException 是 Exception 的子类 146 | public class BizException extends Exception {} 147 | //抛出父类Exception 148 | public static void test() throws Exception {} 149 | 150 | try { 151 | test(); //编译错误 152 | } catch (BizException e) { //捕获异常子类是没法匹配的哦 153 | log.error(e); 154 | } 155 | ``` 156 | **正例:** 157 | 158 | ``` 159 | //抛出子类Exception 160 | public static void test() throws BizException {} 161 | 162 | try { 163 | test(); 164 | } catch (Exception e) { 165 | log.error(e); 166 | } 167 | ``` 168 | 169 | ### 六、捕获到的异常,不能忽略它,至少打点日志吧 170 | **反例:** 171 | 172 | ``` 173 | public static void testIgnoreException() throws Exception { 174 | try { 175 | // 搞事情 176 | } catch (Exception e) { //一般不会有这个异常 177 | 178 | } 179 | } 180 | ``` 181 | **正例:** 182 | 183 | ``` 184 | public static void testIgnoreException() { 185 | try { 186 | // 搞事情 187 | } catch (Exception e) { //一般不会有这个异常 188 | log.error("这个异常不应该在这里出现的,{}",e); 189 | } 190 | } 191 | ``` 192 | 193 | **理由:** 194 | - 虽然一个正常情况都不会发生的异常,但是如果你捕获到它,就不要忽略呀,至少打个日志吧~ 195 | 196 | 197 | ### 七、注意异常对你的代码层次结构的侵染(早发现早处理) 198 | **反例:** 199 | 200 | ``` 201 | public UserInfo queryUserInfoByUserId(Long userid) throw SQLException { 202 | //根据用户Id查询数据库 203 | } 204 | ``` 205 | **正例:** 206 | 207 | ``` 208 | public UserInfo queryUserInfoByUserId(Long userid) { 209 | try{ 210 | //根据用户Id查询数据库 211 | }catch(SQLException e){ 212 | log.error("查询数据库异常啦,{}",e); 213 | }finally{ 214 | //关闭连接,清理资源 215 | } 216 | } 217 | ``` 218 | **理由:** 219 | - 我们的项目,一般都会把代码分 Action、Service、Dao 等不同的层次结构,如果你是DAO层处理的异常,尽早处理吧,如果往上 throw SQLException,上层代码就还是要try catch处理啦,这就污染了你的代码~ 220 | 221 | ### 八、自定义封装异常,不要丢弃原始异常的信息Throwable cause 222 | 223 | 我们常常会想要在捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来,这被称为异常链。公司的框架提供统一异常处理就用到异常链,我们自定义封装异常,不要丢弃原始异常的信息,否则排查问题就头疼啦 224 | 225 | **反例:** 226 | 227 | ``` 228 | public class TestChainException { 229 | public void readFile() throws MyException{ 230 | try { 231 | InputStream is = new FileInputStream("jay.txt"); 232 | Scanner in = new Scanner(is); 233 | while (in.hasNext()) { 234 | System.out.println(in.next()); 235 | } 236 | } catch (FileNotFoundException e) { 237 | //e 保存异常信息 238 | throw new MyException("文件在哪里呢"); 239 | } 240 | } 241 | public void invokeReadFile() throws MyException{ 242 | try { 243 | readFile(); 244 | } catch (MyException e) { 245 | //e 保存异常信息 246 | throw new MyException("文件找不到"); 247 | } 248 | } 249 | public static void main(String[] args) { 250 | TestChainException t = new TestChainException(); 251 | try { 252 | t.invokeReadFile(); 253 | } catch (MyException e) { 254 | e.printStackTrace(); 255 | } 256 | } 257 | } 258 | //MyException 构造器 259 | public MyException(String message) { 260 | super(message); 261 | } 262 | 263 | ``` 264 | 运行结果如下,没有了Throwable cause,不好排查是什么异常了啦 265 | ![](https://user-gold-cdn.xitu.io/2020/6/14/172b156657087952?w=891&h=253&f=png&s=46752) 266 | 267 | **正例:** 268 | ``` 269 | 270 | public class TestChainException { 271 | public void readFile() throws MyException{ 272 | try { 273 | InputStream is = new FileInputStream("jay.txt"); 274 | Scanner in = new Scanner(is); 275 | while (in.hasNext()) { 276 | System.out.println(in.next()); 277 | } 278 | } catch (FileNotFoundException e) { 279 | //e 保存异常信息 280 | throw new MyException("文件在哪里呢", e); 281 | } 282 | } 283 | public void invokeReadFile() throws MyException{ 284 | try { 285 | readFile(); 286 | } catch (MyException e) { 287 | //e 保存异常信息 288 | throw new MyException("文件找不到", e); 289 | } 290 | } 291 | public static void main(String[] args) { 292 | TestChainException t = new TestChainException(); 293 | try { 294 | t.invokeReadFile(); 295 | } catch (MyException e) { 296 | e.printStackTrace(); 297 | } 298 | } 299 | } 300 | //MyException 构造器 301 | public MyException(String message, Throwable cause) { 302 | super(message, cause); 303 | } 304 | ``` 305 | 306 | ![](https://user-gold-cdn.xitu.io/2020/6/14/172b154f01b322e4?w=1030&h=469&f=png&s=558089) 307 | 308 | 309 | ### 九、运行时异常RuntimeException ,不应该通过catch 的方式来处理,而是先预检查,比如:NullPointerException处理 310 | **反例:** 311 | 312 | ``` 313 | try { 314 | obj.method() 315 | } catch (NullPointerException e) { 316 | ... 317 | } 318 | ``` 319 | **正例:** 320 | 321 | ``` 322 | if (obj != null){ 323 | ... 324 | } 325 | ``` 326 | 327 | ### 十、注意异常匹配的顺序,优先捕获具体的异常 328 | 注意异常的匹配顺序,因为只有第一个匹配到异常的catch块才会被执行。如果你希望看到,是NumberFormatException异常,就抛出NumberFormatException,如果是IllegalArgumentException就抛出IllegalArgumentException。 329 | 330 | **反例:** 331 | ``` 332 | try { 333 | doSomething("test exception"); 334 | } catch (IllegalArgumentException e) { 335 | log.error(e); 336 | } catch (NumberFormatException e) { 337 | log.error(e); 338 | } 339 | ``` 340 | 341 | **正例:** 342 | 343 | ``` 344 | try { 345 | doSomething("test exception"); 346 | } catch (NumberFormatException e) { 347 | log.error(e); 348 | } catch (IllegalArgumentException e) { 349 | log.error(e); 350 | } 351 | ``` 352 | 353 | 理由: 354 | - 因为NumberFormatException是IllegalArgumentException 的子类,反例中,不管是哪个异常,都会匹配到IllegalArgumentException,就不会再往下执行啦,因此不知道是否是NumberFormatException。所以需要优先捕获具体的异常,把NumberFormatException放前面~ 355 | 356 | ## 公众号 357 | ![](https://user-gold-cdn.xitu.io/2020/5/16/1721b50d00331393?w=900&h=500&f=png&s=389569) 358 | - 欢迎关注我个人公众号,交个朋友,一起学习哈~ 359 | - 如果答案整理有错,欢迎指出哈,感激不尽~ 360 | -------------------------------------------------------------------------------- /Java基础学习/Java程序员必备的基础流程图.md: -------------------------------------------------------------------------------- 1 | ## 前言: 2 | 整理了一些Java基础流程图/架构图,做一下笔记,大家一起学习。 3 | 4 | ### 1.spring的生命周期 5 | Spring作为当前Java最流行、最强大的轻量级容器框架,了解熟悉spring的生命周期非常有必要; 6 | ![](https://user-gold-cdn.xitu.io/2019/7/7/16bc9fe0f8b19240?w=680&h=800&f=jpeg&s=47867) 7 | 8 | - 首先容器启动后,对bean进行初始化 9 | - 按照bean的定义,注入属性 10 | - 检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给bean,如BeanNameAware等 11 | - 以上步骤,bean对象已正确构造,通过实现BeanPostProcessor接口,可以再进行一些自定义方法处理。 12 | 如:postProcessBeforeInitialzation。 13 | - BeanPostProcessor的前置处理完成后,可以实现postConstruct,afterPropertiesSet,init-method等方法, 14 | 增加我们自定义的逻辑, 15 | - 通过实现BeanPostProcessor接口,进行postProcessAfterInitialzation后置处理 16 | - 接着Bean准备好被使用啦。 17 | - 容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy()方法 18 | - 通过给destroy-method指定函数,就可以在bean销毁前执行指定的逻 19 | 20 | ### 2.TCP三次握手,四次挥手 21 | tcp的三次握手四次挥手是每个程序员都应该熟悉的。 22 | ![](https://user-gold-cdn.xitu.io/2019/7/7/16bca01c8bb28f93?w=499&h=800&f=jpeg&s=28059) 23 | #### 三次握手: 24 | - 第一次握手(SYN=1, seq=x),发送完毕后,客户端进入 SYN_SEND 状态 25 | - 第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1), 发送完毕后,服务器端进入 SYN_RCVD 状态。 26 | - 第三次握手(ACK=1,ACKnum=y+1),发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手,即可以开始数据传输。 27 | #### 四次挥手: 28 | - 第一次挥手(FIN=1,seq=a),发送完毕后,客户端进入 FIN_WAIT_1 状态 29 | - 第二次挥手(ACK=1,ACKnum=a+1),发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态 30 | - 第三次挥手(FIN=1,seq=b),发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK。 31 | - 第四次挥手(ACK=1,ACKnum=b+1),客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。 32 | 33 | 34 | ### 3.线程池执行流程图 35 | 线程池:一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务,这避免了在处理短时间任务时创建与销毁线程的代价。线程池执行流程是每个开发必备的。 36 | ![](https://user-gold-cdn.xitu.io/2019/7/7/16bca03a5a6fd78f?w=800&h=370&f=jpeg&s=20069) 37 | 38 | #### 执行流程 39 | - 提交一个任务,线程池里存活的核心线程数小于线程数corePoolSize时,线程池会创建一个核心线程去处理提交的任务。 40 | - 如果线程池核心线程数已满,即线程数已经等于corePoolSize,一个新提交的任务,会被放进任务队列workQueue排队等待执行。 41 | - 当线程池里面存活的线程数已经等于corePoolSize了,并且任务队列workQueue也满,判断线程数是否达到maximumPoolSize,即最大线程数是否已满,如果没到达,创建一个非核心线程执行提交的任务。 42 | - 如果当前的线程数达到了maximumPoolSize,还有新的任务过来的话,直接采用拒绝策略处理。 43 | 44 | #### JDK提供了四种拒绝策略处理类 45 | - AbortPolicy(抛出一个异常,默认的) 46 | - DiscardPolicy(直接丢弃任务) 47 | - DiscardOldestPolicy(丢弃队列里最老的任务,将当前这个任务继续提交给线程池) 48 | - CallerRunsPolicy(交给线程池调用所在的线程进行处理) 49 | 50 | ### 4.JVM内存结构 51 | JVM内存结构是Java程序员必须掌握的基础。 52 | ![](https://user-gold-cdn.xitu.io/2019/7/7/16bca04f7f24477b?w=800&h=494&f=jpeg&s=23269) 53 | 54 | #### 程序计数器(PC 寄存器) 55 | 程序计数器是一块较小的内存空间,可以看作当前线程所执行的字节码的行号指示器。在虚拟机的模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、异常处理、线程恢复等基础功能都需要依赖计数器完成。 56 | #### Java虚拟机栈 57 | - 与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期与线程相同 58 | - 每个方法被执行的时候都会创建一个"栈帧",用于存储局部变量表(包括参数)、操作数栈、动态链接、方法出口等信息。每个方法被调用到执行完的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。 59 | - 局部变量表存放各种基本数据类型boolean、byte、char、short等 60 | 61 | #### 本地方法栈 62 | 与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的java方法服务,而本地方法栈则是为Native方法服务。 63 | #### Java堆 64 | - GC堆是java虚拟机所管理的内存中最大的一块内存区域,也是被各个线程共享的内存区域,在JVM启动时创建。 65 | - 其大小通过-Xms(最小值)和-Xmx(最大值)参数设置,-Xms为JVM启动时申请的最小内存,-Xmx为JVM可申请的最大内存。 66 | - 由于现在收集器都是采用分代收集算法,堆被划分为新生代和老年代。新生代由S0和S1构成,可通过-Xmn参数来指定新生代的大小。 67 | - 所有对象实例以及数组都在堆上分配。 68 | - Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种符号引用,这部分内容将在类加载后放到方法区的运行时常量池中。 69 | 70 | #### 方法区 71 | - 也称”永久代” ,它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域。可以通过-XX:PermSize 和 -XX:MaxPermSize 参数限制方法区的大小。 72 | - 运行时常量池:是方法区的一部分,其中的主要内容来自于JVM对Class的加载。 73 | - Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种符号引用,这部分内容将在类加载后放到方法区的运行时常量池中。 74 | 75 | 76 | ### 5.Java内存模型 77 | ![](https://user-gold-cdn.xitu.io/2019/7/7/16bca1d8c6b1c853?w=823&h=759&f=png&s=74719) 78 | 79 | - Java的多线程之间是通过共享内存进行通信的,在通信过程中会存在一系列如可见性、原子性、顺序性等问题,而JMM就是围绕着多线程通信以及与其相关的一系列特性而建立的模型。JMM定义了一些语法集,这些语法集映射到Java语言中就是volatile、synchronized等关键字。有兴趣可以看看我的另外一篇笔记:https://www.jianshu.com/p/3c1691aed1a5 80 | - Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。 81 | 82 | ### 6.springMVC执行流程图 83 | 84 | ![](https://user-gold-cdn.xitu.io/2019/7/7/16bca1f3b627ab3a?w=800&h=369&f=jpeg&s=27477) 85 | 86 | - User向服务器发送request,前端控制Servelt DispatcherServlet捕获; 87 | - DispatcherServlet对请求URL进行解析,调用HandlerMapping获得该Handler配置的所有相关的对象,最后以HandlerExecutionChain对象的形式返回. 88 | - DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter. 89 | - 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller) 90 | - Handler执行完成后,返回一个ModelAndView对象到DispatcherServlet 91 | - 根据返回的ModelAndView,选择一个适合的ViewResolver 92 | - ViewResolver 结合Model和View,来渲染视图 93 | - 将渲染结果返回给客户端。 94 | 95 | 96 | ### 7.JDBC执行流程 97 | 98 | ![](https://user-gold-cdn.xitu.io/2019/7/7/16bca20c9187abb8?w=323&h=800&f=jpeg&s=20054) 99 | #### JDBC执行流程: 100 | - 连接数据源 101 | - 为数据库传递查询和更新指令 102 | - 处理数据库响应并返回的结果 103 | 104 | ### 8.spring cloud组件架构 105 | Spring Cloud是一个基于Spring Boot实现的云原生应用开发工具,它为基于JVM的云原生应用开发中涉及的配置管理、服务发现、熔断器、智能路由、微代理、控制总线、分布式会话和集群状态管理等操作提供了一种简单的开发方式。 106 | ![](https://user-gold-cdn.xitu.io/2019/7/7/16bca21d2034aa3c?w=1020&h=711&f=png&s=63801) 107 | 108 | - Eureka 负责服务的注册与发现。 109 | - Hystrix 负责监控服务之间的调用情况,起到熔断,降级作用。 110 | - Spring Cloud Config 提供了统一的配置中心服务。 111 | - 所有对外的请求和服务,我们都通过Zuul来进行转发,起到 API 网关的作用 112 | - 最后我们使用 Sleuth+Zipkin 将所有的请求数据记录下来,方便我们进行后续分析。 113 | - Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。 114 | 它是一个基于HTTP和TCP的客户端负载均衡器。 115 | - Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。 116 | 117 | 118 | ### 9.dubbo 调用 119 | Dubbo是一个分布式服务框架,致力于提供高性能和透明化的远程服务调用方案,这容易和负载均衡弄混,负载均衡是对外提供一个公共地址,请求过来时通过轮询、随机等,路由到不同server。 120 | ![](https://user-gold-cdn.xitu.io/2019/7/7/16bca23e7a51a07f?w=821&h=654&f=png&s=42087) 121 | - Provider: 暴露服务的服务提供方。 122 | - Consumer: 调用远程服务的服务消费方。 123 | - Registry: 服务注册与发现的注册中心。 124 | - Monitor: 统计服务的调用次调和调用时间的监控中心。 125 | - Container: 服务运行容器。 126 | 127 | ### 10.后续待更新... 128 | 129 | ### 个人公众号 130 | 131 | ![](https://user-gold-cdn.xitu.io/2019/7/28/16c381c89b127bbb?w=344&h=344&f=jpeg&s=8943) 132 | 133 | 欢迎大家关注,大家一起学习,一起讨论。 -------------------------------------------------------------------------------- /Java基础学习/README.MD: -------------------------------------------------------------------------------- 1 | ## Java基础篇(持续更新中) 2 | ​ 3 | 公众号:捡田螺的小男孩 4 | ​ 5 | - [一文读懂线程池的工作原理(故事白话文)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488078&idx=1&sn=0a7cef472002f6582fd2354fba83706a&chksm=cf21cd67f85644716263c3a80cead9b7bb36d9677f6f8b06d0602077ece70fcafa9d20c1cffb&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 6 | - [Synchronized解析——如果你愿意一层一层剥开我的心](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487942&idx=1&sn=bbc68d1b9da23bf6474378e310b1ef1b&chksm=cf21ceeff85647f9ad7a08226849fcba3f9481387d13b17a5787fb94027647de81c349f9e390&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 7 | - [异步编程利器:CompletableFuture详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490456&idx=1&sn=95836324db57673a4d7aea4fb233c0d2&chksm=cf21c4b1f8564da72dc7b39279362bcf965b1374540f3b339413d138599f7de59a5f977e3b0e&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 8 | - [Java日常开发的21个坑,你踩过几个?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488115&idx=1&sn=bdd4a4ca36bc7ea902106d058e8537fb&chksm=cf21cd5af856444cb36af600705615454b0aaa2b289b97ddb52d594556ac07a1915b73ecce19&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 9 | - [一份Java程序员的珍藏书单,请您注意查收](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488066&idx=1&sn=44b5a90be1b69d7938dbcf516d85f041&chksm=cf21cd6bf856447d869278386250f59a926881375df848e54f86a21682bdab50f9e09ca56fbd&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 10 | - [Java程序员必备基础:JDK 5-15都有哪些经典新特性](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488058&idx=1&sn=aab4d0dc9020cb62710086474ca90baf&chksm=cf21cd13f8564405040593daa45c62aec218e13f5ff42d679c59f768dd4fcc53ddcf34e0a454&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 11 | - [程序员必备基础:Git 命令全方位学习](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488013&idx=1&sn=7011a51a347e3da2cf8f8540b4d9a5d6&chksm=cf21cd24f8564432d74bc13551ebdeae71a71ea31e339c7a8f1f42f181078b5192475d598626&token=162724582&ang=zh_CN&scene=21#wechat_redirect) 12 | - [给你的Java程序拍个片子吧:jstack命令解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487990&idx=1&sn=b5b3c565392f39e5ac517696603b2ed9&chksm=cf21cedff85647c960407dce77fe04d08e51f8c7332310ccacd925be5567c187aa761dd1d1c8&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 13 | - [Java程序员必备基础结构图](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487985&idx=1&sn=ead28c6c8d81b98e59603b848d250b30&chksm=cf21ced8f85647ce336f19016c7ff1936b21c81066815c8f28b830098716111548edb9767b21&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 14 | - [Java程序员必备:序列化全方位解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487980&idx=1&sn=2a9ce519f87a1ffe1511022e6724208e&chksm=cf21cec5f85647d357c79860171fc1799ef3c44a2bdd0716e8437e31708a17d9000b4224bd36&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 15 | - [优化if-else代码的八种方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487970&idx=1&sn=c296bb03419adf93955c6d0f27e56b29&chksm=cf21cecbf85647dd0ef5160559bc0d524a4be004a28bc5d2770a43409e3b090123c0930cf047&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 16 | - [Java程序员必备基础:泛型解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487964&idx=1&sn=24d7228cc10afc98c52dbf35da61a7b9&chksm=cf21cef5f85647e3d2b3f1e126cdc46d9e889d2e30c09716e0aea016beee3ca6d4c321cf60ae&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 17 | - [Java程序员必备基础:内部类解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487963&idx=1&sn=a0b49cd49a3dd51b6736c9ffa0a5997a&chksm=cf21cef2f85647e49a4bdb43f27583f03fb9ec4719767512dc084edd05675599c3bec44251fb&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 18 | - [老是遇到乱码问题:它是如何产生的,又如何解决呢?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487962&idx=1&sn=7424e843c80b228283fc08d4d24cc4bb&chksm=cf21cef3f85647e5a9c92d280624ad2564e885561a8b64cbf6722459f7c13da7421765321aa9&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 19 | - [谈谈Java反射:从入门到实践,再到原理](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487959&idx=1&sn=778114d611f18b0f307a3f3ab6cd9117&chksm=cf21cefef85647e84b77c0e46620e91cf5ff079785b58a7dc66e5ed7419e21e0da9180699617&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 20 | - [Java程序员必备的一些流程图](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487946&idx=1&sn=57a86e1d2fe1a9ecf00594a0bb6baf5f&chksm=cf21cee3f85647f5cf2ba728cc0838923140130a18ad117e248cf9843460614fc855d556968a&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 21 | - [Java程序员必备:查看日志常用的linux命令](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487954&idx=1&sn=6c04ff4edfcfea52a82795bcb9ed8efd&chksm=cf21cefbf85647ed8df72a23307315be5d1b3d4974c128f111bfdaa84da37cf7b49ff65c1112&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 22 | - [ThreadLocal的八个关键知识点](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247500784&idx=1&sn=6519d0e092be4ed9d6f4da8d90deef2c&chksm=cf221cd9f85595cf9123043241e92a19ca9c212aa8527cfb2aeb9a2472c6bdab9045cf40f22f&token=349136600&lang=zh_CN#rd) -------------------------------------------------------------------------------- /Java基础学习/README.MD.bak: -------------------------------------------------------------------------------- 1 | ## Java基础篇(持续更新中) 2 | ​ 3 | 公众号:捡田螺的小男孩 4 | ​ 5 | - [一文读懂线程池的工作原理(故事白话文)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488078&idx=1&sn=0a7cef472002f6582fd2354fba83706a&chksm=cf21cd67f85644716263c3a80cead9b7bb36d9677f6f8b06d0602077ece70fcafa9d20c1cffb&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 6 | - [Synchronized解析——如果你愿意一层一层剥开我的心](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487942&idx=1&sn=bbc68d1b9da23bf6474378e310b1ef1b&chksm=cf21ceeff85647f9ad7a08226849fcba3f9481387d13b17a5787fb94027647de81c349f9e390&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 7 | - [异步编程利器:CompletableFuture详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490456&idx=1&sn=95836324db57673a4d7aea4fb233c0d2&chksm=cf21c4b1f8564da72dc7b39279362bcf965b1374540f3b339413d138599f7de59a5f977e3b0e&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 8 | - [Java日常开发的21个坑,你踩过几个?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488115&idx=1&sn=bdd4a4ca36bc7ea902106d058e8537fb&chksm=cf21cd5af856444cb36af600705615454b0aaa2b289b97ddb52d594556ac07a1915b73ecce19&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 9 | - [一份Java程序员的珍藏书单,请您注意查收](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488066&idx=1&sn=44b5a90be1b69d7938dbcf516d85f041&chksm=cf21cd6bf856447d869278386250f59a926881375df848e54f86a21682bdab50f9e09ca56fbd&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 10 | - [Java程序员必备基础:JDK 5-15都有哪些经典新特性](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488058&idx=1&sn=aab4d0dc9020cb62710086474ca90baf&chksm=cf21cd13f8564405040593daa45c62aec218e13f5ff42d679c59f768dd4fcc53ddcf34e0a454&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 11 | - [程序员必备基础:Git 命令全方位学习](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488013&idx=1&sn=7011a51a347e3da2cf8f8540b4d9a5d6&chksm=cf21cd24f8564432d74bc13551ebdeae71a71ea31e339c7a8f1f42f181078b5192475d598626&token=162724582&ang=zh_CN&scene=21#wechat_redirect) 12 | - [给你的Java程序拍个片子吧:jstack命令解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487990&idx=1&sn=b5b3c565392f39e5ac517696603b2ed9&chksm=cf21cedff85647c960407dce77fe04d08e51f8c7332310ccacd925be5567c187aa761dd1d1c8&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 13 | - [Java程序员必备基础结构图](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487985&idx=1&sn=ead28c6c8d81b98e59603b848d250b30&chksm=cf21ced8f85647ce336f19016c7ff1936b21c81066815c8f28b830098716111548edb9767b21&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 14 | - [Java程序员必备:序列化全方位解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487980&idx=1&sn=2a9ce519f87a1ffe1511022e6724208e&chksm=cf21cec5f85647d357c79860171fc1799ef3c44a2bdd0716e8437e31708a17d9000b4224bd36&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 15 | - [优化if-else代码的八种方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487970&idx=1&sn=c296bb03419adf93955c6d0f27e56b29&chksm=cf21cecbf85647dd0ef5160559bc0d524a4be004a28bc5d2770a43409e3b090123c0930cf047&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 16 | - [Java程序员必备基础:泛型解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487964&idx=1&sn=24d7228cc10afc98c52dbf35da61a7b9&chksm=cf21cef5f85647e3d2b3f1e126cdc46d9e889d2e30c09716e0aea016beee3ca6d4c321cf60ae&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 17 | - [Java程序员必备基础:内部类解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487963&idx=1&sn=a0b49cd49a3dd51b6736c9ffa0a5997a&chksm=cf21cef2f85647e49a4bdb43f27583f03fb9ec4719767512dc084edd05675599c3bec44251fb&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 18 | - [老是遇到乱码问题:它是如何产生的,又如何解决呢?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487962&idx=1&sn=7424e843c80b228283fc08d4d24cc4bb&chksm=cf21cef3f85647e5a9c92d280624ad2564e885561a8b64cbf6722459f7c13da7421765321aa9&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 19 | - [谈谈Java反射:从入门到实践,再到原理](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487959&idx=1&sn=778114d611f18b0f307a3f3ab6cd9117&chksm=cf21cefef85647e84b77c0e46620e91cf5ff079785b58a7dc66e5ed7419e21e0da9180699617&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 20 | - [Java程序员必备的一些流程图](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487946&idx=1&sn=57a86e1d2fe1a9ecf00594a0bb6baf5f&chksm=cf21cee3f85647f5cf2ba728cc0838923140130a18ad117e248cf9843460614fc855d556968a&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 21 | - [Java程序员必备:查看日志常用的linux命令](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487954&idx=1&sn=6c04ff4edfcfea52a82795bcb9ed8efd&chksm=cf21cefbf85647ed8df72a23307315be5d1b3d4974c128f111bfdaa84da37cf7b49ff65c1112&token=162724582&lang=zh_CN&scene=21#wechat_redirect) -------------------------------------------------------------------------------- /Java基础学习/if-else代码优化的八种方案.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 代码中如果if-else比较多,阅读起来比较困难,维护起来也比较困难,很容易出bug,接下来,本文将介绍优化if-else代码的八种方案。 3 | 4 | 5 | ![](https://user-gold-cdn.xitu.io/2020/3/7/170b325db76e6d7f?w=1202&h=594&f=png&s=91815) 6 | 7 | ### 优化方案一:提前return,去除不必要的else 8 | 如果if-else代码块包含return语句,可以考虑通过提前return,把多余else干掉,使代码更加优雅。 9 | 10 | **优化前:** 11 | 12 | ``` 13 | if(condition){ 14 | //doSomething 15 | }else{ 16 | return ; 17 | } 18 | ``` 19 | **优化后:** 20 | 21 | ``` 22 | if(!condition){ 23 | return ; 24 | } 25 | //doSomething 26 | ``` 27 | 28 | ### 优化方案二:使用条件三目运算符 29 | 使用条件三目运算符可以简化某些if-else,使代码更加简洁,更具有可读性。 30 | 31 | **优化前:** 32 | ``` 33 | int price ; 34 | if(condition){ 35 | price = 80; 36 | }else{ 37 | price = 100; 38 | } 39 | ``` 40 | 优化后: 41 | ``` 42 | int price = condition?80:100; 43 | ``` 44 | 45 | 46 | ### 优化方案三:使用枚举 47 | 在某些时候,使用枚举也可以优化if-else逻辑分支,按个人理解,它也可以看做一种**表驱动方法**。 48 | 49 | **优化前:** 50 | 51 | ``` 52 | String OrderStatusDes; 53 | if(orderStatus==0){ 54 | OrderStatusDes ="订单未支付"; 55 | }else if(OrderStatus==1){ 56 | OrderStatusDes ="订单已支付"; 57 | }else if(OrderStatus==2){ 58 | OrderStatusDes ="已发货"; 59 | } 60 | ... 61 | ``` 62 | **优化后:** 63 | 64 | 先定义一个枚举 65 | ``` 66 | : 67 | public enum OrderStatusEnum { 68 | UN_PAID(0,"订单未支付"),PAIDED(1,"订单已支付"),SENDED(2,"已发货"),; 69 | 70 | private int index; 71 | private String desc; 72 | 73 | public int getIndex() { 74 | return index; 75 | } 76 | 77 | public String getDesc() { 78 | return desc; 79 | } 80 | 81 | OrderStatusEnum(int index, String desc){ 82 | this.index = index; 83 | this.desc =desc; 84 | } 85 | 86 | OrderStatusEnum of(int orderStatus) { 87 | for (OrderStatusEnum temp : OrderStatusEnum.values()) { 88 | if (temp.getIndex() == orderStatus) { 89 | return temp; 90 | } 91 | } 92 | return null; 93 | } 94 | } 95 | 96 | 97 | ``` 98 | 有了枚举之后,以上if-else逻辑分支,可以优化为一行代码 99 | ``` 100 | String OrderStatusDes = OrderStatusEnum.0f(orderStatus).getDesc(); 101 | ``` 102 | ### 优化方案四:合并条件表达式 103 | 如果有一系列条件返回一样的结果,可以将它们合并为一个条件表达式,让逻辑更加清晰。 104 | 105 | **优化前** 106 | ``` 107 | double getVipDiscount() { 108 | if(age<18){ 109 | return 0.8; 110 | } 111 | if("深圳".equals(city)){ 112 | return 0.8; 113 | } 114 | if(isStudent){ 115 | return 0.8; 116 | } 117 | //do somethig 118 | } 119 | ``` 120 | **优化后** 121 | 122 | ``` 123 | double getVipDiscount(){ 124 | if(age<18|| "深圳".equals(city)||isStudent){ 125 | return 0.8; 126 | } 127 | //doSomthing 128 | } 129 | ``` 130 | ### 优化方案五:使用 Optional 131 | 有时候if-else比较多,是因为非空判断导致的,这时候你可以使用java8的Optional进行优化。 132 | 133 | **优化前:** 134 | ``` 135 | String str = "jay@huaxiao"; 136 | if (str != null) { 137 | System.out.println(str); 138 | } else { 139 | System.out.println("Null"); 140 | } 141 | ``` 142 | **优化后:** 143 | 144 | ``` 145 | Optional strOptional = Optional.of("jay@huaxiao"); 146 | strOptional.ifPresentOrElse(System.out::println, () -> System.out.println("Null")); 147 | ``` 148 | 149 | 150 | ### 优化方案六:表驱动法 151 | **表驱动法**,又称之为表驱动、表驱动方法。表驱动方法是一种使你可以在表中查找信息,而不必用很多的逻辑语句(if或Case)来把它们找出来的方法。以下的demo,把map抽象成表,在map中查找信息,而省去不必要的逻辑语句。 152 | 153 | **优化前:** 154 | 155 | ``` 156 | if (param.equals(value1)) { 157 | doAction1(someParams); 158 | } else if (param.equals(value2)) { 159 | doAction2(someParams); 160 | } else if (param.equals(value3)) { 161 | doAction3(someParams); 162 | } 163 | // ... 164 | ``` 165 | 166 | **优化后:** 167 | 168 | ``` 169 | Map action> actionMappings = new HashMap<>(); // 这里泛型 ? 是为方便演示,实际可替换为你需要的类型 170 | 171 | // 初始化 172 | actionMappings.put(value1, (someParams) -> { doAction1(someParams)}); 173 | actionMappings.put(value2, (someParams) -> { doAction2(someParams)}); 174 | actionMappings.put(value3, (someParams) -> { doAction3(someParams)}); 175 | 176 | // 省略多余逻辑语句 177 | actionMappings.get(param).apply(someParams); 178 | ``` 179 | 180 | ### 优化方案七:优化逻辑结构,让正常流程走主干 181 | 182 | **优化前:** 183 | ``` 184 | public double getAdjustedCapital(){ 185 | if(_capital <= 0.0 ){ 186 | return 0.0; 187 | } 188 | if(_intRate > 0 && _duration >0){ 189 | return (_income / _duration) *ADJ_FACTOR; 190 | } 191 | return 0.0; 192 | } 193 | ``` 194 | **优化后:** 195 | ``` 196 | public double getAdjustedCapital(){ 197 | if(_capital <= 0.0 ){ 198 | return 0.0; 199 | } 200 | if(_intRate <= 0 || _duration <= 0){ 201 | return 0.0; 202 | } 203 | 204 | return (_income / _duration) *ADJ_FACTOR; 205 | } 206 | ``` 207 | 将条件反转使异常情况先退出,让正常流程维持在主干流程,可以让代码结构更加清晰。 208 | 209 | ### 优化方案八:策略模式+工厂方法消除if else 210 | 211 | 假设需求为,根据不同勋章类型,处理相对应的勋章服务,优化前有以下代码: 212 | ``` 213 | String medalType = "guest"; 214 | if ("guest".equals(medalType)) { 215 | System.out.println("嘉宾勋章"); 216 | } else if ("vip".equals(medalType)) { 217 | System.out.println("会员勋章"); 218 | } else if ("guard".equals(medalType)) { 219 | System.out.println("展示守护勋章"); 220 | } 221 | ... 222 | ``` 223 | 224 | 首先,我们把每个条件逻辑代码块,抽象成一个公共的接口,可以得出以下代码: 225 | 226 | ``` 227 | //勋章接口 228 | public interface IMedalService { 229 | void showMedal(); 230 | } 231 | ``` 232 | 我们根据每个逻辑条件,定义相对应的策略实现类,可得以下代码: 233 | ``` 234 | //守护勋章策略实现类 235 | public class GuardMedalServiceImpl implements IMedalService { 236 | @Override 237 | public void showMedal() { 238 | System.out.println("展示守护勋章"); 239 | } 240 | } 241 | //嘉宾勋章策略实现类 242 | public class GuestMedalServiceImpl implements IMedalService { 243 | @Override 244 | public void showMedal() { 245 | System.out.println("嘉宾勋章"); 246 | } 247 | } 248 | //VIP勋章策略实现类 249 | public class VipMedalServiceImpl implements IMedalService { 250 | @Override 251 | public void showMedal() { 252 | System.out.println("会员勋章"); 253 | } 254 | } 255 | ``` 256 | 接下来,我们再定义策略工厂类,用来管理这些勋章实现策略类,如下: 257 | 258 | ``` 259 | //勋章服务工产类 260 | public class MedalServicesFactory { 261 | 262 | private static final Map map = new HashMap<>(); 263 | static { 264 | map.put("guard", new GuardMedalServiceImpl()); 265 | map.put("vip", new VipMedalServiceImpl()); 266 | map.put("guest", new GuestMedalServiceImpl()); 267 | } 268 | public static IMedalService getMedalService(String medalType) { 269 | return map.get(medalType); 270 | } 271 | } 272 | ``` 273 | 使用了策略+工厂模式之后,代码变得简洁多了,如下: 274 | ``` 275 | public class Test { 276 | public static void main(String[] args) { 277 | String medalType = "guest"; 278 | IMedalService medalService = MedalServicesFactory.getMedalService(medalType); 279 | medalService.showMedal(); 280 | } 281 | } 282 | ``` 283 | 284 | 285 | ## 参考与感谢 286 | - [6个实例详解如何把if-else代码重构成高质量代码](https://blog.csdn.net/qq_35440678/article/details/77939999) 287 | - [如何 “干掉” if...else](https://www.jianshu.com/p/1db0bba283f0) 288 | 289 | ## 个人公众号 290 | 291 | ![](https://user-gold-cdn.xitu.io/2019/7/28/16c381c89b127bbb?w=344&h=344&f=jpeg&s=8943) 292 | 293 | - 觉得写得好的小伙伴给个点赞+关注啦,谢谢~ 294 | - 同时非常期待小伙伴们能够关注我公众号,后面慢慢推出更好的干货~嘻嘻 295 | - github地址:https://github.com/whx123/JavaHome -------------------------------------------------------------------------------- /Java基础学习/触发类加载的六大时机.md: -------------------------------------------------------------------------------- 1 | 2 | ![](https://user-gold-cdn.xitu.io/2019/9/22/16d59927154281d2?w=500&h=334&f=jpeg&s=30619) 3 | ## 前言 4 | 什么情况下会触发类加载的进行呢?本文将结合代码demo谈谈几种情况,希望对大家有帮助。 5 | 6 | ## 类加载时机 7 | 什么情况需要开始类加载过程的第一阶段:加载?Java虚拟机规范中并没有进行强制约束,这点可以交给虚拟机的具体实现来自由把握。但是对于初始化阶段,虚拟机规范则严格规定了以下几种情况必须立即对类进行初始化,如果类没有进行过初始化,则需要先触发其初始化。 8 | 9 | 10 | ![](https://user-gold-cdn.xitu.io/2019/9/22/16d593cdb1dee573?w=815&h=650&f=png&s=73353) 11 | ## 创建类的实例 12 | 为了验证类加载,我们先配置一个JVM参数 13 | ``` 14 | -XX:+TraceClassLoading 监控类的加载 15 | ``` 16 | 在IDE配置如下: 17 | 18 | ![](https://user-gold-cdn.xitu.io/2019/9/22/16d58ccf239f17d5?w=1269&h=840&f=png&s=96736) 19 | 20 | demo代码: 21 | ``` 22 | public class ClassLoadInstance { 23 | 24 | static { 25 | System.out.println("ClassLoadInstance类初始化时就会被执行!"); 26 | } 27 | 28 | public ClassLoadInstance() { 29 | System.out.println("ClassLoadInstance构造函数!"); 30 | } 31 | } 32 | 33 | public class ClassLoadTest { 34 | 35 | public static void main(String[] args) { 36 | ClassLoadInstance instance = new ClassLoadInstance(); 37 | } 38 | } 39 | 40 | ``` 41 | 运行结果: 42 | 43 | ![](https://user-gold-cdn.xitu.io/2019/9/22/16d58d7a7735d914?w=1218&h=119&f=png&s=23340) 44 | 45 | 46 | **结论:** 47 | 48 | new ClassLoadInstance实例时,发现ClassLoadInstance被加载了,因此 new创建实例对象,会触发类加载进行。 49 | 50 | ## 访问类的静态变量 51 | demo代码: 52 | ``` 53 | public class ClassLoadStaticVariable { 54 | 55 | static { 56 | System.out.println("ClassLoadStaticVariable类初始化时就会被执行!"); 57 | } 58 | 59 | public static int i = 100; 60 | 61 | public ClassLoadStaticVariable() { 62 | System.out.println("ClassLoadStaticVariable构造函数!"); 63 | } 64 | 65 | } 66 | 67 | public class ClassLoadTest { 68 | 69 | public static void main(String[] args) { 70 | System.out.println(ClassLoadStaticVariable.i); 71 | } 72 | } 73 | ``` 74 | 运行结果: 75 | 76 | ![](https://user-gold-cdn.xitu.io/2019/9/22/16d58e23d0d91b82?w=1240&h=112&f=png&s=17651) 77 | 78 | **结论:** 79 | 80 | 访问类ClassLoadStaticVariable的静态变量i时,发现ClassLoadStaticVariable类被加载啦,因此访问类的静态变量会触发类加载。 81 | 82 | **注意:** 83 | 84 | 访问final修饰的静态变量时,不会触发类加载,因为在编译期已经将此常量放在常量池了。 85 | 86 | ## 访问类的静态方法 87 | demo代码: 88 | ``` 89 | public class ClassLoadStaticMethod { 90 | 91 | static { 92 | System.out.println("ClassLoadStaticMethod类初始化时就会被执行!"); 93 | } 94 | 95 | public static void method(){ 96 | System.out.println("静态方法被调用"); 97 | } 98 | 99 | public ClassLoadStaticMethod() { 100 | System.out.println("ClassLoadStaticMethod构造函数!"); 101 | } 102 | 103 | } 104 | 105 | public class ClassLoadTest { 106 | 107 | public static void main(String[] args) { 108 | ClassLoadStaticMethod.method(); 109 | } 110 | } 111 | ``` 112 | 运行结果: 113 | 114 | ![](https://user-gold-cdn.xitu.io/2019/9/22/16d58effc2119fa4?w=1299&h=109&f=png&s=21427) 115 | 116 | 结论: 117 | 118 | 访问类ClassLoadStaticMethod的静态方法method时,发现ClassLoadStaticMethod类被加载啦,因此访问类的静态方法会触发类加载。 119 | 120 | ## 反射 121 | demo代码: 122 | ``` 123 | package classload; 124 | 125 | public class ClassLoadStaticReflect { 126 | 127 | static { 128 | System.out.println("ClassLoadStaticReflect类初始化时就会被执行!"); 129 | } 130 | 131 | public static void method(){ 132 | System.out.println("静态方法被调用"); 133 | } 134 | 135 | public ClassLoadStaticReflect() { 136 | System.out.println("ClassLoadStaticReflect构造函数!"); 137 | } 138 | 139 | } 140 | 141 | public class ClassLoadTest { 142 | 143 | public static void main(String[] args) throws ClassNotFoundException { 144 | Class.forName("classload.ClassLoadStaticReflect"); 145 | } 146 | } 147 | ``` 148 | 149 | 运行结果: 150 | 151 | ![](https://user-gold-cdn.xitu.io/2019/9/22/16d5900d7dfcb244?w=1227&h=82&f=png&s=17698) 152 | 153 | **结论:** 154 | 155 | 反射得到类ClassLoadStaticReflect时,发现ClassLoadStaticReflect类被加载啦,因此反射会触发类加载。 156 | 157 | ## 当初始化一个类时,发现其父类还未初始化,则先触发父类的初始化 158 | demo代码: 159 | ``` 160 | //父类 161 | public class ClassLoadSuper { 162 | static { 163 | System.out.println("ClassLoadSuper类初始化时就会被执行!这是父类"); 164 | } 165 | 166 | public static int superNum = 100; 167 | 168 | public ClassLoadSuper() { 169 | System.out.println("父类ClassLoadSuper构造函数!"); 170 | } 171 | } 172 | //子类 173 | public class ClassLoadSub extends ClassLoadSuper { 174 | 175 | static { 176 | System.out.println("ClassLoadSub类初始化时就会被执行!这是子类"); 177 | } 178 | 179 | public static int subNum = 100; 180 | 181 | public ClassLoadSub() { 182 | System.out.println("子类ClassLoadSub构造函数!"); 183 | } 184 | 185 | } 186 | 187 | public class ClassLoadTest { 188 | 189 | public static void main(String[] args) throws ClassNotFoundException { 190 | ClassLoadSub classLoadSub = new ClassLoadSub(); 191 | } 192 | } 193 | ``` 194 | 195 | 运行结果: 196 | 197 | ![](https://user-gold-cdn.xitu.io/2019/9/22/16d5913a74736e7d?w=1259&h=209&f=png&s=45708) 198 | 看了运行结果,是不是发现,网上那道经典面试题(**讲讲类的实例化顺序**?)也很清晰啦。 199 | 先父类静态变量/静态代码块-> 再子类静态变量/静态代码块->父类构造器->子类构造器 200 | 201 | **结论:** 202 | 203 | 实例化子类ClassLoadSub的时候,发现父类ClassLoadSuper先被加载,因此当初始化一个类时,发现其父类还未初始化,则先触发父类的初始化 204 | 205 | ## 虚拟机启动时,定义了main()方法的那个类先初始化 206 | demo代码: 207 | ``` 208 | package classload; 209 | 210 | public class ClassLoadTest { 211 | 212 | public static void main(String[] args) { 213 | System.out.println(ClassLoadSub.subNum); 214 | } 215 | } 216 | ``` 217 | 218 | 运行结果: 219 | 220 | ![](https://user-gold-cdn.xitu.io/2019/9/22/16d5922bdec72ac7?w=1391&h=371&f=png&s=83213) 221 | 222 | **结论:** 223 | 224 | 虚拟机启动时,即使有ClassLoadSub,ClassLoadSuper,ClassLoadTest等类被加载, 但ClassLoadTest最先被加载,即定义了main()方法的那个类会先触发类加载。 225 | 226 | ## 练习与小结 227 | 触发类加载的六大时机,我们都分析完啦,是不是不做个题都觉得意犹未尽呢?接下来,我们来分析类加载一道经典面试题吧。 228 | 229 | ``` 230 | class SingleTon { 231 | private static SingleTon singleTon = new SingleTon(); 232 | public static int count1; 233 | public static int count2 = 0; 234 | 235 | private SingleTon() { 236 | count1++; 237 | count2++; 238 | } 239 | 240 | public static SingleTon getInstance() { 241 | return singleTon; 242 | } 243 | } 244 | 245 | public class ClassLoadTest { 246 | public static void main(String[] args) { 247 | SingleTon singleTon = SingleTon.getInstance(); 248 | System.out.println("count1=" + singleTon.count1); 249 | System.out.println("count2=" + singleTon.count2); 250 | } 251 | } 252 | 253 | ``` 254 | 255 | 运行结果: 256 | 257 | ![](https://user-gold-cdn.xitu.io/2019/9/22/16d592b67bfbed36?w=1332&h=268&f=png&s=50004) 258 | 259 | **分析:** 260 | 261 | 1. SingleTon.getInstance(),调用静态方法,触发SingleTon类加载。 262 | 2. SingleTon类加载初始化,按顺序初始化静态变量。 263 | 3. 先执行private static SingleTon singleTon = new SingleTon(); ,调用构造器后,count1,count2均为1; 264 | 4. 按顺序执行 public static int count1; 没有赋值,所以count1依旧为1; 265 | 5. 按顺序执行 public static int count2 = 0;所以count2变为0. 266 | 267 | ## 个人公众号 268 | 269 | ![](https://user-gold-cdn.xitu.io/2019/7/28/16c381c89b127bbb?w=344&h=344&f=jpeg&s=8943) 270 | 271 | 欢迎大家关注,大家一起学习,一起讨论。 272 | 273 | 274 | -------------------------------------------------------------------------------- /Java程序员需要看哪些书/README.MD: -------------------------------------------------------------------------------- 1 | ## Java程序员书单(持续更新中) 2 | ​ 3 | 公众号:捡田螺的小男孩 4 | ​ 5 | - [一份Java程序员的珍藏书单,请您注意查收](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488066&idx=1&sn=44b5a90be1b69d7938dbcf516d85f041&chksm=cf21cd6bf856447d869278386250f59a926881375df848e54f86a21682bdab50f9e09ca56fbd&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 6 | -------------------------------------------------------------------------------- /Java面试题集结号/Elasticsearch/Elasticsearch面试.md: -------------------------------------------------------------------------------- 1 | ### Elasticsearch 2 | 1. 详细描述一下Elasticsearch索引文档的过程。 3 | 2. 详细描述一下Elasticsearch搜索的过程。 4 | 3. Elasticsearch 的倒排索引是什么。 5 | 4. Elasticsearch是如何实现master选举的。 6 | 5. lucence内部结构是什么。 7 | 6. Lucene全文搜索的原理 8 | 7. 在并发情况下,Elasticsearch 如何保证读写一致呢? 9 | 8. 详细阐述一下 Elasticsearch 搜索的过程。 10 | 9. Elasticsearch 索引数据多了怎么办呢,如何调优,部署 11 | 10. Elasticsearch 对于大数据量(上亿量级)的聚合如何实现? -------------------------------------------------------------------------------- /Java面试题集结号/JVM 篇/28. 了解过JVM调优没,基本思路是什么,如何确定它们的大小.md: -------------------------------------------------------------------------------- 1 | 如果CPU使用率较高,GC频繁且GC时间长,可能就需要JVM调优了。 2 | 基本思路就是让每一次GC都回收尽可能多的对象,对于CMS来说,要合理设置年轻代和年老代的大小。 3 | 4 | 这是一个迭代的过程,可以先采用JVM的默认值,然后通过压测分析GC日志。 5 | 如果看年轻代的内存使用率处在高位,导致频繁的Minor GC,而频繁GC的效率又不高,说明对象没那么快能被回收,这时年轻代可以适当调大一点。 6 | 如果看年老代的内存使用率处在高位,导致频繁的Full GC,这样分两种情况:如果每次Full GC后年老代的内存占用率没有下来,可以怀疑是内存泄漏;如果Full GC后年老代的内存占用率下来了,说明不是内存泄漏,要考虑调大年老代。 7 | 对于G1收集器来说,可以适当调大Java堆,因为G1收集器采用了局部区域收集策略,单次垃圾收集的时间可控,可以管理较大的Java堆。 -------------------------------------------------------------------------------- /Java面试题集结号/JVM 篇/29. 淘宝热门商品信息在JVM哪个内存区域.md: -------------------------------------------------------------------------------- 1 | https://www.nowcoder.com/discuss/81596?type=0&order=0&pos=8&page=1 -------------------------------------------------------------------------------- /Java面试题集结号/JVM 篇/30. 字节码的编译过程.md: -------------------------------------------------------------------------------- 1 | ![](https://user-gold-cdn.xitu.io/2020/5/14/1720eda10954cb6b?w=1120&h=549&f=png&s=128315) -------------------------------------------------------------------------------- /Java面试题集结号/JVM 篇/31. Java需要开发人员回收内存垃圾吗?.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/Java面试题集结号/JVM 篇/31. Java需要开发人员回收内存垃圾吗?.md -------------------------------------------------------------------------------- /Java面试题集结号/JVM 篇/32. Java中垃圾回收有什么目的?什么时候进行垃圾回收?.md: -------------------------------------------------------------------------------- 1 | 垃圾回收的目的是识别并且丢弃应用不再使用的对象来释放和重用资源。 2 | 3 | 触发主GC(Garbage Collector,垃圾回收)的条件: 4 | (1)当应用程序空闲时,即没有应用线程在运行时,GC会被调用。 5 | (2)Java堆内存不足时,GC会被调用。 6 | -------------------------------------------------------------------------------- /Java面试题集结号/JVM 篇/33. System.gc()和Runtime.gc()会做什么事情?.md: -------------------------------------------------------------------------------- 1 | 这两个方法用来提示JVM要进行垃圾回收。但是,立即开始还是延迟进行垃圾回收是取决于JVM的。 -------------------------------------------------------------------------------- /Java面试题集结号/JVM 篇/JVM面试题.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/Java面试题集结号/JVM 篇/JVM面试题.md -------------------------------------------------------------------------------- /Java面试题集结号/Java 并发 && 多线程/26. 线程的生命周期,线程的几种状态。.md: -------------------------------------------------------------------------------- 1 | ![](https://user-gold-cdn.xitu.io/2020/5/14/172108007fefffe5?w=1280&h=617&f=png&s=254427) 2 | 3 | -------------------------------------------------------------------------------- /Java面试题集结号/Java 并发 && 多线程/40. happen-before原则.md: -------------------------------------------------------------------------------- 1 | 如果前一个操作的执行结果必须对后一个操作可见,那就不允许这两个操作进行重排序,且happen-befor具有传递性 -------------------------------------------------------------------------------- /Java面试题集结号/Java 并发 && 多线程/41. 公平锁非公平锁.md: -------------------------------------------------------------------------------- 1 | 这个是在ReentrankLock中实现的,synchronized没有,是用一个队列实现的,在公平锁好理解,就是先进这个队列的,也先出队列获得资源,而非公平锁的话,则是还没有进队列之前可以与队列中的线程竞争尝试获得锁,如果获取失败,则进队列,此时也是要乖乖等前面出队才行 -------------------------------------------------------------------------------- /Java面试题集结号/Java 并发 && 多线程/42.可重入锁.md: -------------------------------------------------------------------------------- 1 | 如果一个线程获得过该锁,可以再次获得,主要是用途就是在递归方面,还有就是防止死锁,比如在一个同步方法块中调用了另一个相同锁对象的同步方法块 -------------------------------------------------------------------------------- /Java面试题集结号/Java 并发 && 多线程/43.独享锁共享锁.md: -------------------------------------------------------------------------------- 1 | 共享锁可以由多个线程获取使用,而独享锁只能由一个线程获取。 2 | 对ReentrantReadWriteLock其读锁是共享锁,其写锁是独占锁 3 | 读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的。其中获得写锁的线程还能同时获得读锁,然后通过释放写锁来降级。读锁则不能升级 -------------------------------------------------------------------------------- /Java面试题集结号/Java 并发 && 多线程/44. 偏向锁轻量级锁重量级锁.md: -------------------------------------------------------------------------------- 1 | - 在jdk1.6中做了第synchronized的优化,偏向锁指的是当前只有这个线程获得,没有发生争抢,此时将方法头的markword设置成0,然后每次过来都cas一下就好,不用重复的获取锁 2 | - 轻量级锁:在偏向锁的基础上,有线程来争抢,此时膨胀为轻量级锁,多个线程获取锁时用cas自旋获取,而不是阻塞状态 3 | - 重量级锁:轻量级锁自旋一定次数后,膨胀为重量级锁,其他线程阻塞,当获取锁线程释放锁后唤醒其他线程。(线程阻塞和唤醒比上下文切换的时间影响大的多,涉及到用户态和内核态的切换) 4 | - 自旋锁:在没有获取锁的时候,不挂起而是不断轮询锁的状态 -------------------------------------------------------------------------------- /Java面试题集结号/Java 并发 && 多线程/45. 如何保证内存可见性.md: -------------------------------------------------------------------------------- 1 | - volatile 通过内存屏障 2 | - synchronized 通过修饰的程序段同一时间只能由同一线程运行,释放锁前会刷新到主内存 -------------------------------------------------------------------------------- /Java面试题集结号/Java 并发 && 多线程/9. 如何检测死锁?怎么预防死锁?死锁四个必要条件.md: -------------------------------------------------------------------------------- 1 | - 1、互斥条件:一个资源每次只能被一个进程使用。 2 | - 2、保持和请求条件:一个进程因请求资源而阻塞时,对已获得资源保持不放。 3 | - 3、不可剥夺性:进程已获得资源,在未使用完成前,不能被剥夺。 4 | - 4、循环等待条件(闭环):若干进程之间形成一种头尾相接的循环等待资源关系。 5 | 6 | 如何避免死锁?只要破坏其中任意一个条件,就可以避免死锁 7 | 8 | 一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。 -------------------------------------------------------------------------------- /Java面试题集结号/Java 并发 && 多线程/Java 并发 && 多线程.md: -------------------------------------------------------------------------------- 1 | ### Java 并发 && 多线程 2 | 1. synchronized 的实现原理以及锁优化? 3 | 2. ThreadLocal原理,使用注意点,应用场景有哪些? 4 | 3. synchronized和ReentrantLock的区别? 5 | 4. 说说CountDownLatch与CyclicBarrier 区别 6 | 5. Fork/Join框架的理解 7 | 6. 为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法? 8 | 7. Java中的volatile关键是什么作用?怎样使用它?在Java中它跟synchronized方法有什么不同?volatile 的实现原理 9 | 8. CAS?CAS 有什么缺陷,如何解决? 10 | 9. 如何检测死锁?怎么预防死锁?死锁四个必要条件 11 | 10. 如果线程过多,会怎样? 12 | 11. 说说 Semaphore原理? 13 | 12. AQS组件,实现原理 14 | 13. 假设有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行? 15 | 14. LockSupport作用是? 16 | 15. Condition接口及其实现原理 17 | 16. 说说并发与并行的区别? 18 | 17. 为什么要用线程池?Java的线程池内部机制,参数作用,几种工作阻塞队列,线程池类型以及使用场景 19 | 18. 如何保证多线程下 i++ 结果正确? 20 | 19. 10 个线程和2个线程的同步代码,哪个更容易写? 21 | 20. 什么是多线程环境下的伪共享(false sharing)? 22 | 21. 线程池如何调优,最大数目如何确认? 23 | 22. Java 内存模型? 24 | 23. 怎么实现所有线程在等待某个事件的发生才会去执行? 25 | 24. 说一下 Runnable和 Callable有什么区别? 26 | 25. 用Java编程一个会导致死锁的程序,你将怎么解决? 27 | 26. 线程的生命周期,线程的几种状态。 28 | 27. ReentrantLock实现原理 29 | 28. java并发包concurrent及常用的类 30 | 29. wait(),notify()和suspend(),resume()之间的区别 31 | 30. FutureTask是什么? 32 | 31. 一个线程如果出现了运行时异常会怎么样 33 | 32. 生产者消费者模型的作用是什么 34 | 33. ReadWriteLock是什么 35 | 34. Java中用到的线程调度算法是什么? 36 | 35. 线程池中的阻塞队列如果满了怎么办? 37 | 36. 线程池中 submit()和 execute()方法有什么区别? 38 | 37. 介绍一下 AtomicInteger 类的原理? 39 | 38. 多线程锁的升级原理是什么? 40 | 39. 指令重排序,内存栅栏等? 41 | 40. Java 内存模型 happens-before原则 42 | 41. 公平锁/非公平锁 43 | 42. 可重入锁 44 | 43. 独享锁、共享锁 45 | 44. 偏向锁/轻量级锁/重量级锁 46 | 45. 如何保证内存可见性 47 | 46. 非核心线程延迟死亡,如何实现? 48 | 47. ConcurrentHashMap读操作为什么不需要加锁? 49 | 48. ThreadLocal 如何解决 Hash 冲突? 50 | 49. ThreadLocal 的内存泄露是怎么回事? 51 | 50. 为什么ThreadLocalMap 的 key是弱引用,设计理念是? 52 | 51. 同步方法和同步代码块的区别是什么? 53 | 52. 在Java中Lock接口比synchronized块的优势是什么?如果你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它? 54 | 53. 用Java实现阻塞队列。 55 | 54. 用Java写代码来解决生产者——消费者问题。 56 | 55. 什么是竞争条件?你怎样发现和解决竞争? 57 | 56. 为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法? 58 | 57. Java中你怎样唤醒一个阻塞的线程? 59 | 58. 什么是不可变对象,它对写并发应用有什么帮助? 60 | 59. 你在多线程环境中遇到的共同的问题是什么?你是怎么解决它的? 61 | 60. Java 中能创建 volatile数组吗 62 | 61. volatile 能使得一个非原子操作变成原子操作吗 63 | 62. 你是如何调用 wait()方法的?使用 if 块还是循环?为什么? 64 | 63. 我们能创建一个包含可变对象的不可变对象吗? 65 | 64. 在多线程环境下,SimpleDateFormat是线程安全的吗 66 | 65. 为什么Java中 wait 方法需要在 synchronized 的方法中调用? 67 | 66. BlockingQueue,CountDownLatch及Semeaphore的使用场景 68 | 67. Java中interrupted 和 isInterruptedd方法的区别? 69 | 68. 怎么检测一个线程是否持有对象监视器 70 | 69. 什么情况会导致线程阻塞 71 | 70. 如何在两个线程间共享数据 72 | 71. Thread.sleep(1000)的作用是什么? 73 | 72. 使用多线程可能带来什么问题 74 | 73. 说说线程的生命周期和状态? 75 | 74. 什么是上下文切换 76 | 75. Java Monitor 的工作机理 77 | 76. 按线程池内部机制,当提交新任务时,有哪些异常要考虑。 78 | 77. 线程池都有哪几种工作队列? 79 | 78. 说说几种常见的线程池及使用场景? 80 | 79. 使用无界队列的线程池会导致内存飙升吗? 81 | 80. 为什么阿里发布的 Java开发手册中强制线程池不允许使用 Executors 去创建? 82 | 81. Future有缺陷嘛? 83 | -------------------------------------------------------------------------------- /Java面试题集结号/README.MD: -------------------------------------------------------------------------------- 1 | ## 1. 面试真题 2 | 3 | 关注公众号:捡田螺的小男孩 4 | ​ 5 | - [oppo后端16连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498750&idx=1&sn=19fe8b4fff28fe81db14e733053bbc74&chksm=cf2224d7f855adc1d0984980a4e3de31fe33329164a472ca8d8255a8a80b69b2e23850811323&token=2001057130&lang=zh_CN#rd) 6 | - [小厂后端十连问(附答案)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498316&idx=1&sn=7749b78293b7b2af51eda99844e08a56&chksm=cf222565f855ac7324232e2af459f8b6e6eb5fd5b272c2b29bda08cc579421b6704a0de94b2e&token=2001057130&lang=zh_CN#rd) 7 | - [腾讯云后端15连问!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498171&idx=1&sn=f5a7ec25a569822be0f73fbcd413e8ba&chksm=cf222692f855af84fba419166fcd4235c0e78af3a2e1ec4c723a4efb1bd1ad6f8a5b9404c599&token=2001057130&lang=zh_CN#rd) 8 | - [社招后端21连问(三年工作经验一面)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498084&idx=1&sn=96c8148cfeeeb16668ed9e03fa9131cc&chksm=cf22264df855af5b6e81b93738cca28989226a53ec702fcfaa0cc5004dded4208c5ee5ea844a&token=2001057130&lang=zh_CN#rd) 9 | - [一份热乎乎的字节面试真题](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497742&idx=1&sn=18765e7356f446a7f2521f45b467d5d3&chksm=cf222727f855ae31dd2029e3219814211336c41d9228d271a583d3691ddadca586529aca9302&token=2001057130&lang=zh_CN#rd) 10 | - [面试必备:虾皮服务端15连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497256&idx=1&sn=3b799c2d514aa25e85a6faa60d639a0b&chksm=cf222901f855a017b73356b99b830b8800a7a9172fab891c5759d8dd69a270872ea9480c0b7c&token=2001057130&lang=zh_CN#rd) 11 | - [宇宙条一面:十道经典面试题解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495342&idx=1&sn=54e1c0c16a6467001524c34818025331&chksm=cf223187f855b89140db5ca429e6efc19d0111abf7f36b78a0ecd73b00fded1ff1e7ba32a6f1&token=2001057130&lang=zh_CN#rd) 12 | - [蚂蚁金服一面:十道经典面试题解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247493270&idx=1&sn=1c78a81d6e1bd0f0fd947fe8c3a33e32&chksm=cf2239bff855b0a9627855f20a17799e0506eb7548a409bfa0ee0450328d7519ec70f7b962cc&token=2001057130&lang=zh_CN#rd) 13 | ​ 14 | ## 2. 必考经典面试题 15 | ​ 16 | - [Redis主从、哨兵、 Cluster集群一锅端!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498254&idx=1&sn=6489360c3b226df9811e66cb40ec7656&chksm=cf222527f855ac3112628bcec7730064fee3fdbe869fbd0a7410c22766a0c036a7e5c1a69fa0&token=2001057130&lang=zh_CN#rd) 17 | - [我们为什么要分库分表?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498625&idx=1&sn=0d7bd9d1b46eeff4c715a6761355e9b0&chksm=cf2224a8f855adbea8931c8e011711f6c70cffeef8ddf8b87729c710eacef11b46eef80fda36&token=2001057130&lang=zh_CN#rd) 18 | - [面试必备:聊聊MySQL的主从](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497982&idx=1&sn=bb589329cceb5462fc41f66ec63dbf56&chksm=cf2227d7f855aec16dd4d3b3425c0401850eeaf2c9cdc82e82722d38a00c24ee9ccfa3353774&token=2001057130&lang=zh_CN#rd) 19 | - [消息队列经典十连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497847&idx=1&sn=29a32672b712e7dfadfa36c9902b2ec7&chksm=cf22275ef855ae484fb3f51a5726e9a4bc45222e8fbbd33631d177dc4b5619c36889ea178463&token=2001057130&lang=zh_CN#rd) 20 | - [八种幂等设计](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497427&idx=1&sn=2ed160c9917ad989eee1ac60d6122855&chksm=cf2229faf855a0ecf5eb34c7335acdf6420426490ee99fc2b602d54ff4ffcecfdab24eeab0a3&token=2001057130&lang=zh_CN#rd) 21 | - [看一遍就理解:零拷贝详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247496788&idx=1&sn=f65ddd10d16d8376efa0037762153932&chksm=cf222b7df855a26bad76249e7b77e28da3097b226f9165d79f5031516d9c345827fca901559c&token=2001057130&lang=zh_CN#rd) 22 | - [看一遍就理解:IO模型详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247496448&idx=1&sn=cd502f850290a25949dd4a11ac55a039&chksm=cf222c29f855a53f094bde2868900fa252b07385e73564e9ee9f0510cb4e74387d9d23ab67e6&token=2001057130&lang=zh_CN#rd) 23 | - [看一遍就理解:MVCC原理详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495277&idx=1&sn=a1812febb4246f824ce54d778f672025&chksm=cf223144f855b8528ad6cce707dc3a1b4d387817bd751dfab4f79dda90c6640f9763d25f3f33&token=2001057130&lang=zh_CN#rd) 24 | - [2W字!详解20道Redis经典面试题!(珍藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494124&idx=1&sn=c185f7d999d5f006608d05707a8a7eea&chksm=cf2236c5f855bfd329c6e2ee27f23f8131ebcd312960190a10f1a819d67f07a21a08ad17f263&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 25 | - [七种方案!探讨Redis分布式锁的正确使用姿势](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488142&idx=1&sn=79a304efae7a814b6f71bbbc53810c0c&chksm=cf21cda7f85644b11ff80323defb90193bc1780b45c1c6081f00da85d665fd9eb32cc934b5cf&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 26 | - [面试必备!TCP协议经典十五连问!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490868&idx=1&sn=96889bfe6a97f9200fa2d682cf2f5d89&chksm=cf21c21df8564b0b0757df584560a69340b1775fe1c70b867439565969ec3aed19c442ff4eeb&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 27 | - [2W字!梳理50道经典计算机网络面试题(收藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247492832&idx=1&sn=601fa1c340a313bc0f74bb75cdb6a95a&chksm=cf223bc9f855b2dfb8d0e74f3360e2edfe25c3a728fe17e9e80b6022340994fd9d9e1ca83ca8&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 28 | - [ZooKeeper的十二连问,你顶得了嘛?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488047&idx=1&sn=4913c7e1c3b8835f7512d8dc6b845727&chksm=cf21cd06f8564410cce6121230256facb1ab3b5a9ed35579896f428d84bdea7b86836109d575&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 29 | - [50道Java集合经典面试题(收藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488007&idx=1&sn=c5c16c8ec916c791e776216f3177c7e2&chksm=cf21cd2ef85644382a985e9fed1956d6ee60c86ce69e65f31f775318435fdb86bf368e26edf2&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 30 | - [100道MySQL数据库经典面试题解析(收藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488000&idx=1&sn=1c38db7fd110bbcc1ffb2d72a56aaf25&chksm=cf21cd29f856443f25a3fe98ae8e888faceef9bee45df045969b2cffb105363dcc2a4480bb74&token=1495321435&lang=zh_CN&scene=21#wechat_redirect) 31 | - [Spring 面试63问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497672&idx=1&sn=6ff0350e23d014b29a47bdec79af9ef5&chksm=cf2228e1f855a1f70fa78d9bd85c53dfbe154c1325aa1e203e4c918132c430d51bb68e961eda&token=2001057130&lang=zh_CN#rd) 32 | - [多线程50连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247501446&idx=1&sn=3d83f3c1035c963c1fda3f77ab750e71&chksm=cf2219aff85590b9ba054dc33956a5cafe1beaa77b231dc4dc0cf891be3e16ef367f6b2ac4ed&token=245109219&lang=zh_CN#rd) -------------------------------------------------------------------------------- /Java面试题集结号/README.MD.bak: -------------------------------------------------------------------------------- 1 | ## 1. 面试真题 2 | 3 | 关注公众号:捡田螺的小男孩 4 | ​ 5 | - [oppo后端16连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498750&idx=1&sn=19fe8b4fff28fe81db14e733053bbc74&chksm=cf2224d7f855adc1d0984980a4e3de31fe33329164a472ca8d8255a8a80b69b2e23850811323&token=2001057130&lang=zh_CN#rd) 6 | - [小厂后端十连问(附答案)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498316&idx=1&sn=7749b78293b7b2af51eda99844e08a56&chksm=cf222565f855ac7324232e2af459f8b6e6eb5fd5b272c2b29bda08cc579421b6704a0de94b2e&token=2001057130&lang=zh_CN#rd) 7 | - [腾讯云后端15连问!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498171&idx=1&sn=f5a7ec25a569822be0f73fbcd413e8ba&chksm=cf222692f855af84fba419166fcd4235c0e78af3a2e1ec4c723a4efb1bd1ad6f8a5b9404c599&token=2001057130&lang=zh_CN#rd) 8 | - [社招后端21连问(三年工作经验一面)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498084&idx=1&sn=96c8148cfeeeb16668ed9e03fa9131cc&chksm=cf22264df855af5b6e81b93738cca28989226a53ec702fcfaa0cc5004dded4208c5ee5ea844a&token=2001057130&lang=zh_CN#rd) 9 | - [一份热乎乎的字节面试真题](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497742&idx=1&sn=18765e7356f446a7f2521f45b467d5d3&chksm=cf222727f855ae31dd2029e3219814211336c41d9228d271a583d3691ddadca586529aca9302&token=2001057130&lang=zh_CN#rd) 10 | - [面试必备:虾皮服务端15连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497256&idx=1&sn=3b799c2d514aa25e85a6faa60d639a0b&chksm=cf222901f855a017b73356b99b830b8800a7a9172fab891c5759d8dd69a270872ea9480c0b7c&token=2001057130&lang=zh_CN#rd) 11 | - [宇宙条一面:十道经典面试题解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495342&idx=1&sn=54e1c0c16a6467001524c34818025331&chksm=cf223187f855b89140db5ca429e6efc19d0111abf7f36b78a0ecd73b00fded1ff1e7ba32a6f1&token=2001057130&lang=zh_CN#rd) 12 | - [蚂蚁金服一面:十道经典面试题解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247493270&idx=1&sn=1c78a81d6e1bd0f0fd947fe8c3a33e32&chksm=cf2239bff855b0a9627855f20a17799e0506eb7548a409bfa0ee0450328d7519ec70f7b962cc&token=2001057130&lang=zh_CN#rd) 13 | ​ 14 | ## 2. 必考经典面试题 15 | ​ 16 | - [Redis主从、哨兵、 Cluster集群一锅端!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498254&idx=1&sn=6489360c3b226df9811e66cb40ec7656&chksm=cf222527f855ac3112628bcec7730064fee3fdbe869fbd0a7410c22766a0c036a7e5c1a69fa0&token=2001057130&lang=zh_CN#rd) 17 | - [我们为什么要分库分表?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498625&idx=1&sn=0d7bd9d1b46eeff4c715a6761355e9b0&chksm=cf2224a8f855adbea8931c8e011711f6c70cffeef8ddf8b87729c710eacef11b46eef80fda36&token=2001057130&lang=zh_CN#rd) 18 | - [面试必备:聊聊MySQL的主从](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497982&idx=1&sn=bb589329cceb5462fc41f66ec63dbf56&chksm=cf2227d7f855aec16dd4d3b3425c0401850eeaf2c9cdc82e82722d38a00c24ee9ccfa3353774&token=2001057130&lang=zh_CN#rd) 19 | - [消息队列经典十连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497847&idx=1&sn=29a32672b712e7dfadfa36c9902b2ec7&chksm=cf22275ef855ae484fb3f51a5726e9a4bc45222e8fbbd33631d177dc4b5619c36889ea178463&token=2001057130&lang=zh_CN#rd) 20 | - [八种幂等设计](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497427&idx=1&sn=2ed160c9917ad989eee1ac60d6122855&chksm=cf2229faf855a0ecf5eb34c7335acdf6420426490ee99fc2b602d54ff4ffcecfdab24eeab0a3&token=2001057130&lang=zh_CN#rd) 21 | - [看一遍就理解:零拷贝详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247496788&idx=1&sn=f65ddd10d16d8376efa0037762153932&chksm=cf222b7df855a26bad76249e7b77e28da3097b226f9165d79f5031516d9c345827fca901559c&token=2001057130&lang=zh_CN#rd) 22 | - [看一遍就理解:IO模型详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247496448&idx=1&sn=cd502f850290a25949dd4a11ac55a039&chksm=cf222c29f855a53f094bde2868900fa252b07385e73564e9ee9f0510cb4e74387d9d23ab67e6&token=2001057130&lang=zh_CN#rd) 23 | - [看一遍就理解:MVCC原理详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495277&idx=1&sn=a1812febb4246f824ce54d778f672025&chksm=cf223144f855b8528ad6cce707dc3a1b4d387817bd751dfab4f79dda90c6640f9763d25f3f33&token=2001057130&lang=zh_CN#rd) 24 | - [2W字!详解20道Redis经典面试题!(珍藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494124&idx=1&sn=c185f7d999d5f006608d05707a8a7eea&chksm=cf2236c5f855bfd329c6e2ee27f23f8131ebcd312960190a10f1a819d67f07a21a08ad17f263&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 25 | - [七种方案!探讨Redis分布式锁的正确使用姿势](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488142&idx=1&sn=79a304efae7a814b6f71bbbc53810c0c&chksm=cf21cda7f85644b11ff80323defb90193bc1780b45c1c6081f00da85d665fd9eb32cc934b5cf&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 26 | - [面试必备!TCP协议经典十五连问!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490868&idx=1&sn=96889bfe6a97f9200fa2d682cf2f5d89&chksm=cf21c21df8564b0b0757df584560a69340b1775fe1c70b867439565969ec3aed19c442ff4eeb&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 27 | - [2W字!梳理50道经典计算机网络面试题(收藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247492832&idx=1&sn=601fa1c340a313bc0f74bb75cdb6a95a&chksm=cf223bc9f855b2dfb8d0e74f3360e2edfe25c3a728fe17e9e80b6022340994fd9d9e1ca83ca8&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 28 | - [ZooKeeper的十二连问,你顶得了嘛?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488047&idx=1&sn=4913c7e1c3b8835f7512d8dc6b845727&chksm=cf21cd06f8564410cce6121230256facb1ab3b5a9ed35579896f428d84bdea7b86836109d575&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 29 | - [50道Java集合经典面试题(收藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488007&idx=1&sn=c5c16c8ec916c791e776216f3177c7e2&chksm=cf21cd2ef85644382a985e9fed1956d6ee60c86ce69e65f31f775318435fdb86bf368e26edf2&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 30 | - [100道MySQL数据库经典面试题解析(收藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488000&idx=1&sn=1c38db7fd110bbcc1ffb2d72a56aaf25&chksm=cf21cd29f856443f25a3fe98ae8e888faceef9bee45df045969b2cffb105363dcc2a4480bb74&token=1495321435&lang=zh_CN&scene=21#wechat_redirect) 31 | - [Spring 面试63问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497672&idx=1&sn=6ff0350e23d014b29a47bdec79af9ef5&chksm=cf2228e1f855a1f70fa78d9bd85c53dfbe154c1325aa1e203e4c918132c430d51bb68e961eda&token=2001057130&lang=zh_CN#rd) -------------------------------------------------------------------------------- /Java面试题集结号/dubbo/dubbo面试题/dubbo面试题.md: -------------------------------------------------------------------------------- 1 | ### dubbo框架 2 | 1. Dubbo的服务请求失败怎么处理 3 | 2. dubbo的负载均衡有几种算法?(随机,轮询,最少活跃请求数,一致性hash) 4 | 3. Dubbo 和 Spring Cloud 有什么区别? 5 | 4. dubbo都支持什么协议,推荐用哪种? 6 | 5. 画一画服务注册与发现的流程图 7 | 6. Dubbo默认使用什么注册中心,还有别的选择吗? 8 | 7. 在 Provider 上可以配置的 Consumer 端的属性有哪些? 9 | 8. Dubbo启动时如果依赖的服务不可用会怎样? 10 | 9. Dubbo推荐使用什么序列化框架,你知道的还有哪些? 11 | 10. Dubbo默认使用的是什么通信框架,还有别的选择吗? 12 | 11. 服务上线怎么兼容旧版本? 13 | 12. Dubbo服务之间的调用是阻塞的吗? 14 | 13. Dubbo telnet 命令能做什么? 15 | 14. Dubbo如何一条链接并发多个调用。 16 | 15. Dubbo 的使用场景有哪些? 17 | 16. Dubbo 核心功能有哪些? 18 | 17. Dubbo 核心组件有哪些? 19 | 18. Dubbo 服务器注册于发现的流程? 20 | 19. Dubbo 支持哪些协议,它们的优缺点有哪些? 21 | 20. Dubbo 的注册中心集群挂掉,发布者和订阅者之间还能通信么? 22 | 21. Dubbo源码使用了哪些设计模式 23 | 22. Dubbo集群提供了哪些负载均衡策略? 24 | 23. Dubbo的集群容错方案有哪些? 25 | 24. Dubbo 支持哪些序列化方式? 26 | 25. Dubbo超时重试,Dubbo超时时间设置 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/Java基础面试题.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/Java面试题集结号/java 基础/Java基础面试题.md -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/1.equals和==的区别.md: -------------------------------------------------------------------------------- 1 | equals与 ==的区别? 2 | ![](httpsuser-gold-cdn.xitu.io2020510171ff375ad2f6e94w=720&h=404&f=png&s=237039) 3 | 4 | == 5 | - 基本类型:比较的是值是否相同;(byte,short,char,int,long,float,double,boolean等基本类型) 6 | - 引用类型:比较的是在内存中的存放地址是否相同; 7 | 8 | equals 9 | - 引用类型:默认情况下,比较的是内存地址值。 10 | - 只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。 11 | - String类中被复写的equals()方法其实是比较两个字符串的内容。 12 | 13 | 请解释字符串比较之中“==”和equals()的区别? 14 | - ==:比较的是两个字符串内存地址(堆内存)的数值是否相等,属于数值比较; 15 | - equals():比较的是两个字符串的内容,属于内容比较。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/10. String类能被继承吗,为什么.md: -------------------------------------------------------------------------------- 1 | String被关键字final修饰,所以不能被继承。 2 | 3 | String是JAVA用于处理字符串的类, 我们平常双引号包含的字符串例如"abc",其实就是一个String的实例。String是一个常量,一旦创建它的值就不能被修改, 是immutable只读类。 语法上定义为final的类, 就是不允许被有子类去修改String的值。 那为什么要定义String为不可变化的类呢? 4 | 5 | 1.字符串常量池的实现, 我们在代码中定义的"abc"等字符串很可能在常量池中存在, 每个String引用指向同一个字符串实现重用。如果String引用能修改字符串的值, 常量池设计无法实现。 6 | 7 | 2.安全和易用性的考虑, 在我们应用中,字符串是用得最多的类,我们经常需要在不同的类,方法间传递String, 甚至在多线程中操作String, 很多场景下是希望字符串能创建就不修改,安全简单的使用,所以String作为这个常见的一个字符串封装做了写保护,就好像C语言里面的字符串指针指向字符串常量,指针则是无法修改字符串的值的。 8 | 9 | 3.类似String的不可变类还有java.lang.Integer,Long,Double等wrapper class, 原因也是类似的。 10 | -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/101. 抽象类必须要有抽象方法吗?.md: -------------------------------------------------------------------------------- 1 | 不需要,抽象类不一定非要有抽象方法。 2 | 3 | 示例代码: 4 | 5 | ``` 6 | abstract class Dog { 7 | public static void sayHi() { 8 | System. out. println("hi~"); 9 | } 10 | } 11 | ``` 12 | 上面代码,抽象类并没有抽象方法但完全可以正常运行。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/11.说说Java中多态的实现原理.md: -------------------------------------------------------------------------------- 1 | [Java中多态以及原理](http://frobisher.me/2017/09/07/java-polymorphism-impl/) -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/110. 抽象类能使用 final 修饰.md: -------------------------------------------------------------------------------- 1 | 不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类, -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/111. 接口和抽象类有什么区别.md: -------------------------------------------------------------------------------- 1 | - 实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。 2 | - 构造函数:抽象类可以有构造函数;接口不能有。 3 | - 实现数量:类可以实现很多个接口;但是只能继承一个抽象类。 4 | - 访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/112. Files的常用方法都有哪些.md: -------------------------------------------------------------------------------- 1 | - Files. exists():检测文件路径是否存在。 2 | - Files. createFile():创建文件。 3 | - Files. createDirectory():创建文件夹。 4 | - Files. delete():删除一个文件或目录。 5 | - Files. copy():复制文件。 6 | - Files. move():移动文件。 7 | - Files. size():查看文件个数。 8 | - Files. read():读取文件。 9 | - Files. write():写入文件。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/113. 线程和进程的区别?.md: -------------------------------------------------------------------------------- 1 | 一个程序下至少有一个进程,一个进程下至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/117. 说说代理的实现原理.md: -------------------------------------------------------------------------------- 1 | - 1.创建一个接口 2 | - 2.创建一个实现了这个接口的实现类 3 | - 3.创建一个实现了这个接口的代理类,在代理类中实例化实现类,并且调用实现类中的方法 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/118. 了解哪设计模式,举例说说在jdk源码哪些用到了你说的设计模式.md: -------------------------------------------------------------------------------- 1 | - 单例:ioc容器 2 | - 模板:ioc、springmvc 3 | - 建造者模式:lombok 4 | - 工厂:ioc 5 | - 代理:aop 6 | - 订阅/发布:消息队列,redis的pub/sub -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/119. 什么是BS架构?什么是CS架构.md: -------------------------------------------------------------------------------- 1 | - B/S(Browser/Server),浏览器/服务器程序 2 | - C/S(Client/Server),客户端/服务端,桌面应用程序 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/12. Java泛型和类型擦除.md: -------------------------------------------------------------------------------- 1 | [Java程序员必备基础:泛型解析](https://juejin.im/post/5e1954c0f265da3e1e0568de) -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/120. Java有那些开发平台呢?.md: -------------------------------------------------------------------------------- 1 | - JAVA SE:主要用在客户端开发 2 | - JAVA EE:主要用在web应用程序开发 3 | - JAVA ME:主要用在嵌入式应用程序开发 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/121. Java内部类为什么可以访问到外部类呢?.md: -------------------------------------------------------------------------------- 1 | 因为内部类创建的时候,需要外部类的对象,在内部类对象创建的时候会把外部类的引用传递进去 2 | 3 | 可以看我这篇文章 [Java程序员必备基础:内部类解析](https://juejin.im/post/5e105e1ef265da5d61695a45) -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/122. Java支持的数据类型有哪些?什么是自动拆装箱呢?.md: -------------------------------------------------------------------------------- 1 | Java语言支持的8种基本数据类型是:byte,short,int,long,float,double,boolean,char 2 | 3 | 自动装箱是Java编译器在基本数据类型和对应的对象包装类型之间做的一个转化。比如:把int转化成Integer,double转化成Double,等等。反之就是自动拆箱。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/123. 创建线程有几种不同的方式.md: -------------------------------------------------------------------------------- 1 | - 继承Thread类 2 | - 实现Runnable接口 3 | - 使用Executor框架来创建线程池 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/124. hashCode()和equals()方法的重要性体现在什么地方?.md: -------------------------------------------------------------------------------- 1 | Java中的HashMap使用hashCode()和equals()方法来确定键值对的索引,当根据键获取值的时候也会用到这两个方法。如果没有正确的实现这两个方法,两个不同的键可能会有相同的hash值,因此,可能会被集合认为是相等的。而且,这两个方法也用来发现重复元素。所以这两个方法的实现对HashMap的精确性和正确性是至关重要的。 2 | -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/16. &和&&的区别.md: -------------------------------------------------------------------------------- 1 | &运算符有两种用法:(1)按位与;(2)逻辑与。 2 | &&运算符是短路与运算。 3 | 逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。 4 | -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/17. Java 中 IO 流分为几种.md: -------------------------------------------------------------------------------- 1 | 按功能来分:输入流(input)、输出流(output)。 2 | 3 | 按类型来分:字节流和字符流。 4 | 5 | 字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/2.final, finally, finalize.md: -------------------------------------------------------------------------------- 1 | - final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表 2 | 示该变量是一个常量不能被重新赋值。 3 | - finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块 4 | 中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。 5 | - finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调 6 | 用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的 7 | 最后判断。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/21. 守护线程是什么?用什么方法实现守护线程.md: -------------------------------------------------------------------------------- 1 | 守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。在 Java 中垃圾回收线程就是特殊的守护线程。 2 | 3 | -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/26. String s 与new String的区别.md: -------------------------------------------------------------------------------- 1 | 不一样,因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String("i") 则会被分到堆内存中。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/3.重写与重载的区别.md: -------------------------------------------------------------------------------- 1 | - 1.重写必须继承,重载不用。 2 | - 2.重写的方法名,参数数目相同zd,参数类型兼容,重载的方法名相同,参数列表不同回 3 | - 3.重写的方法修饰符大于等于父类的方法,重载和修饰符无关。 4 | - 4.重写不可以抛答出父类没有抛出的一般异常,可以抛出运行时异常 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/31. JDK 和 JRE 有什么区别?.md: -------------------------------------------------------------------------------- 1 | - JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。 2 | - JRE:Java Runtime Environment 的简称,Java 运行环境,为 Java 的运行提供了所需环境。 3 | 4 | 具体来说 JDK 其实包含了 JRE,同时还包含了编译 Java 源码的编译器 Javac,还包含了很多 Java 程序调试和分析的工具。简单来说:如果你需要运行 Java 程序,只需安装 JRE 就可以了,如果你需要编写 Java 程序,需要安装 JDK。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/32. String 类的常用方法都有那些.md: -------------------------------------------------------------------------------- 1 | 2 | - indexOf():返回指定字符的索引。 3 | - charAt():返回指定索引处的字符。 4 | - replace():字符串替换。 5 | - trim():去除字符串两端空白。 6 | - split():分割字符串,返回一个分割后的字符串数组。 7 | - getBytes():返回字符串的 byte 类型数组。 8 | - length():返回字符串长度。 9 | - toLowerCase():将字符串转成小写字母。 10 | - toUpperCase():将字符串转成大写字符。 11 | - substring():截取字符串。 12 | - equals():字符串比较。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/36. 什么是值传递和引用传递?.md: -------------------------------------------------------------------------------- 1 | - 值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量. 2 | - 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。 所以对引用对象进行操作会同时改变原对象. 3 | 4 | 一般认为,java内的传递都是值传递. 5 | 6 | -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/37. 是否可以在static环境中访问非static变量.md: -------------------------------------------------------------------------------- 1 | - static变量在Java中是属于类的,它在所有的实例中的值是一样的。当类被Java虚拟机载入的时候,会对static变量进行初始化。因为静态的成员属于类,随着类的加载而加载到静态方法区内存,当类加载时,此时不一定有实例创建,没有实例,就不可以访问非静态的成员。类的加载先于实例的创建,因此静态环境中,不可以访问非静态! -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/4.两个对象的 hashCode()相同,则 equals()是否也一定为 true.md: -------------------------------------------------------------------------------- 1 | - 两个对象equals相等,则它们的hashcode必须相等,反之则不一定。 2 | - 两个对象==相等,则其hashcode一定相等,反之不一定成立。 3 | 4 | **hashCode 的常规协定:** 5 | 6 | - 1.在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。 7 | - 2.两个对象的equals()相等,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。 8 | - 3.两个对象的equals()不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不要求一定生成不同的整数结果。但是,为不相等的对象生成不同整数结果可以提高哈希表的性能。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/49. 访问修饰符public,private,protected,以及不写(默认)时的区别?.md: -------------------------------------------------------------------------------- 1 | ![](https://user-gold-cdn.xitu.io/2020/5/15/17213fa3f944b0c4?w=1232&h=276&f=png&s=23829) -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/5.抽象类和接口有什么区别.md: -------------------------------------------------------------------------------- 1 | - 抽象类要被子类继承,接口要被类实现。 2 | - 接口只能做方法声明,抽象类中可以作方法声明,也可以做方法实现。 3 | - 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。 4 | - 接口是设计的结果,抽象类是重构的结果。 5 | - 抽象类和接口都是用来抽象具体对象的,但是接口的抽象级别最高。 6 | - 抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量。 7 | - 抽象类主要用来抽象类别,接口主要用来抽象功能。 8 | -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/50. final 在 java 中有什么作用?.md: -------------------------------------------------------------------------------- 1 | - final 修饰的类叫最终类,该类不能被继承。 2 | - final 修饰的方法不能被重写。 3 | - final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/52. String 属于基础的数据类型吗?.md: -------------------------------------------------------------------------------- 1 | String 不属于基础类型,基础类型有 8 种:byte、boolean、char、short、int、float、long、double,而 String 属于对象。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/53. 如何将字符串反转?.md: -------------------------------------------------------------------------------- 1 | 2 | 使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。 3 | 4 | ``` 5 | // StringBuffer reverse 6 | StringBuffer stringBuffer = new StringBuffer(); 7 | stringBuffer. append("abcdefg"); 8 | System. out. println(stringBuffer. reverse()); // gfedcba 9 | // StringBuilder reverse 10 | StringBuilder stringBuilder = new StringBuilder(); 11 | stringBuilder. append("abcdefg"); 12 | System. out. println(stringBuilder. reverse()); // gfedcba 13 | ``` -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/6.BIO、NIO、AIO 有什么区别.md: -------------------------------------------------------------------------------- 1 | BIO 2 | 3 | 就是传统的 java.io 包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序。它的有点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。 4 | 5 | NIO 6 | 7 | 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。 8 | 9 | AIO 10 | 11 | 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/7. String,Stringbuffer,StringBuilder的区别.md: -------------------------------------------------------------------------------- 1 | **String:** 2 | 3 | - String类是一个不可变的类,一旦创建就不可以修改。 4 | - String是final类,不能被继承 5 | - String实现了equals()方法和hashCode()方法 6 | 7 | **StringBuffer:** 8 | 9 | - 继承自AbstractStringBuilder,是可变类。 10 | - StringBuffer是线程安全的 11 | - 可以通过append方法动态构造数据。 12 | 13 | **StringBuilder:** 14 | 15 | - 继承自AbstractStringBuilder,是可变类。 16 | - StringBuilder是非线性安全的。 17 | - 执行效率比StringBuffer高。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/77. Java 中的 Math. round(-1. 5) 等于多少?.md: -------------------------------------------------------------------------------- 1 | 等于 -1,因为在数轴上取值时,中间值(0.5)向右取整,所以正 0.5 是往上取整,负 0.5 是直接舍弃。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/8.JAVA中的几种基本数据类型是什么,各自占用多少字节.md: -------------------------------------------------------------------------------- 1 | 2 | ![](https://user-gold-cdn.xitu.io/2020/5/11/17200f2a9c046c73?w=1228&h=433&f=png&s=226700) -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/9. Java 中,Comparator 与Comparable 有什么不同?.md: -------------------------------------------------------------------------------- 1 | 链接:https://www.nowcoder.com/questionTerminal/99f7d1f4f8374e419a6d6924d35d9530 2 | 来源:牛客网 3 | 4 | - Comparable & Comparator 都是用来实现集合中元素的比较、排序的,只是 Comparable 是在集合内部定义的方法实现的排序,Comparator 是在集合外部实现的排序,所以,如想实现排序,就需要在集合外定义 Comparator 接口的方法或在集合内实现 Comparable 接口的方法。 5 | - Comparator位于包java.util下,而Comparable位于包 java.lang下。 6 | - Comparable 是一个对象本身就已经支持自比较所需要实现的接口(如 String、Integer 自己就可以完成比较大小操作,已经实现了Comparable接口) 自定义的类要在加入list容器中后能够排序,可以实现Comparable接口,在用Collections类的sort方法排序时,如果不指定Comparator,那么就以自然顺序排序, 这里的自然顺序就是实现Comparable接口设定的排序方式。 7 | - 而 Comparator 是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。 可以说一个是自已完成比较,一个是外部程序实现比较的差别而已。 用 Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。 比如:你想对整数采用绝对值大小来排序,Integer 是不符合要求的,你不需要去修改 Integer 类(实际上你也不能这么做)去改变它的排序行为,只要使用一个实现了 Comparator 接口的对象来实现控制它的排序就行了。 -------------------------------------------------------------------------------- /Java面试题集结号/java 基础/基础题答案/Java锁有哪些种类,以及区别.md: -------------------------------------------------------------------------------- 1 | - 公平锁/非公平锁 2 | - 可重入锁 3 | - 独享锁/共享锁 4 | - 互斥锁/读写锁 5 | - 乐观锁/悲观锁 6 | - 分段锁 7 | - 偏向锁/轻量级锁/重量级锁 8 | - 自旋锁 -------------------------------------------------------------------------------- /Java面试题集结号/linux命/linux命令面试题.md: -------------------------------------------------------------------------------- 1 | ### 常用Linux 命令 2 | 1. 修改目录,文件权限的命令 3 | 2. 如何获取一个本地服务器上可用的端口。 4 | 3. 说说常见的linux命令,linux查看内存的命令是什么? 5 | 4. 查看系统磁盘空间剩余情况的命令 6 | 5. 如何获取java进程的pid 7 | 6. 如何获取某个进程的网络端口号; 8 | 7. 如何实时打印日志 9 | 8. 如何统计某个字符串行数; 10 | 9. 用一行命令查看文件的最后五行。 11 | 10. 用一行命令输出正在运行的java进程。 12 | 11. 绝对路径,当前目录、上层目录,切换目录分别用什么命令? 13 | 12. 怎么清屏?怎么退出当前命令? 14 | 13. 目录创建,创建文件,复制文件分别用什么命令? 15 | 14. 查看文件内容有哪些命令可以使用?tail?cat?less?more? 16 | 15. 怎么使一个命令在后台运行? 17 | 16. 终止进程用什么命令? 带什么参数? kill-9 pid有什么风险? 18 | 17. 搜索文件用什么命令? 格式是怎么样的? 19 | 18. 使用什么命令查看网络是否连通? 20 | 19. 使用什么命令查看 ip 地址及接口信息? 21 | 20. awk 详解 22 | -------------------------------------------------------------------------------- /Java面试题集结号/mybatis/mybatis面试题/mybatis面试题.md: -------------------------------------------------------------------------------- 1 | ### mybatis 2 | 1. mybatis 中 #{}和 ${}的区别是什么? 3 | 2. 什么是SQL注入 ,如何避免。 4 | 3. 说一下 mybatis 的一级缓存和二级缓存 5 | 4. mybatis 是否支持延迟加载?延迟加载的原理是什么? 6 | 5. mybatis 动态sql中使用标签与直接写where关键字有什么区别? 7 | 6. mybatis 动态sql标签中循环标签中有哪些属性,各自的作用。 8 | 7. mybatis 和 hibernate 的区别有哪些? 9 | 8. RowBounds是一次性查询全部结果吗?为什么? 10 | 9. MyBatis 定义的接口,怎么找到实现的? 11 | 10. Mybatis的底层实现原理。 12 | 11. Mybatis是如何进行分页的?分页插件的原理是什么? 13 | 12. Mybatis执行批量插入,能返回数据库主键列表吗? 14 | 13. Mybatis都有哪些Executor执行器?它们之间的区别是什么? 15 | 14. Mybatis动态sql有什么用?执行原理?有哪些动态sql? 16 | 15. mybatis有几种分页方式? 17 | 16. MyBatis框架的优点和缺点 18 | 17. 使用MyBatis框架,当实体类中的属性名和表中的字段名不一样 ,怎么办 ? 19 | 18. 通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗? 20 | 19. Xml映射文件中,除了常见的select|insert|updae|delete标签之外,还有哪些标签? 21 | 20. 简述Mybatis的插件运行原理,以及如何编写一个插件。 -------------------------------------------------------------------------------- /Java面试题集结号/netty/netty面试题.md: -------------------------------------------------------------------------------- 1 | ### Netty/tomcat 2 | 1. BIO、NIO和AIO区别 3 | 2. 说一下Netty 的各大组件 4 | 3. Netty 线程模型和 Reactor 模式 5 | 4. 什么是 Netty 的零拷贝? 6 | 5. NIO 的底层实现。 7 | 6. netty的心跳处理在弱网下怎么办 8 | 7. Netty 高性能表现在哪些方面? 9 | 8. Netty 和 Tomcat 有什么区别? 10 | 9. Netty 发送消息有几种方式? 11 | 10. 默认情况 Netty 起多少线程?何时启动? 12 | 11. Netty 支持哪些心跳类型设置? 13 | 12. Java 中怎么创建 ByteBuffer 14 | 13. Java 中的内存映射缓存区是什么? 15 | 14. 简单讲讲tomcat结构,以及其类加载器流程,线程模型等 16 | 15. tomcat如何调优,涉及哪些参数 17 | 16. IO多路复用机制 18 | 17. Netty 的应用场景有哪些? 19 | 18. 有几种I/O 网络模型? 20 | 19. 说说Netty的执行流程? 21 | 20. select、poll、epoll的机制及其区别? -------------------------------------------------------------------------------- /Java面试题集结号/nginx/nginx面试题.md: -------------------------------------------------------------------------------- 1 | ### nginx 2 | 1. Nginx的模块与工作原理是什么? 3 | 2. Nginx 是什么?有什么作用? 4 | 3. 说说Nginx的一些特性。 5 | 4. 请说一下Nginx如何处理HTTP请求。 6 | 5. 你知道,Nginx服务器上的Master和Worker进程分别是什么吗? 7 | 6. nginx常用命令,启动,重启,检查配置文件等 8 | 7. Nginx 和 Apache 比较,各有什么优缺点? 9 | 8. Nginx 多进程模型是如何实现高并发的? 10 | 9. 说说Nginx的反向代理和负载均衡 11 | 10. 请列举Nginx服务器的最佳用途。 -------------------------------------------------------------------------------- /Java面试题集结号/spring cloud/spring cloud面试题.md: -------------------------------------------------------------------------------- 1 | ### spring cloud 2 | 1. Eureka和Zookeeper区别 3 | 2. 什么是服务熔断?什么是服务降级? 4 | 3. 什么是Ribbon? 5 | 4. 什么是 Netflix Feign?它的优点是什么? 6 | 5. Ribbon和Feign的区别? 7 | 6. 什么是Spring Cloud Bus? 8 | 7. Spring Cloud Gateway? 9 | 8. 什么是SpringCloudConfig? 10 | 9. 什么是 Hystrix?它如何实现容错? 11 | 10. 什么是微服务?微服务优缺点 12 | 11. Sentinel,微服务哨兵,了解过吗 -------------------------------------------------------------------------------- /Java面试题集结号/spring 面试题/spring相关面试题.md: -------------------------------------------------------------------------------- 1 | ### Spring 相关 2 | 1. BeanFactory和 ApplicationContext有什么区别? 3 | 2. Spring IOC 的理解,其初始化过程 4 | 3. Spring Bean 的生命周期 5 | 4. Spring MVC 的工作原理? 6 | 5. Spring 循环注入的原理? 7 | 6. Spring 中用到了那些设计模式? 8 | 7. Spring AOP的理解,各个术语,他们是怎么相互工作的? 9 | 8. Spring框架中的单例bean是线程安全的吗? 10 | 9. Spring @ Resource和Autowired有什么区别? 11 | 10. Spring 的不同事务传播行为有哪些,有什么作用? 12 | 11. Spring Bean 的加载过程是怎样的? 13 | 12. 请举例说明@Qualifier注解 14 | 13. Spring 是如何管理事务的,事务管理机制? 15 | 14. 使用Spring框架的好处是什么? 16 | 15. Spring由哪些模块组成? 17 | 16. ApplicationContext通常的实现是什么? 18 | 17. 什么是Spring的依赖注入? 19 | 18. 你怎样定义类的作用域? 20 | 19. Spring框架中的单例bean是线程安全的吗? 21 | 20. 你可以在Spring中注入一个null 和一个空字符串吗? 22 | 21. 你能说下 Spring Boot 与 Spring 的区别吗 23 | 22. SpringBoot 的自动配置是怎么做的? 24 | 23. @RequestMapping 的作用是什么? 25 | 24. spring boot 有哪些方式可以实现热部署? 26 | 25. 说说Ioc容器的加载过程 27 | 26. 为什么 Spring 中的 bean 默认为单例? 28 | 27. 说说Spring中的@Configuration 29 | 27. FileSystemResource 和ClassPathResource 有何区别? 30 | 28. 什么是 Swagger?你用 Spring Boot 实现了它吗? 31 | 29. spring的controller是单例还是多例,怎么保证并发的安全。 32 | 30. 说一下Spring的核心模块 33 | 31. 如何向 Spring Bean 中注入一个 Java.util.Properties 34 | 32. 如何给Spring 容器提供配置元数据? 35 | 33. 如何在Spring中如何注入一个java集合,实现过吗? 36 | 34. 什么是基于Java的Spring注解配置? 举几个例子? 37 | 35. 怎样开启注解装配? 38 | 36. Spring支持哪些事务管理类型 39 | 37. 在Spring AOP 中,关注点和横切关注的区别是什么? 40 | 38. spring 中有多少种IOC 容器? 41 | 39. 描述一下 DispatcherServlet 的工作流程 42 | 40. 介绍一下 WebApplicationContext吧 43 | 41. Spring Boot 的配置文件有哪几种格式?它们有什么区别? 44 | 42. Spring Boot 需要独立的容器运行吗? 45 | 43. Spring Boot 自动配置原理是什么? 46 | 44. RequestMapping 和 GetMapping 的不同之处在哪里? 47 | 45. 如何使用Spring Boot实现异常处理? 48 | 46. Spring Boot 中如何解决跨域问题 ? 49 | 47. Spring Boot 如何实现热部署 ? 50 | 48. Spring Boot打成的 jar 和普通的jar有什么区别呢? 51 | 49. bootstrap.properties 和 application.properties 有何区别 ? 52 | 50. springboot启动机制 -------------------------------------------------------------------------------- /Java面试题集结号/zookeeper/zookeeper面试题.md: -------------------------------------------------------------------------------- 1 | ### ZooKeeper 2 | 1. Zookeeper的用途,选举的原理是什么。 3 | 2. Zookeeper watch机制原理。 4 | 3. zookeeper 怎么保证主从节点的状态同步? 5 | 4. 集群中有3台服务器,其中一个节点宕机,这个时候 zookeeper 还可以使用吗? 6 | 5. zookeeper都有哪些功能? 7 | 6. 什么是paxos算法,什么是zab协议。 8 | 7. zookeeper 是如何保证事务的顺序一致性的? 9 | 8. zookeeper 负载均衡和 nginx 负载均衡区别 10 | 9. Zookeeper 的典型应用场景 11 | 10. 说说四种类型的数据节点Znode 12 | 11. Zookeeper 的服务器角色(Leader,Follower,Observer) 13 | 12. Zookeeper 有哪几种几种部署模式? -------------------------------------------------------------------------------- /Java面试题集结号/分布式/分布式面试题.md: -------------------------------------------------------------------------------- 1 | ### 分布式 2 | 1. 说说分布式事务?分布式事务的解决方案 3 | 2. 什么是分布式系统? 4 | 3. 讲讲CAP理念。 5 | 4. 怎么理解强一致性、单调一致性和最终一致性? 6 | 5. 如何实现分布式锁? 7 | 6. 如何实现分布式 Session? 8 | 7. 负载均衡的理解? 9 | 8. 分布式集群下如何做到唯一序列号? 10 | 9. 分布式事务 11 | 10. 什么是一致性hash。 12 | -------------------------------------------------------------------------------- /Java面试题集结号/操作系统/12. 操作系统的页式存储.md: -------------------------------------------------------------------------------- 1 | 把内存分成大小相同的内存页,然后程序通过页表来查询到自己的存储位置,这样就可以使用不连续的内存来加载程序 2 | 事实上现在都用虚拟内存的方式,把程序分段加载到虚拟内存中,再把内存分页,通过段表、页表的形式来映射程序在内存中的位置 -------------------------------------------------------------------------------- /Java面试题集结号/操作系统/操作系统面试题.md: -------------------------------------------------------------------------------- 1 | ### 操作系统 2 | 1. Linux系统下你关注过哪些内核参数,说说你知道的。 3 | 2. epoll和poll有什么区别。 4 | 3. 线上CPU爆高,请问你如何找到问题所在。 5 | 4. Linux下IO模型有几种,各自的含义是什么。 6 | 5. top 命令之后有哪些内容,有什么作用。 7 | 6. 进程通信有几种方式? 8 | 7. 说说进程的调度算法 9 | 8. 常见的几种内存管理机制 10 | 9. 什么是虚拟内存(Virtual Memory)? 11 | 10. 内存置换算法 12 | 11. 虚拟地址、逻辑地址、线性地址、物理地址的区别。 13 | 12. 操作系统的页式存储 14 | 13. 进程和线程的区别 15 | 14. socket客户端和服务端通信过程 16 | 15. 影响调度程序的指标是什么? 17 | 16. 进程间状态有哪些? 18 | 17. 一个线程在内存中如何存储? 19 | 18. 僵尸进程是什么,如果产生一个僵尸进程,如何查找僵尸进程 20 | 19. 一个进程有20个线程,在某个线程中调用fork,新的进程会有20个线程吗? 21 | 20. 什么是 RR 调度算法? 22 | 21. 什么是 DMA(直接内存访问)? 23 | -------------------------------------------------------------------------------- /Java面试题集结号/数据库/数据库面试题.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/Java面试题集结号/数据库/数据库面试题.md -------------------------------------------------------------------------------- /Java面试题集结号/数据结构与算法/10. 你了解大O符号(big-O notation)么?你能给出不同数据结构的例子么?.md: -------------------------------------------------------------------------------- 1 | 大O符号表示一个程序运行时所需要的渐进时间复杂度上界。 2 | 3 | 其函数表示是: 4 | 对于函数f(n),g(n),如果存在一个常数c,使得** f(n) <= c * g(n),则 f(n) = O(g(n));** 5 | 6 | 大O描述当数据结构中的元素增加时,算法的规模和性能在最坏情景下有多好。 7 | 大O还可以描述其它行为,比如内存消耗。因为集合类实际上是数据结构,因此我们一般使用大O符号基于时间,内存,性能选择最好的实现。 8 | 大O符号可以对大量数据性能给予一个很好的说明。 9 | 10 | -------------------------------------------------------------------------------- /Java面试题集结号/数据结构与算法/数据结构与算法面试题.md: -------------------------------------------------------------------------------- 1 | ### 算法 2 | 1. 谈一谈一致性哈希算法。 3 | 2. 快排怎么实现 4 | 3. 手写二分查找 5 | 4. 如何判断一个单链表是否有环 6 | 5. 平衡二叉树的时间复杂度; 7 | 6. 反转单链表 8 | 7. 合并多个单有序链表 9 | 8. LRU 淘汰算法,用java自己实现一个LRU。 10 | 9. 跳表和平衡树区别 11 | 10. 你了解大O符号(big-O notation)么?你能给出不同数据结构的例子么? 12 | 11. 如何手撸一个队列? 13 | 12. 10亿个数字里里面找最小的10个。 14 | 13. 平衡二叉树的时间复杂度; 15 | 14. 有1亿个数字,其中有2个是重复的,快速找到它,时间和空间要最优。 16 | 15. 八大基本排序的时间,空间复杂度 17 | 16. 堆排序的原理 18 | 17. 树的几种遍历方式 19 | 18. 递归算法 20 | 19. 一个乱序数组,求第K大的数。排序方式使用字典序。 21 | 20. 一棵二叉树,求最大通路长度。 22 | 21. 万亿级别的两个URL文件A和B,如何求出A和B的差集C,(Bit映射->hash分组->多文件读写效率->磁盘寻址以及应用层面对寻址的优化) 23 | 22. 最快的排序算法是哪个?给阿里2万多名员工按年龄排序应该选择哪个算法?堆和树的区别;写出快排代码;链表逆序代码 24 | 23. LeetCode的经典算法题目,都要刷一遍以上哈~ -------------------------------------------------------------------------------- /Java面试题集结号/消息队列/消息队列面试题.md: -------------------------------------------------------------------------------- 1 | ### 消息队列 2 | 1. 消息队列有哪些使用场景。 3 | 2. 消息中间件如何解决消息丢失问题? 4 | 3. 谈谈消息的重发,补充策略。 5 | 4. 如何保证消息的顺序性。 6 | 5. 怎么利用mq实现最终一致性? 7 | 6. kafka 和其他消息队列的区别,kafka 主从同步怎么实现? 8 | 7. MQ的连接是线程安全的吗,你们公司的MQ服务架构怎样的。 9 | 8. kafka吞吐量高的原因。 10 | 9. rabbitmq如何实现集群高可用? 11 | 10. 使用kafka有没有遇到什么问题,怎么解决的? 12 | 11. MQ有可能发生重复消费,如何避免,如何做到幂等? 13 | 12. MQ的消息延迟了怎么处理,消息可以设置过期时间么,过期了你们一般怎么处理? 14 | 13. rabbitmq 有几种广播类型? 15 | 14. 使用 kafka 集群需要注意什么? 16 | 15. 为什么使用消息队列?有什么用? 17 | 16. 消息队列有什么优点和缺点? 18 | 17. Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么区别,以及适合哪些场景? 19 | 18. MQ能否保证消息必达,即消息的可靠性 20 | 19. 大量消息在MQ里长时间积压,该如何解决? 21 | 20. MQ消息过期失效怎么办? 22 | 21. kafka可以脱离zookeeper单独使用吗?为什么? 23 | 22. kafka 的分区策略有哪些? 24 | 23. kafka 有几种数据保留策略? 25 | 24. RabbitMQ 中的 broker 是指什么?cluster 又是指什么? 26 | 25. Kafka 消息是采用 Pull 模式,还是 Push 模式? 27 | 26. RabbitMQ 有哪些重要组件 28 | 27. 如何确保消息接收方消费了消息? 29 | 28. 消息基于什么传输? 30 | 29. 消息怎么路由? 31 | 30. 消息如何分发? -------------------------------------------------------------------------------- /Java面试题集结号/系统设计方案设计/10. 分布式session如何管理,你有哪些方案.md: -------------------------------------------------------------------------------- 1 | - Redis做缓存持久化存储session 2 | - 数据库存储session -------------------------------------------------------------------------------- /Java面试题集结号/系统设计方案设计/9. 如何设计存储海量数据的存储系统.md: -------------------------------------------------------------------------------- 1 | 海量数据的解决方案: 2 | 页面上:使用缓存;页面静态化技术; 3 | 数据库层面: 4 | 分离数据库中活跃的数据; 5 | 批量读取和延迟修改; 6 | 读写分离; 7 | 使用NoSQL和Hadoop等技术; 8 | 分布式部署数据库; 9 | 应用服务和数据服务分离; 10 | 其他方面: 11 | 使用搜索引擎搜索数据库中的数据; 12 | 进行业务的拆分; 13 | 高并发情况下的解决方案: 14 | 应用程序和静态资源文件进行分离,静态资源可以使用CDN; 15 | 集群与分布式; 16 | 使用Nginx反向代理; -------------------------------------------------------------------------------- /Java面试题集结号/系统设计方案设计/大厂系统设计方案面试题.md: -------------------------------------------------------------------------------- 1 | ### 系统设计/方案设计 2 | 1. 谈谈如何设计秒杀系统。 3 | 2. 一千万的用户实时排名如何实现; 4 | 3. 五万人并发抢票怎么实现 5 | 4. 手机扫二维码登录是怎么实现的? 6 | 5. Google是如何在一秒内把搜索结果返回给用户的。 7 | 6. 12306网站的订票系统如何实现,如何保证不会票不被超卖。 8 | 7. 如果有几十亿的白名单,每天白天需要高并发查询,晚上需要更新一次,如何设计这个功能。 9 | 8. 接口的幂等性如何设计 10 | 9. 如何设计存储海量数据的存储系统 11 | 10. 分布式session如何管理,你有哪些方案 12 | 11. 讲一下如何给高并发系统做限流? 13 | 12. 使用SpringBoot如何开发邮件发送系统? 14 | 13. 你如何设计一个能抗住大流量的系统,说说设计方案 15 | 14. 如何设计一个高并发的系统? 16 | 15. 数据量大的情况下分页查询很慢,有什么优化方案? 17 | 16. 设计一个秒杀系统,30分钟没付款就自动关闭交易。 18 | 17. 如何使用redis和zookeeper实现分布式锁?有什么区别优缺点,会有什么问题,分别适用什么 19 | 18. 如何设计一个安全的API接口。 20 | 19. 线上系统突然变得异常缓慢,你如何查找问题。 21 | 20. 设计一个社交网站中的“私信”功能,要求高并发、可扩展等等。 画一下架构图。 22 | 21. 后台系统怎么防止请求重复提交 23 | 22. 讲讲你理解的服务治理。 24 | 23. 执行某操作,前50次成功,第51次失败a全部回滚b前50次提交第51次抛异常,ab场景分别如何设置 25 | 24. 一个大的含有50M个URL的记录,一个小的含有500个URL的记录,找出两个记录里相同的URL 26 | 31. 海量日志数据,提取出某日访问百度次数最多的那个IP 27 | 32. 1000个线程同时运行,怎么防止不卡 28 | 33. 设计一个分布式自增id生成服务 -------------------------------------------------------------------------------- /Java面试题集结号/缓存,Redis/Redis为什么这么快.md: -------------------------------------------------------------------------------- 1 | 2 | ## 前言 3 | 4 | 大家好呀,我是捡田螺的小男孩。我们都知道Redis很快,它QPS可达10万(每秒请求数)。**Redis为什么这么快呢**,本文将跟大家一起学习。 5 | 6 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3880491879ed4a228d8f4213d987f6a3~tplv-k3u1fbpfcp-zoom-1.image) 7 | 8 | 9 | - 公众号:**捡田螺的小男孩** 10 | - [github地址](https://github.com/whx123/JavaHome),感谢每一颗star 11 | 12 | ## 基于内存实现 13 | 14 | 我们都知道内存读写是比磁盘读写快很多的。Redis是基于内存存储实现的数据库,相对于数据存在磁盘的数据库,就省去磁盘磁盘I/O的消耗。MySQL等磁盘数据库,需要建立索引来加快查询效率,而Redis数据存放在内存,直接操作内存,所以就很快。 15 | 16 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0d7be13173814a43a60960fe59a48c61~tplv-k3u1fbpfcp-zoom-1.image) 17 | 18 | ## 高效的数据结构 19 | 20 | 我们知道,MySQL索引为了提高效率,选择了B+树的数据结构。其实合理的数据结构,就是可以让你的应用/程序更快。先看下Redis的数据结构&内部编码图: 21 | 22 | 23 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4ddd7723cc0e4953b746b13db7a5cea3~tplv-k3u1fbpfcp-zoom-1.image) 24 | 25 | 26 | ### SDS简单动态字符串 27 | 28 | 29 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0e68a18c50f149dda136fc9b1aa73ab8~tplv-k3u1fbpfcp-zoom-1.image) 30 | 31 | ``` 32 | struct sdshdr { //SDS简单动态字符串 33 | int len; //记录buf中已使用的空间 34 | int free; // buf中空闲空间长度 35 | char buf[]; //存储的实际内容 36 | } 37 | ``` 38 | 39 | 40 | #### 字符串长度处理 41 | 42 | 在C语言中,要获取```捡田螺的小男孩```这个字符串的长度,需要从头开始遍历,复杂度为O(n); 43 | 在Redis中, 已经有一个**len**字段记录当前字符串的长度啦,直接获取即可,时间复杂度为O(1)。 44 | 45 | #### 减少内存重新分配的次数 46 | 47 | 在C语言中,修改一个字符串,需要重新分配内存,修改越频繁,内存分配就越频繁,而分配内存是会**消耗性能**的。而在Redis中,SDS提供了两种优化策略:空间预分配和惰性空间释放。 48 | 49 | **空间预分配** 50 | 51 | 当SDS简单动态字符串修改和空间扩充时,除了分配必需的内存空间,还会额外分配未使用的空间。分配规则是酱紫的: 52 | 53 | > - SDS修改后,len的长度小于1M,那么将额外分配与len相同长度的未使用空间。比如len=100,重新分配后,buf 的实际长度会变为100(已使用空间)+100(额外空间)+1(空字符)=201。 54 | > - SDS修改后, len长度大于1M,那么程序将分配1M的未使用空间。 55 | 56 | **惰性空间释放** 57 | 58 | 当SDS缩短时,不是回收多余的内存空间,而是用free记录下多余的空间。后续再有修改操作,直接使用free中的空间,减少内存分配。 59 | 60 | #### 哈希 61 | 62 | Redis 作为一个K-V的内存数据库,它使用用一张全局的哈希来保存所有的键值对。这张哈希表,有多个哈希桶组成,哈希桶中的entry元素保存了```*key```和```*value```指针,其中```*key```指向了实际的键,```*value```指向了实际的值。 63 | 64 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/13924387cd9e4a2d96d873dba1cc3ca9~tplv-k3u1fbpfcp-zoom-1.image) 65 | 66 | 哈希表查找速率很快的,有点类似于Java中的**HashMap**,它让我们在**O(1)** 的时间复杂度快速找到键值对。首先通过key计算哈希值,找到对应的哈希桶位置,然后定位到entry,在entry找到对应的数据。 67 | 68 | 有些小伙伴可能会有疑问:你往哈希表中写入大量数据时,不是会遇到**哈希冲突**问题嘛,那效率就会降下来啦。 69 | > **哈希冲突:** 通过不同的key,计算出一样的哈希值,导致落在同一个哈希桶中。 70 | 71 | Redis为了解决哈希冲突,采用了**链式哈希**。链式哈希是指同一个哈希桶中,多个元素用一个链表来保存,它们之间依次用指针连接。 72 | 73 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/131951cd80354c24b62584d71b8fe9f9~tplv-k3u1fbpfcp-zoom-1.image) 74 | 75 | 有些小伙伴可能还会有疑问:哈希冲突链上的元素只能通过指针逐一查找再操作。当往哈希表插入数据很多,冲突也会越多,冲突链表就会越长,那查询效率就会降低了。 76 | 77 | 为了保持高效,Redis 会对哈希表做**rehash操作**,也就是增加哈希桶,减少冲突。为了rehash更高效,Redis还默认使用了两个全局哈希表,一个用于当前使用,称为主哈希表,一个用于扩容,称为备用哈希表。 78 | 79 | #### 跳跃表 80 | 81 | 跳跃表是Redis特有的数据结构,它其实就是在**链表的基础上,增加多级索引**,以提高查找效率。跳跃表的简单原理图如下: 82 | 83 | ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0b62d59ffbd945e18f6dfcbf650a6eed~tplv-k3u1fbpfcp-watermark.image) 84 | 85 | - 每一层都有一条有序的链表,最底层的链表包含了所有的元素。 86 | - 跳跃表支持平均 O(logN),最坏 O(N)复杂度的节点查找,还可以通过顺序性操作批量处理节点。 87 | 88 | 89 | #### 压缩列表ziplist 90 | 91 | 压缩列表ziplist是列表键和字典键的的底层实现之一。它是由一系列特殊编码的内存块构成的列表, 一个ziplist可以包含多个entry, 每个entry可以保存一个长度受限的字符数组或者整数,如下: 92 | 93 | 94 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4ce3da7cddbe4e6e94a775151664ed93~tplv-k3u1fbpfcp-zoom-1.image) 95 | 96 | - zlbytes :记录整个压缩列表占用的内存字节数 97 | - zltail: 尾节点至起始节点的偏移量 98 | - zllen : 记录整个压缩列表包含的节点数量 99 | - entryX: 压缩列表包含的各个节点 100 | - zlend : 特殊值0xFF(十进制255),用于标记压缩列表末端 101 | 102 | 由于内存是**连续分配**的,所以遍历速度很快。。 103 | 104 | 105 | ## 合理的数据编码 106 | 107 | Redis支持多种数据基本类型,每种基本类型对应不同的数据结构,每种数据结构对应不一样的编码。为了提高性能,Redis设计者总结出,数据结构最适合的编码搭配。 108 | 109 | Redis是使用对象(redisObject)来表示数据库中的键值,当我们在 Redis 中创建一个键值对时,至少创建两个对象,一个对象是用做键值对的键对象,另一个是键值对的值对象。 110 | ``` 111 | //关注公众号:捡田螺的小男孩 112 | typedef struct redisObject{ 113 | //类型 114 | unsigned type:4; 115 | //编码 116 | unsigned encoding:4; 117 | //指向底层数据结构的指针 118 | void *ptr; 119 | //... 120 | }robj; 121 | ``` 122 | 123 | redisObject中,**type** 对应的是对象类型,包含String对象、List对象、Hash对象、Set对象、zset对象。**encoding** 对应的是编码。 124 | 125 | - String:如果存储数字的话,是用int类型的编码;如果存储非数字,小于等于39字节的字符串,是embstr;大于39个字节,则是raw编码。 126 | - List:如果列表的元素个数小于512个,列表每个元素的值都小于64字节(默认),使用ziplist编码,否则使用linkedlist编码 127 | - Hash:哈希类型元素个数小于512个,所有值小于64字节的话,使用ziplist编码,否则使用hashtable编码。 128 | - Set:如果集合中的元素都是整数且元素个数小于512个,使用intset编码,否则使用hashtable编码。 129 | - Zset:当有序集合的元素个数小于128个,每个元素的值小于64字节时,使用ziplist编码,否则使用skiplist(跳跃表)编码 130 | 131 | ## 合理的线程模型 132 | 133 | 134 | ### 单线程模型:避免了上下文切换 135 | 136 | Redis是单线程的,其实是指**Redis的网络IO和键值对读写**是由一个线程来完成的。但Redis的其他功能,比如持久化、异步删除、集群数据同步等等,实际是由额外的线程执行的。 137 | 138 | Redis的单线程模型,避免了**CPU不必要的上下文切换**和**竞争锁的消耗**。也正因为是单线程,如果某个命令执行过长(如hgetall命令),会造成阻塞。Redis是面向快速执行场景的内存数据库,所以要慎用如lrange和smembers、hgetall等命令。 139 | 140 | 什么是**上下文切换**?举个粟子: 141 | 142 | > - 比如你在看一本英文小说,你看到某一页,发现有个单词不会读,你加了个书签,然后去查字典。查完字典后,你回来从书签那里继续开始读,这个流程就很舒畅。 143 | > - 如果你一个人读这本书,肯定没啥问题。但是如果你去查字典的时候,别的小伙伴翻了一下你的书,然后溜了。你再回来看的时候,发现书不是你看的那一页了,你得花时间找到你的那一页。 144 | > - 一本书,你一个人怎么看怎么打标签都没事,但是人多了翻来翻去,这本书各种标记就很乱了。可能这个解释很粗糙,但是道理应该是一样的。 145 | 146 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/053215e73f844b8da8c2880205947fc1~tplv-k3u1fbpfcp-zoom-1.image) 147 | 148 | ### I/O 多路复用 149 | 150 | 什么是I/O多路复用? 151 | - I/O :网络 I/O 152 | - 多路 :多个网络连接 153 | - 复用:复用同一个线程。 154 | - IO多路复用其实就是一种同步IO模型,它实现了一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;而没有文件句柄就绪时,就会阻塞应用程序,交出cpu。 155 | 156 | 157 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/07838d0a48ef4b38acccb7b52e5435e1~tplv-k3u1fbpfcp-zoom-1.image) 158 | 159 | 160 | > 多路I/O复用技术可以让单个线程高效的处理多个连接请求,而Redis使用用epoll作为I/O多路复用技术的实现。并且Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间。 161 | 162 | ## 虚拟内存机制 163 | 164 | Redis直接自己构建了VM机制 ,不会像一般的系统会调用系统函数处理,会浪费一定的时间去移动和请求。 165 | 166 | **Redis的虚拟内存机制是啥呢?** 167 | > 虚拟内存机制就是暂时把不经常访问的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据(热数据)。通过VM功能可以实现冷热数据分离,使热数据仍在内存中、冷数据保存到磁盘。这样就可以避免因为内存不足而造成访问速度下降的问题。 168 | 169 | ### 参考与感谢 170 | 171 | - [Redis之VM机制](https://www.codenong.com/cs106843764/) 172 | - [一文揭秘单线程的Redis为什么这么快?](https://zhuanlan.zhihu.com/p/57089960) 173 | - [洞察|Redis是单线程的,但Redis为什么这么快?](https://zhuanlan.zhihu.com/p/42272979) 174 | 175 | 176 | -------------------------------------------------------------------------------- /Java面试题集结号/缓存,Redis/Redis缓存面试题.md: -------------------------------------------------------------------------------- 1 | ### 缓存/Redis 2 | 1. Redis用过哪些数据类型,每种数据类型的使用场景 3 | 2. Redis缓存穿透、缓存雪崩和缓存击穿原因,以及解决方案 4 | 3. 如何使用Redis来实现分布式锁,redis分布式锁有什么缺陷? 5 | 4. Redis 持久化机制,有几种方式,优缺点是什么,怎么实现的,RDB和AOF的区别 6 | 5. Redis集群,高可用,原理。 7 | 6. Redis的数据淘汰策略 8 | 7. 为什么要用redis?为什么要用缓存,在哪些场景使用缓存 9 | 8. redis事务,了解吗,了解Redis事务的CAS操作吗 10 | 9. 如何解决 Redis 的并发竞争Key问题。 11 | 10. Redis为什么是单线程的,为什么单线程还这么快? 12 | 11. 如何保证缓存与数据库双写时的数据一致性? 13 | 12. redis和memcached有什么区别 14 | 13. JVM本地缓存,了解过吗 15 | 14. redis的list结构相关的操作。 16 | 15. redis2和redis3的区别,redis3内部通讯机制。 17 | 16. Redis的选举算法和流程是怎样的? 18 | 17. Reids的主从复制机制原理。 19 | 18. Redis的线程模型是什么? 20 | 19. Redis的使用要注意什么,讲讲持久化方式,内存设置,集群的应用和优劣势,淘汰策略等。 21 | 20. Redis缓存分片 22 | 21. redis的集群怎么同步的数据的? 23 | 22. 请思考一个方案,设计一个可以控制缓存总体大小的自动适应的本地缓存。 24 | 23. redis的哨兵模式,一个key值如何在redis集群中找到存储在哪里。 25 | 24. Redis,一个字符串类型的值能存储最大容量是多少? 26 | 25. MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据? 27 | 26. Redis和Redisson有什么关系? 28 | 27. Redis中的管道有什么用? 29 | 28. Redis事务相关的命令有哪几个? 30 | 29. Redis key的过期时间和永久有效分别怎么设置? 31 | 30. Redis回收使用的是什么算法? 32 | 31. 一个Redis实例最多能存放多少的keys?List、Set、Sorted Set他们最多能存放多少元素? 33 | 32. Redis—跳跃表,复杂度是多少? 34 | 33. Redis有哪些优缺点?为什么要用 Redis ? 35 | 34. 为什么要用Redis 而不用 map/guava 做缓存? 36 | 35. 如何用 Redis 统计独立用户访问量? 37 | 36. 如何选择合适的持久化方式 38 | 37. Redis持久化数据和缓存怎么做扩容? 39 | 38. Redis key的过期时间和永久有效分别怎么设置? 40 | 39. 我们知道通过expire来设置key 的过期时间,那么对过期的数据怎么处理呢? 41 | 40. Redis的过期键的删除策略 42 | 41. Redis的内存用完了会发生什么? 43 | 42. Redis如何做内存优化? 44 | 43. Redis事务的三个阶段 45 | 44. Redis事务相关命令 46 | 45. Redis事务保证原子性吗,支持回滚吗? 47 | 46. Redis事务支持隔离性吗? 48 | 47. Redis集群的主从复制模型是怎样的? 49 | 48. 生产环境中的 redis 是怎么部署的? 50 | 49. 说说Redis哈希槽的概念 51 | 50. Redis集群会有写操作丢失吗?为什么? 52 | 51. Redis集群最大节点个数是多少? 53 | 52. Redis集群如何选择数据库? 54 | 53. Redis是单线程的,如何提高多核CPU的利用率? 55 | 54. 为什么要做Redis分区?有什么缺点? 56 | 55. 你知道有哪些Redis分区实现方案? 57 | 56. 缓存的实现原理,设计缓存要注意什么 58 | 57. 如何解决 Redis 的并发竞争 Key 问题 59 | 58. 分布式Redis是前期做还是后期规模上来了再做好?为什么? 60 | 59. 什么是 RedLock? 61 | 60. Redis支持的Java客户端都有哪些?官方推荐用哪个? 62 | 61. 为什么Redis的操作是原子性的,怎么保证原子性 63 | 62. Redis常见性能问题和解决方案? 64 | 63. 一个字符串类型的值能存储最大容量是多少? 65 | 64. Redis如何做大量数据插入? 66 | 65. 假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来? 67 | 66. 使用Redis做过异步队列吗,是如何实现的? 68 | 67. Redis如何实现延时队列? 69 | 68. Redis回收进程如何工作的? 70 | 69. 热点数据和冷数据是什么 71 | 70. 使用过Redis哪些命令? -------------------------------------------------------------------------------- /Java面试题集结号/计算机网络/29.你所知道网络协议有那些?.md: -------------------------------------------------------------------------------- 1 | - HTTP:超文本传输协议 2 | - FTP:文件传输协议 3 | - SMPT:简单邮件协议 4 | - TELNET:远程终端协议 5 | - POP3:邮件读取协议 -------------------------------------------------------------------------------- /Java面试题集结号/计算机网络/7. 当你用浏览器打开一个链接(如:httpwww.javastack.cn)到返回结果,发生了什么。.md: -------------------------------------------------------------------------------- 1 | - DNS解析 2 | - TCP连接 3 | - 发送HTTP请求 4 | - 服务器处理请求并返回HTTP报文 5 | - 浏览器解析渲染页面 6 | - 连接结束 -------------------------------------------------------------------------------- /Java面试题集结号/计算机网络/计算机网络面试.md: -------------------------------------------------------------------------------- 1 | 2 | ### 计算机网络 3 | 1. 请详细介绍一下TCP 的三次握手机制,为什么要三次握手? 4 | 2. 讲一下HTTP与HTTPS 的区别。 5 | 3. Session和cookie的区别。 6 | 4. TCP的四次挥手,为什么要有TIME_WAIT 状态,为什么需要四次握手 7 | 5. http1.0和http1.1有什么区别。 8 | 6. HTTP的常见状态码有哪些,代表什么含义?比如200, 302, 404? 9 | 7. 当你用浏览器打开一个链接到返回结果,发生了什么。 10 | 8. TCP/IP如何保证可靠性,说说TCP头的结构。 11 | 9. GET与POST方式的区别 12 | 10. 如何避免浏览器缓存。 13 | 11. TCP/IP模型? 14 | 12. 讲一讲 TCP 和 UDP 各有什么特点,两者有什么区别 15 | 13. 详细讲一下TCP的滑动窗口 16 | 14. 说一下拥塞控制 17 | 15. 如何理解HTTP协议的无状态性。 18 | 16. HTTP有哪些 method? 19 | 17. HTTP长连接和短连接 20 | 18. HTTPS原理,加签,验签,什么是数字签名?什么是数字证书?对称加密和非对称加密等。 21 | 19. 谈下你对 IP 地址分类的理解? 22 | 20. ARP及RARP协议的工作原理? 23 | 21. 怎么解决拆包和粘包? 24 | 22. DNS 的解析过程? 25 | 23. 什么是DoS、DDoS、DRDoS攻击?如何防御? 26 | 24. WebSocket与socket的区别 27 | 25. 讲一讲SYN超时,洪泛攻击,以及解决策略 28 | 26. ICMP协议的功能 29 | 27. 什么是 session,有哪些实现 session 的机制? 30 | 28. Http请求的过程与原理 31 | 29. 你知道网络协议有那些? 32 | 30. HTTPS 为什么是安全的?说一下他的底层实现原理? 33 | 31. ping的原理 34 | 32. 如果服务器出现了大量 CLOSE_WAIT 状态如何解决。 35 | 33. TCP 黏包是怎么产生的? 36 | 34. OSI七层体系结构路 37 | 35. 由器与交换机的区别 38 | 36. 什么是XSS攻击,如何避免 39 | 37. 什么是CSRF攻击,如何避免 40 | 38. Https双向和单向验证的区别 41 | 39. 如果客户端禁止Cookie能实现Session 42 | 40. HTTP请求中session实现原理 -------------------------------------------------------------------------------- /Mysql基础学习/README.md: -------------------------------------------------------------------------------- 1 | - [聊聊select for update到底加了什么锁](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247506728&idx=1&sn=5526ee3984e971d4c3b251c2ad76d658&chksm=c1e026a4f697afb28224d5ce0ecca7432879b357cd6433834c66d94c72a1935ba13e2e3e274e&token=337310304&lang=zh_CN#rd) 2 | - [数据库死锁排查思路分享](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247507770&idx=1&sn=b84b20aca057b34d511a501ff91941b5&chksm=c1e022b6f697aba05248128cb82f93aed341b1cc80e6d568c7150a4ffa6775692c7c9fa423a3&token=1822874069&lang=zh_CN#rd) 3 | -------------------------------------------------------------------------------- /Mysql基础学习/mysql数据库相关流程图原理图.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | 整理了一些Mysql数据库相关流程图/原理图,做一下笔记,大家一起学习。 4 | 5 | ## 1.mysql主从复制原理图 6 | 7 | mysql主从复制原理是大厂后端的高频面试题,了解mysql主从复制原理非常有必要。 8 | 9 | **主从复制原理,简言之,就三步曲,如下:** 10 | 11 | - 主数据库有个bin-log二进制文件,纪录了所有增删改Sql语句。(binlog线程) 12 | - 从数据库把主数据库的bin-log文件的sql语句复制过来。(io线程) 13 | - 从数据库的relay-log重做日志文件中再执行一次这些sql语句。(Sql执行线程) 14 | 15 | **如下图所示:** 16 | 17 | ![](https://user-gold-cdn.xitu.io/2019/8/1/16c4d9dd1b8235c3?w=1176&h=552&f=png&s=98700) 18 | 19 | 上图主从复制分了五个步骤进行: 20 | 21 | 步骤一:主库的更新事件(update、insert、delete)被写到binlog 22 | 23 | 步骤二:从库发起连接,连接到主库。 24 | 25 | 步骤三:此时主库创建一个binlog dump thread,把binlog的内容发送到从库。 26 | 27 | 步骤四:从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log 28 | 29 | 步骤五:还会创建一个SQL线程,从relay log里面读取内容,从Exec_Master_Log_Pos位置开始执行读取到的更新事件,将更新内容写入到slave的db 30 | 31 | ## 2.Mysql逻辑架构图 32 | 33 | 如果能在脑海中构建出MySql各组件之间如何协同工作的架构图,就会有助于深入理解MySql服务器 34 | ![](https://user-gold-cdn.xitu.io/2019/8/2/16c4f6e298211cd1?w=864&h=832&f=png&s=56601) 35 | 36 | Mysql逻辑架构图主要分三层: 37 | 38 | **1) 第一层负责连接处理,授权认证,安全等等** 39 | 40 | - 每个客户端连接都会在服务器进程中拥有一个线程,服务器维护了一个线程池,因此不需要为每一个新建的连接创建或者销毁线程。 41 | - 当客户端连接到Mysql服务器时,服务器对其进行认证,通过用户名和密码认证,也可以通过SSL证书进行认证。 42 | - 一旦客户端连接成功,服务器会继续验证客户端是否具有执行某个特定查询的权限。 43 | 44 | **2)第二层负责编译并优化SQL** 45 | 46 | - 这一层包括查询解析,分析,优化,缓存以及所有的的内置函数。 47 | - 对于SELECT语句,在解析查询前,服务器会先检查查询缓存,如果能在其中找到对应的查询结果,则无需再进行查询解析、优化等过程,直接返回查询结果。 48 | - 所有跨存储引擎的功能都在这一层实现:存储过程,触发器,视图。 49 | 50 | **3)第三层是存储引擎。** 51 | 52 | - 存储引擎负责在MySQL中存储数据、提取数据。 53 | - 存储引擎通过API与上层进行通信,这些API屏蔽了不同存储引擎之间的差异,使得这些差异对上层查询过程透明。 54 | - 存储引擎不会去解析SQL,不同存储引擎之间也不会相互通信,而只是简单地响应上层服务器的请求。 55 | 56 | 57 | ## 3.InnoDb 逻辑存储结构图 58 | 59 | 从InnoDb 存储引擎的逻辑存储结构看,所有数据都被逻辑地存放在一个空间中,称之为表空间(tablespace)。表空间又由段(segment),区(extent),页(page)组成。页在一些文档中有时候也称为块(block)。 InnoDb 逻辑存储结构图如下: 60 | ![](https://user-gold-cdn.xitu.io/2019/8/2/16c5246cdda2239a?w=1215&h=901&f=png&s=114803) 61 | 62 | **表空间(tablespace)** 63 | 64 | - 表空间是Innodb存储引擎逻辑的最高层,**所有的数据都存放在表空间中**。 65 | - 默认情况下,Innodb存储引擎有一个**共享表空间ibdata1**,即所有数据都存放在这个表空间中内。 66 | - 如果启用了**innodb_file_per_table**参数,需要注意的是每张表的表空间内存放的**只是数据、索引、和插入缓冲Bitmap**,其他类的数据,比如**回滚(undo)信息、插入缓冲检索页、系统事物信息,二次写缓冲等**还是放在原来的共享表内的。 67 | 68 | **段(segment)** 69 | 70 | - 表空间由段组成,常见的段有**数据段、索引段、回滚段**等。 71 | - InnoDB存储引擎表是索引组织的,因此数据即索引,索引即数据。**数据段即为B+树的叶子结点,索引段即为B+树的非索引结点**。 72 | - 在InnoDB存储引擎中对段的管理都是由**引擎自身所完成**,DBA不能也没必要对其进行控制。 73 | 74 | 75 | **区(extent)** 76 | 77 | - 区是由**连续页**组成的空间,在任何情况下每个区的大小都为**1MB**。 78 | - 为了**保证区中页的连续性**,InnoDB存储引擎**一次从磁盘申请4~5个区**。 79 | - 默认情况下,InnoDB存储引擎页的大小为**16KB**,一个区中一共**64个连续**的区。 80 | 81 | **页(page)** 82 | 83 | - **页是InnoDB磁盘管理的最小单位**。 84 | - 在InnoDB存储引擎中,默认每个页的大小为**16KB**。 85 | - 从InnoDB1.2.x版本开始,可以通过参数**innodb_page_size**将页的大小设置为4K,8K,16K。 86 | - InnoDB存储引擎中,常见的页类型有:**数据页,undo页,系统页,事务数据页,插入缓冲位图页,插入缓冲空闲列表页**等。 87 | 88 | 89 | ## 4.Innodb页结构相关示意图 90 | ### Innodb页结构单体图 91 | InnoDB数据页由以下7部分组成,如图所示: 92 | 93 | ![](https://user-gold-cdn.xitu.io/2019/8/2/16c50f27b898487c?w=834&h=707&f=png&s=66481) 94 | 95 | 其中**File Header、Page Header、File Trailer**的大小是固定的,分别为**38,56,8**字节,这些空间用来标记该页的一些信息,如**Checksum,数据页所在B+树索引的层数**等。**User Records、Free Space、Page Directory**这些部分为**实际的行记录**存储空间,因此大小是动态的。 96 | 97 | 下边我们用表格的方式来大致描述一下这7个部分: 98 | 99 | ![](https://user-gold-cdn.xitu.io/2019/8/2/16c518a90733a4a3?w=1078&h=802&f=png&s=141015) 100 | 101 | ### 记录在页中的存储流程图 102 | 每当我们插入一条记录,都会从Free Space部分,也就是尚未使用的存储空间中申请一个记录大小的空间划分到User Records部分,当Free Space部分的空间全部被User Records部分替代掉之后,也就意味着这个页使用完了,如果还有新的记录插入的话,就需要去申请新的页了,这个过程的图示如下: 103 | 104 | ![](https://user-gold-cdn.xitu.io/2019/8/2/16c52272973d2453?w=1916&h=717&f=png&s=124100) 105 | 106 | ### 不同Innodb页构成的数据结构图 107 | 一张表中可以有成千上万条记录,一个页只有16KB,所以可能需要好多页来存放数据。不同页其实构成了一条**双向链表**,File Header是InnoDB页的第一部分,它的**FIL_PAGE_PREV和FIL_PAGE_NEXT**就分别代表本页的上一个和下一个页的页号,即链表的上一个以及下一个节点指针。 108 | 109 | ![](https://user-gold-cdn.xitu.io/2019/8/2/16c523ccb2917853?w=1820&h=906&f=png&s=145672) 110 | 111 | ## 5.Innodb索引结构图 112 | 我们先看一份数据表样本,假设**Col1是主键**,如下: 113 | 114 | ![](https://user-gold-cdn.xitu.io/2019/8/2/16c527b96af9d8ad?w=674&h=555&f=png&s=45731) 115 | 116 | ### B+树聚集索引结构图 117 | 118 | ![](https://user-gold-cdn.xitu.io/2019/8/2/16c528780391fd35?w=1331&h=664&f=png&s=91882) 119 | 120 | - 聚集索引就是以主键创建的索引 121 | - 聚集索引在叶子节点存储的是表中的数据 122 | 123 | 124 | ### 非聚集索引结构图 125 | 假设索引列为Col3,索引结构图如下: 126 | ![](https://user-gold-cdn.xitu.io/2019/8/2/16c528a96f872632?w=1282&h=571&f=png&s=80764) 127 | 128 | - 非聚集索引就是以非主键创建的索引 129 | - 非聚集索引在叶子节点存储的是主键和索引列 130 | - 使用非聚集索引查询出数据时,拿到叶子上的主键再去查到想要查找的数据。(拿到主键再查找这个过程叫做**回表**) 131 | - 假设所查询的列,刚好都是索引对应的列,不用再**回表**查,那么这个索引列,就叫**覆盖索引**。 132 | 133 | ## InnoDB 锁类型思维导图 134 | 135 | ![](https://user-gold-cdn.xitu.io/2019/8/2/16c52a7ec7a9591a?w=1341&h=720&f=png&s=107766) 136 | ### 加锁机制 137 | 乐观锁与悲观锁是两种**并发控制**的思想,可用于解决丢失更新问题。 138 | 139 | **乐观锁** 140 | 141 | - 每次去取数据,都很乐观,觉得不会出现并发问题。 142 | - 因此,访问、处理数据每次都不上锁。 143 | - 但是在更新的时候,再根据版本号或时间戳判断是否有冲突,有则处理,无则提交事务。 144 | 145 | **悲观锁** 146 | 147 | - 每次去取数据,很悲观,都觉得会被别人修改,会有并发问题。 148 | - 因此,访问、处理数据前就加**排他锁**。 149 | - 在整个数据处理过程中锁定数据,事务提交或回滚后才释放锁. 150 | 151 | ### 锁粒度 152 | - **表锁:** 开销小,加锁快;锁定力度大,发生锁冲突概率高,并发度最低;不会出现死锁。 153 | - **行锁:** 开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高。 154 | - **页锁:** 开销和加锁速度介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般 155 | 156 | ### 兼容性 157 | **共享锁:** 158 | - 又称读锁(S锁)。 159 | - 一个事务获取了共享锁,其他事务可以获取共享锁,不能获取排他锁,其他事务可以进行读操作,不能进行写操作。 160 | - SELECT ... LOCK IN SHARE MODE 显示加共享锁。 161 | 162 | **排他锁:** 163 | - 又称写锁(X锁)。 164 | - 如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。 165 | - SELECT ... FOR UPDATE 显示添加排他锁。 166 | 167 | 168 | ### 锁模式 169 | - **记录锁:** 在行相应的索引记录上的锁,锁定一个行记录 170 | - **gap锁:** 是在索引记录间歇上的锁,锁定一个区间 171 | - **next-key锁:** 是记录锁和在此索引记录之前的gap上的锁的结合,锁定行记录+区间。 172 | - **意向锁** 是为了支持多种粒度锁同时存在; 173 | 174 | ## 参考与感谢 175 | - 《MySQL技术内幕》 176 | - 《高性能MySql》 177 | - MySQL InnoDB锁机制全面解析分享 https://segmentfault.com/a/1190000014133576 178 | - 数据库两大神器【索引和锁】 https://juejin.im/post/5b55b842f265da0f9e589e79#heading-2 179 | - InnoDB的逻辑存储结构学习 https://blog.csdn.net/m0_37752084/article/details/80496490 180 | - MySQL索引背后的数据结构及算法原理 http://blog.codinglabs.org/articles/theory-of-mysql-index.html 181 | 182 | ## 个人公众号 183 | 184 | ![](https://user-gold-cdn.xitu.io/2019/7/28/16c381c89b127bbb?w=344&h=344&f=jpeg&s=8943) 185 | 186 | 欢迎大家关注,大家一起学习,一起讨论哈。 187 | 188 | 189 | 190 | -------------------------------------------------------------------------------- /Mysql基础学习/后端程序员必备:索引失效的十大杂症.md: -------------------------------------------------------------------------------- 1 | ### 背景 2 | 最近生产爆出一条慢sql,原因是用了or和!=,导致索引失效。于是,总结了索引失效的十大杂症,希望对大家有帮助,加油。 3 | 4 | 5 | ### 一、查询条件包含or,可能导致索引失效 6 | 7 | 新建一个user表,它有一个普通索引userId,结构如下: 8 | ``` 9 | CREATE TABLE `user` ( 10 | `id` int(11) NOT NULL AUTO_INCREMENT, 11 | `userId` int(11) NOT NULL, 12 | `age` int(11) NOT NULL, 13 | `name` varchar(255) NOT NULL, 14 | PRIMARY KEY (`id`), 15 | KEY `idx_userId` (`userId`) 16 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 17 | ``` 18 | 19 | 1. 执行一条查询sql,它是会走索引的,如下图所示: 20 | ![](https://user-gold-cdn.xitu.io/2019/12/7/16ee05205553eca8?w=1171&h=274&f=png&s=23198) 21 | 2. 把or条件+没有索引的age加上,并不会走索引,如图: 22 | ![](https://user-gold-cdn.xitu.io/2019/12/7/16ee05890dadd492?w=1176&h=268&f=png&s=24193) 23 | 24 | **分析&结论:** 25 | 26 | - 对于or+没有索引的age这种情况,假设它走了userId的索引,但是走到age查询条件时,它还得全表扫描,也就是需要三步过程: 全表扫描+索引扫描+合并 27 | - 如果它一开始就走全表扫描,直接一遍扫描就完事。 28 | - mysql是有优化器的,处于效率与成本,遇到or条件,索引可能失效,看起来也合情合理。 29 | 30 | **注意:** 如果or条件的列都加了索引,索引可能会走的,大家可以自己试一试。 31 | 32 | 33 | ### 二、如何字段类型是字符串,where时一定用引号括起来,否则索引失效 34 | 35 | 假设demo表结构如下: 36 | ``` 37 | CREATE TABLE `user` ( 38 | `id` int(11) NOT NULL AUTO_INCREMENT, 39 | `userId` varchar(32) NOT NULL, 40 | `name` varchar(255) NOT NULL, 41 | PRIMARY KEY (`id`), 42 | KEY `idx_userId` (`userId`) USING BTREE 43 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 44 | ``` 45 | 46 | userId为字符串类型,是B+树的普通索引,如果查询条件传了一个数字过去,它是不走索引的,如图所示: 47 | ![](https://user-gold-cdn.xitu.io/2019/12/7/16ee09af4fcea760?w=1365&h=297&f=png&s=26114) 48 | 49 | 如果给数字加上'',也就是传一个字符串呢,当然是走索引,如下图: 50 | 51 | ![](https://user-gold-cdn.xitu.io/2019/12/7/16ee09fb8187b744?w=1329&h=307&f=png&s=26008) 52 | 53 | **分析与结论:** 54 | 55 | **为什么第一条语句未加单引号就不走索引了呢?** 56 | 这是因为不加单引号时,是字符串跟数字的比较,它们类型不匹配,MySQL会做**隐式的类型转换**,把它们转换为浮点数再做比较。 57 | 58 | 59 | ### 三、like通配符可能导致索引失效。 60 | 并不是用了like通配符,索引一定失效,而是like查询是以%开头,才会导致索引失效。 61 | 62 | 表结构: 63 | ``` 64 | CREATE TABLE `user` ( 65 | `id` int(11) NOT NULL AUTO_INCREMENT, 66 | `userId` varchar(32) NOT NULL, 67 | `name` varchar(255) NOT NULL, 68 | PRIMARY KEY (`id`), 69 | KEY `idx_userId` (`userId`) USING BTREE 70 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 71 | ``` 72 | 73 | like查询以%开头,索引失效,如图: 74 | ![](https://user-gold-cdn.xitu.io/2019/12/7/16ee0cbcfd861ad0?w=1362&h=351&f=png&s=27789) 75 | 76 | 把%放后面,发现索引还是正常走的,如下: 77 | ![](https://user-gold-cdn.xitu.io/2019/12/7/16ee0cdaa1e4e3ba?w=1418&h=348&f=png&s=28178) 78 | 79 | 把%加回来,改为只查索引的字段(**覆盖索引**),发现还是走索引,惊不惊喜,意不意外 80 | ![](https://user-gold-cdn.xitu.io/2019/12/7/16ee0d0053d631d6?w=1488&h=353&f=png&s=30036) 81 | 82 | **结论:** 83 | 84 | like查询以%开头,会导致索引失效。可以有两种方式优化: 85 | - 使用覆盖索引 86 | - 把%放后面 87 | 88 | **附:** 索引包含所有满足查询需要的数据的索引,称为覆盖索引(Covering Index)。 89 | 90 | ### 四、联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。 91 | 表结构:(有一个联合索引`idx_userid_age`,`userId`在前,`age`在后) 92 | ``` 93 | CREATE TABLE `user` ( 94 | `id` int(11) NOT NULL AUTO_INCREMENT, 95 | `userId` int(11) NOT NULL, 96 | `age` int(11) DEFAULT NULL, 97 | `name` varchar(255) NOT NULL, 98 | PRIMARY KEY (`id`), 99 | KEY `idx_userid_age` (`userId`,`age`) USING BTREE 100 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 101 | ``` 102 | 在联合索引中,查询条件满足**最左匹配原则**时,索引是正常生效的。请看demo: 103 | 104 | ![](https://user-gold-cdn.xitu.io/2019/12/7/16ee1115291be614?w=1318&h=289&f=png&s=26076) 105 | 106 | ![](https://user-gold-cdn.xitu.io/2019/12/7/16ee11320240b06d?w=1352&h=244&f=png&s=24752) 107 | 108 | 如果条件列不是联合索引中的第一个列,索引失效,如下: 109 | 110 | ![](https://user-gold-cdn.xitu.io/2019/12/7/16ee1160ba900688?w=1450&h=203&f=png&s=22934) 111 | 112 | **分析与结论:** 113 | 114 | - 当我们创建一个联合索引的时候,如(k1,k2,k3),相当于创建了(k1)、(k1,k2)和(k1,k2,k3)三个索引,这就是最左匹配原则。 115 | - 联合索引不满足最左原则,索引一般会失效,但是这个还跟Mysql优化器有关的。 116 | 117 | 118 | 119 | ### 五、在索引列上使用mysql的内置函数,索引失效。 120 | 表结构: 121 | ``` 122 | CREATE TABLE `user` ( 123 | `id` int(11) NOT NULL AUTO_INCREMENT, 124 | `userId` varchar(32) NOT NULL, 125 | `loginTime` datetime NOT NULL, 126 | PRIMARY KEY (`id`), 127 | KEY `idx_userId` (`userId`) USING BTREE, 128 | KEY `idx_login_time` (`loginTime`) USING BTREE 129 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 130 | ``` 131 | 132 | 虽然loginTime加了索引,但是因为使用了mysql的内置函数Date_ADD(),索引直接GG,如图: 133 | ![](https://user-gold-cdn.xitu.io/2019/12/7/16ee0ea3f2480fa9?w=1385&h=348&f=png&s=31108) 134 | 135 | ### 六、对索引列运算(如,+、-、*、/),索引失效。 136 | 表结构: 137 | ``` 138 | CREATE TABLE `user` ( 139 | `id` int(11) NOT NULL AUTO_INCREMENT, 140 | `userId` varchar(32) NOT NULL, 141 | `age` int(11) DEFAULT NULL, 142 | PRIMARY KEY (`id`), 143 | KEY `idx_age` (`age`) USING BTREE 144 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 145 | ``` 146 | 虽然age加了索引,但是因为它进行运算,索引直接迷路了。。。 147 | 如图: 148 | 149 | ![](https://user-gold-cdn.xitu.io/2019/12/7/16ee0fbd476c1faa?w=1405&h=291&f=png&s=25315) 150 | 151 | ### 七、索引字段上使用(!= 或者 < >,not in)时,可能会导致索引失效。 152 | 表结构: 153 | ``` 154 | CREATE TABLE `user` ( 155 | `id` int(11) NOT NULL AUTO_INCREMENT, 156 | `userId` int(11) NOT NULL, 157 | `age` int(11) DEFAULT NULL, 158 | `name` varchar(255) NOT NULL, 159 | PRIMARY KEY (`id`), 160 | KEY `idx_age` (`age`) USING BTREE 161 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 162 | ``` 163 | 虽然age加了索引,但是使用了!= 或者 < >,not in这些时,索引如同虚设。如下: 164 | 165 | ![](https://user-gold-cdn.xitu.io/2019/12/8/16ee1474432f64cc?w=1443&h=220&f=png&s=24093) 166 | 167 | ![](https://user-gold-cdn.xitu.io/2019/12/8/16ee1489157b7181?w=1434&h=285&f=png&s=26778) 168 | 169 | 170 | ### 八、索引字段上使用is null, is not null,可能导致索引失效。 171 | 表结构: 172 | ``` 173 | CREATE TABLE `user` ( 174 | `id` int(11) NOT NULL AUTO_INCREMENT, 175 | `card` varchar(255) DEFAULT NULL, 176 | `name` varchar(255) DEFAULT NULL, 177 | PRIMARY KEY (`id`), 178 | KEY `idx_name` (`name`) USING BTREE, 179 | KEY `idx_card` (`card`) USING BTREE 180 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 181 | ``` 182 | 183 | 单个name字段加上索引,并查询name为非空的语句,其实会走索引的,如下: 184 | 185 | ![](https://user-gold-cdn.xitu.io/2019/12/8/16ee16273576109d?w=1516&h=251&f=png&s=25893) 186 | 187 | 单个card字段加上索引,并查询name为非空的语句,其实会走索引的,如下: 188 | ![](https://user-gold-cdn.xitu.io/2019/12/8/16ee169934434177?w=1180&h=242&f=png&s=23687) 189 | 190 | 但是它两用or连接起来,索引就失效了,如下: 191 | 192 | ![](https://user-gold-cdn.xitu.io/2019/12/8/16ee16b069fd566d?w=1220&h=283&f=png&s=25460) 193 | 194 | 195 | 196 | ### 九、左连接查询或者右连接查询查询关联的字段编码格式不一样,可能导致索引失效。 197 | 新建两个表,一个user,一个user_job 198 | ``` 199 | CREATE TABLE `user` ( 200 | `id` int(11) NOT NULL AUTO_INCREMENT, 201 | `name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL, 202 | `age` int(11) NOT NULL, 203 | PRIMARY KEY (`id`), 204 | KEY `idx_name` (`name`) USING BTREE 205 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 206 | 207 | CREATE TABLE `user_job` ( 208 | `id` int(11) NOT NULL, 209 | `userId` int(11) NOT NULL, 210 | `job` varchar(255) DEFAULT NULL, 211 | `name` varchar(255) DEFAULT NULL, 212 | PRIMARY KEY (`id`), 213 | KEY `idx_name` (`name`) USING BTREE 214 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 215 | 216 | ``` 217 | 218 | user 表的name字段编码是utf8mb4,而user_job表的name字段编码为utf8。 219 | 220 | ![](https://user-gold-cdn.xitu.io/2019/12/8/16ee2cda2c47eb0c?w=1038&h=352&f=png&s=25812) 221 | 222 | ![](https://user-gold-cdn.xitu.io/2019/12/8/16ee2d07a2e78ded?w=1033&h=415&f=png&s=29243) 223 | 224 | 执行左外连接查询,user_job表还是走全表扫描,如下: 225 | 226 | ![](https://user-gold-cdn.xitu.io/2019/12/8/16ee2d1749af07c0?w=1187&h=313&f=png&s=31938) 227 | 228 | 如果把它们改为name字段编码一致,还是会走索引。 229 | 230 | ![](https://user-gold-cdn.xitu.io/2019/12/8/16ee2d4145752fb7?w=1138&h=315&f=png&s=31574) 231 | 232 | 233 | ### 十、mysql估计使用全表扫描要比使用索引快,则不使用索引。 234 | 235 | - 当表的索引被查询,会使用最好的索引,除非优化器使用全表扫描更有效。优化器优化成全表扫描取决与使用最好索引查出来的数据是否超过表的30%的数据。 236 | 237 | - 不要给'性别'等增加索引。如果某个数据列里包含了均是"0/1"或“Y/N”等值,即包含着许多重复的值,就算为它建立了索引,索引效果不会太好,还可能导致全表扫描。 238 | 239 | Mysql出于效率与成本考虑,估算全表扫描与使用索引,哪个执行快。这跟它的优化器有关,来看一下它的逻辑架构图吧(图片来源网上) 240 | 241 | ![](https://user-gold-cdn.xitu.io/2019/12/8/16ee2dbf2878b2d2?w=911&h=537&f=png&s=258744) 242 | 243 | 244 | ### 总结 245 | 246 | 总结了索引失效的十大杂症,在这里来个首尾呼应吧,分析一下我们生产的那条慢sql。 247 | 模拟的表结构与肇事sql如下: 248 | ``` 249 | CREATE TABLE `user_session` ( 250 | `user_id` varchar(32) CHARACTER SET utf8mb4 NOT NULL, 251 | `device_id` varchar(64) NOT NULL, 252 | `status` varchar(2) NOT NULL, 253 | `create_time` datetime NOT NULL, 254 | `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, 255 | PRIMARY KEY (`user_id`,`device_id`) USING BTREE 256 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 257 | ``` 258 | 259 | ``` 260 | explain 261 | update user_session set status =1 262 | where (`user_id` = '1' and `device_id`!='2') 263 | or (`user_id` != '1' and `device_id`='2') 264 | ``` 265 | 266 | **分析:** 267 | 268 | - 执行的sql,使用了`or`条件,因为组合主键(`user_id`,`device_id`),看起来像是每一列都加了索引,索引会生效。 269 | - 但是出现`!=`,可能导致索引失效。也就是`or`+`!=`两大综合症,导致了慢更新sql。 270 | 271 | 272 | **解决方案:** 273 | 274 | 那么,怎么解决呢?我们是把`or`条件拆掉,分成两条执行。同时给`device_id`加一个普通索引。 275 | 276 | 277 | 最后,总结了索引失效的十大杂症,希望大家在工作学习中,参考这十大杂症,**多点结合执行计划`expain`和场景,具体分析** ,而不是**按部就班,墨守成规**,认定哪个情景一定索引失效。 278 | 279 | ### 个人公众号 280 | 281 | ![](https://user-gold-cdn.xitu.io/2019/7/28/16c381c89b127bbb?w=344&h=344&f=jpeg&s=8943) 282 | 283 | - 如果你是个爱学习的好孩子,可以关注我公众号,一起学习讨论。 284 | - 如果你觉得本文有哪些不正确的地方,可以评论,也可以关注我公众号,私聊我,大家一起学习进步哈。 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | 整理一份超级详细的Java面试题+后端基础+日常工作总结,做最暖心的男孩子,后面会慢慢完善,希望大家找到理想offer 4 | 5 | ⭐ 点右上角给一个 Star,鼓励技术人输出更多干货,感谢感谢,爱了! ! 6 | 7 | 作者捡田螺的小男孩,浪迹过几家大厂,**掘金优秀创作者**,CSDN博主,知乎博主。以下内容全部出自公众号:**捡田螺的小男孩**,欢迎关注。 8 | 9 | - [田螺原创精品100篇](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497536&idx=1&sn=3ac9934f607d79e51457fd01f4c8a4ef&chksm=cf222869f855a17fc30c744e5b7ccdeca407f3b7ddcca46bae1c93b1436ffc6fe417ccb8aef4&token=1990771297&lang=zh_CN#rd) 10 | 11 | ## 工作总结 12 | 13 | - [盘点数据库主从延迟的9个原因以及解决方案](https://mp.weixin.qq.com/s/aT7YjsTrM_dhDbddr8TSjg?token=528541177&lang=zh_CN) 14 | - [实战项目,是如何保证缓存跟数据库数据一致性的?](https://mp.weixin.qq.com/s/UVHMeFDO4NYTnSwHZc9f1A?token=528541177&lang=zh_CN) 15 | - [工作总结!日志打印的15个建议](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494838&idx=1&sn=cdb15fd346bddf3f8c1c99f0efbd67d8&chksm=cf22339ff855ba891616c79d4f4855e228e34a9fb45088d7acbe421ad511b8d090a90f5b019f&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 16 | - [25种代码坏味道总结+优化示例](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490148&idx=1&sn=00a181bf74313f751b3ea15ebc303545&chksm=cf21c54df8564c5bc5b4600fce46619f175f7ae557956f449629c470a08e20580feef4ea8d53&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 17 | - [聊聊日常开发中,如何减少bug呢?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490662&idx=1&sn=d38a090611af7f64ee3c6a31331d5228&chksm=cf21c34ff8564a59e505e6edf3065a0fc506c6d2c96f492c8d8873cd46dedbe0704e43cb9c2e&token=1990771297&lang=zh_CN#rd) 18 | - [工作四年,分享50个让你代码更好的小建议](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488708&idx=1&sn=6e2e0a740f5d42a59641487a0bf1e3bf&chksm=cf21cbedf85642fbb485fa1c7bf9af21923d8503f2542b6f8283ce79ddc683f7d9e45da83100&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 19 | - [写代码有这16个好习惯,可以减少80%非业务的bug](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488097&idx=1&sn=eaca1f92ca3ccd9de00dbc4ef3e4029a&chksm=cf21cd48f856445e4cc24c1f8bcf18d1479bad0a37a87a2fb70717d8a4e65dcf7b4d5f83d24f&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 20 | - [Java日常开发的21个坑,你踩过几个?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488115&idx=1&sn=bdd4a4ca36bc7ea902106d058e8537fb&chksm=cf21cd5af856444cb36af600705615454b0aaa2b289b97ddb52d594556ac07a1915b73ecce19&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 21 | - [CAS乐观锁解决并发问题的一次实践](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487937&idx=1&sn=206a37bf6d6a7aa1d05674c479ed7a72&chksm=cf21cee8f85647fe7a082049a41c0f640f54976d2cdf4302b24c5517ca42b854eb84b13ece10&token=1990771297&lang=zh_CN#rd) 22 | - [写代码有这些想法,同事才不会认为你是复制粘贴程序员](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487961&idx=1&sn=e646231067968d9f58e6665914293f9a&chksm=cf21cef0f85647e6f3ff2feece004ac3bd979e37fe45103c88d0f299dfe632a5cf6dd547c1d9&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 23 | - [程序员必备:Java日期处理的十个坑](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487973&idx=1&sn=0f713413098fb579e5f200b829f71e89&chksm=cf21ceccf85647da450765d79bf5943da551c3be950447063b9f8c77c21bf2a39b99387a949b&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 24 | - [内存泄漏问题的分析和解决方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487986&idx=1&sn=d681a585ac489703788e3baa48eb9aa3&chksm=cf21cedbf85647cd23bbab9dfec63e6877f83c34efb19bd16075d5d90fea91d3f4a20fc77921&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 25 | - [程序员必备基础:加签验签](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488022&idx=1&sn=70484a48173d36006c8db1dfb74ab64d&chksm=cf21cd3ff8564429a1205f6c1d78757faae543111c8461d16c71aaee092fe3e0fed870cc5e0e&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 26 | - [记一次接口性能优化实践总结:优化接口性能的八个建议](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488004&idx=1&sn=00840efd9c0bd0a7f172b59eb2ca130f&chksm=cf21cd2df856443bf21d8e09cfe5c8452ecaf82e3c2210fca3b28829ded04defddcf63c0a59b&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 27 | - [程序员必备基础:如何安全传输存储用户密码?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488117&idx=1&sn=5d3d0eda0ed45f3f576e211de31ca3a9&chksm=cf21cd5cf856444af1407a94a2abf445265ca7c5f5855cfa1c223cb209e99040c7889621f231&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 28 | - [一次代码优化实践,用了模板方法+策略+工厂方法模式](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488061&idx=1&sn=1d9ab7954b03521ab81ecf033c0e5e50&chksm=cf21cd14f8564402b213f0ef908bbdb0e12fed4b281c5803b8e539cacb1551654194becfb7d6&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 29 | - [保证接口数据安全的10种方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247500285&idx=1&sn=7d0723f25d46e858859cfd79acb6fb9d&chksm=cf221ed4f85597c2093f81baa5fdedc65817bf2d23a7951236836b0f54c2335695cbed61cd13&token=1990771297&lang=zh_CN#rd) 30 | - [实战总结!18种接口优化方案的总结](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247506674&idx=1&sn=8b2914d9aafa334029495b029b69d0b6&chksm=c1e0277ef697ae68e8c2bffe4bd7d9849be3165ef1a20286538f6a7569a6ba0879d517d55b87&token=337310304&lang=zh_CN#rd) 31 | - [聊聊工作中常用的Lambda表达式](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247506654&idx=1&sn=4835e9f486e643765d4ad3b3fc93e079&chksm=c1e02752f697ae442f62fc122d7604f4b01979f6d1665df414bb499fd8ba211335ebc503c368&token=337310304&lang=zh_CN#rd) 32 | - [21个MySQL表设计的经验准则](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247506621&idx=1&sn=afca898cb461827054d706a92f9b9250&chksm=c1e02731f697ae27a83e5637ee2184d1e26e5090caeaa58121d3cf5afab7d4d5832cac6d171a&token=337310304&lang=zh_CN#rd) 33 | - [程序员必备基础:如何安全传输存储用户密码?](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247506023&idx=1&sn=b96dde436c1c9fe4bda745ca5ca1b170&source=41#wechat_redirect) 34 | 35 | ## 福利 500+页原创面试题 36 | 37 | - [田螺原创500+页面试题](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499943&idx=1&sn=fe869c0a97a306e42830336fe74e17a6&chksm=cf221f8ef8559698781709bfbccbb85087286e48434905fb18bec3a3ec0af7329c2a1632c230&token=1990771297&lang=zh_CN#rd) 38 | 39 | 40 | ## 个人公众号 41 | 42 | 微信搜公众号:**捡田螺的小男孩** 43 | 44 | - 小伙伴可以关注我的公众号(扫描下面二维码,还有**很多很多干货文章**),一起学习讨论哈~~ 45 | 46 | ![扫一扫](https://user-images.githubusercontent.com/20244922/179399354-8a9fd2a8-42ba-4303-9ce5-04891e899e6d.png) 47 | -------------------------------------------------------------------------------- /image/公众号二维码图片.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/image/公众号二维码图片.png -------------------------------------------------------------------------------- /image/夏天的风.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/image/夏天的风.jpg -------------------------------------------------------------------------------- /image/赞赏码.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/image/赞赏码.jpg -------------------------------------------------------------------------------- /letecode解题算法介绍/README.MD: -------------------------------------------------------------------------------- 1 | ## leetcode(持续更新中) 2 | 3 | 关注公众号:捡田螺的小男孩 4 | 5 | - [看一遍就理解:动态规划详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247489016&idx=1&sn=bba3fb1a7a864b6ccefeb9f633751811&chksm=cf21cad1f85643c716c8c9396d3a6711f7722f8f81c8f40f5a91c525c98f73f5c476b7d49dd4&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 6 | - [程序员必备的基本算法:递归详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488073&idx=1&sn=ec81b4a1f8b11ea59264b55e571fed91&chksm=cf21cd60f8564476952c5abb8ffa93fc38fde354a61ca5596e1875d35760383f3a92b2879e30&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 7 | - [看一遍就理解,图解单链表反转](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487967&idx=1&sn=e75373dcb0507081c242ba018b42ca82&chksm=cf21cef6f85647e0cbf0b2072eb1264a44abcaa9f4a0621ef8954a1b1d6719560f7f4cbbce60&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 8 | - [面试必备:回溯算法详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497665&idx=1&sn=39011296fa99eda839ab2bbe83a42cdf&chksm=cf2228e8f855a1fe8f059130dc0b3d9ad34431a27bbe7e16f508b7e9340c24e2e4dfd8b414c2&token=1990771297&lang=zh_CN#rd) 9 | - [leetcode必备算法:聊聊滑动窗口](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247496003&idx=1&sn=8c40eb3e611514f3bafb8d6873c03fda&chksm=cf222e6af855a77ce2fc36d4e4fc02945286300206f43975e30bc23b65c9ca67b6a1ac9806d1&token=1990771297&lang=zh_CN#rd) 10 | - [五分钟搞定贪心算法,从此不惧大厂面试](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490681&idx=1&sn=0388da1492fe0fdfa3ed6b1a43511328&chksm=cf21c350f8564a466d89578f73886eb462c6dd485f42e7953f126be5f9af49b3fb0be3457d52&token=1990771297&lang=zh_CN#rd) 11 | - [双指针+归并排序!图解排序链表!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247496038&idx=1&sn=96a1a665e43ee9e3337e3d941db49f1e&chksm=cf222e4ff855a75919f0be68e78472199c44d0e9d94de6d5bf621a892ba211738d6f4dbd53ac&token=1990771297&lang=zh_CN#rd) 12 | - [双指针技巧](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488116&idx=1&sn=aeec0553e2317bef76d158d2b0e0b5a5&chksm=cf21cd5df856444b8963efc2745bce6801df4bc547b679ae8366fa8c3cd293f1f7c60c18e4f6&token=1990771297&lang=zh_CN#rd) 13 | - [字符串匹配算法详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494506&idx=1&sn=1f13b0cc1f03af464e1063be8ef1cb57&chksm=cf223443f855bd5597898126d12c6039f64da47b8a95714018203ee5e453950c802ebecfabe1&token=1990771297&lang=zh_CN#rd) 14 | -------------------------------------------------------------------------------- /letecode解题算法介绍/反转链表看一遍就懂.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 反转链表是程序员必备的基本素养,经常在面试、笔试的过程中出现。一直觉得反转链表实现代码不是很好理解,决定搬leetcode那道经典反转链表题出来,用十多张图去解析它,希望加深大家对链表反转的理解,谢谢阅读。 3 | 4 | ### leetcode的反转链表原题&答案 5 | 6 | **题目描述:** 反转一个单链表。 7 | ``` 8 | 输入: 1->2->3->4->5->NULL 9 | 输出: 5->4->3->2->1->NULL 10 | ``` 11 | 12 | **分析:** 13 | 14 | 假设存在链表 1 → 2 → 3 → Ø,我们想要把它改成 Ø ← 1 ← 2 ← 3。 15 | 16 | 在遍历列表时,将当前节点的 next 指针改为指向前一个元素。由于节点没有引用其上一个节点,因此必须事先存储其前一个元素。在更改引用之前,还需要另一个指针来存储下一个节点。不要忘记在最后返回新的头引用! 17 | 18 | **代码实现:** 19 | ``` 20 | public ListNode reverseList(ListNode head) { 21 | ListNode prev = null; 22 | ListNode curr = head; 23 | while (curr != null) { 24 | ListNode nextTemp = curr.next; 25 | curr.next = prev; 26 | prev = curr; 27 | curr = nextTemp; 28 | } 29 | return prev; 30 | } 31 | ``` 32 | 33 | ### 图解链表反转代码的实现 34 | 接下来,我们图解以上代码实现,先对以上实现代码加上行号,如下: 35 | ``` 36 | public ListNode reverseList(ListNode head) { //1 37 | ListNode prev = null; // 2 38 | ListNode curr = head; // 3 39 | while (curr != null) { //4 40 | ListNode nextTemp = curr.next; //5 41 | curr.next = prev; // 6 42 | prev = curr; //7 43 | curr = nextTemp; //8 44 | } 45 | return prev; //9 46 | } 47 | ``` 48 | 49 | #### 第一行代码图解 50 | 51 | ``` 52 | public ListNode reverseList(ListNode head) { //1 53 | ``` 54 | 我们顺着题目描述意思,假设链表就有1、2、3个元素吧,后面还跟着一个null,又因为输入是ListNode head,所以这个即将要反转的链表如下: 55 | 56 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1701fbe941179b51?w=909&h=245&f=png&s=21126) 57 | 58 | #### 第二行代码图解 59 | 60 | ``` 61 | ListNode prev = null; // 2 62 | ``` 63 | 将null赋值给prev,即prev指向null,可得图如下: 64 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1701f9da5c94b500?w=216&h=210&f=png&s=7193) 65 | 66 | #### 第三行代码图解 67 | 68 | ``` 69 | ListNode curr = head; 70 | ``` 71 | 将链表head赋值给curr,即curr指向head链表,可得图如下: 72 | 73 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1701fbfcaab4dd99?w=870&h=243&f=png&s=20089) 74 | 75 | 76 | #### 循环部分代码图解 77 | 78 | ``` 79 | while (curr != null) { //4 80 | ListNode nextTemp = curr.next; //5 81 | curr.next = prev; // 6 82 | prev = curr; //7 83 | curr = nextTemp; //8 84 | } 85 | ``` 86 | 循环部分是**链表反转的核心**部分,我们先走一遍循环,图解分析一波。 87 | 88 | 因为**curr指向了head**,**head不为null**,所以进入循环。**先来看第5行:** 89 | ``` 90 | ListNode nextTemp = curr.next; //5 91 | ``` 92 | 把curr.next 赋值给nextTemp变量,即nextTemp 指向curr的下一节点(即节点2),可得图如下: 93 | 94 | 95 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1701ff3466fffa10?w=833&h=240&f=png&s=21780) 96 | 97 | 再执行到第6行: 98 | ``` 99 | curr.next = prev; // 6 100 | ``` 101 | 把prev赋值给curr.next,因为prev初始化化指向null,即curr(节点1)指向了null,链表图解成这样了: 102 | 103 | 104 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1701fbd124bde1f2?w=1068&h=263&f=png&s=27349) 105 | 106 | 然后我们看执行到第7行 107 | 108 | ``` 109 | prev = curr; //7 110 | ``` 111 | 把curr赋值给prev,prev指向curr,图解如下: 112 | 113 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1701fc2fc9a7ba5c?w=1091&h=347&f=png&s=35650) 114 | 115 | 接着,我们执行到第8行: 116 | 117 | ``` 118 | curr = nextTemp; //8 119 | ``` 120 | 把nextTemp赋值给curr,即curr指向nextTemp,图解如下: 121 | 122 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1701fc5ceadd3e2f?w=1042&h=364&f=png&s=35340) 123 | 124 | 至此,第一遍循环执行结束啦,回到循环条件,**curr依旧不为null**,我们继续图解完它。 125 | 126 | 5-8行代码又执行一遍,依次可得图: 127 | 128 | ``` 129 | ListNode nextTemp = curr.next; //5 130 | curr.next = prev; // 6 131 | prev = curr; //7 132 | curr = nextTemp; //8 133 | ``` 134 | 135 | 执行完```ListNode nextTemp = curr.next; ```后: 136 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1701fceea65ac66e?w=1011&h=360&f=png&s=34547) 137 | 138 | 执行完```curr.next = prev; ```后: 139 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1701fd391ef75c17?w=1108&h=339&f=png&s=35342) 140 | 141 | 执行完```prev = curr; ```后: 142 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1701fd5ad58277e1?w=1091&h=359&f=png&s=36280) 143 | 144 | 执行完```curr = nextTemp;```后: 145 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1701fd73f53f1a2b?w=1103&h=346&f=png&s=35454) 146 | 147 | 来到这里,发现curr还是不为null,再回到while循环,再执行一遍: 148 | 149 | ``` 150 | ListNode nextTemp = curr.next; //5 151 | curr.next = prev; // 6 152 | prev = curr; //7 153 | curr = nextTemp; //8 154 | ``` 155 | 依次可得图: 156 | 157 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1701fdf5b6532ed5?w=1130&h=340&f=png&s=35491) 158 | 159 | 160 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1702007c264eddd4?w=1077&h=320&f=png&s=32820) 161 | 162 | 163 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1701fe5ac9b9d1d8?w=1135&h=330&f=png&s=34388) 164 | 165 | ![](https://user-gold-cdn.xitu.io/2020/2/7/1701fe66d7971447?w=1137&h=330&f=png&s=34740) 166 | 167 | 来到这里,我们发现curr已经为null了,可以跳出循环了。这时候prev指向的就是链表的反转呀,所以第9行执行完,反转链表功能实现: 168 | 169 | ``` 170 | return prev; //9 171 | ``` 172 | 173 | ### 参考与感谢 174 | - [LeetCode 官网](https://leetcode-cn.com/problems/reverse-linked-list/solution/) 175 | 176 | 177 | ### 个人公众号 178 | 179 | ![](https://user-gold-cdn.xitu.io/2019/7/28/16c381c89b127bbb?w=344&h=344&f=jpeg&s=8943) 180 | 181 | - 如果你是个爱学习的好孩子,可以关注我公众号,一起学习讨论。 182 | - 如果你觉得本文有哪些不正确的地方,可以评论,也可以关注我公众号,私聊我,大家一起学习进步哈。 -------------------------------------------------------------------------------- /中间件/README.MD: -------------------------------------------------------------------------------- 1 | ## 中间件 2 | 3 | - [一文快速入门分库分表中间件 Sharding-JDBC (必修课)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499781&idx=1&sn=74bbb25c9347408f1edf7f8c9c82d7cf&chksm=cf221f2cf855963a6549069deeabe93bb6d6e889bcd086668bf6f0e23327fa1ddb31adc6d10c&token=1990771297&lang=zh_CN#rd) 4 | - [全方位对比Zookeeper、Eureka、Nacos、Consul和Etcd](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498268&idx=1&sn=7b24b8625fb4ff88d50c9bd55335f478&chksm=cf222535f855ac230dfca629127f93efec606641d7338c29a8d41e7d2016a7f0b6ec28a432a0&token=1990771297&lang=zh_CN#rd) 5 | - [消息队列经典十连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497847&idx=1&sn=29a32672b712e7dfadfa36c9902b2ec7&chksm=cf22275ef855ae484fb3f51a5726e9a4bc45222e8fbbd33631d177dc4b5619c36889ea178463&token=1990771297&lang=zh_CN#rd) 6 | - [Kafka性能篇:为何Kafka这么"快"?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488717&idx=1&sn=006c65f9a9a5796961c42f3cafc37cb4&chksm=cf21cbe4f85642f2e8ff948f8de8a69508783cee6dafd22512d6a06cd03f7065001bd1d8d87b&token=1990771297&lang=zh_CN#rd) 7 | - [后端程序员必备:RocketMQ相关流程图/原理图](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487949&idx=1&sn=888e0917884b2918a94053e5cd560e00&chksm=cf21cee4f85647f24877791d574f5ef3f979fc9c4c84ca3fd1ea1aa08ab30c1041ad3aaa5650&token=1990771297&lang=zh_CN#rd) 8 | - [ZooKeeper的十二连问,你顶得了嘛?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488047&idx=1&sn=4913c7e1c3b8835f7512d8dc6b845727&chksm=cf21cd06f8564410cce6121230256facb1ab3b5a9ed35579896f428d84bdea7b86836109d575&token=162724582&lang=zh_CN&scene=21#wechat_redirect) -------------------------------------------------------------------------------- /分布式/README.MD: -------------------------------------------------------------------------------- 1 | ## 分布式 2 | 3 | 关注公众号:捡田螺的小男孩 4 | 5 | - [面试必备:聊聊分布式锁的多种实现!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498595&idx=1&sn=4e5308930e151a609baa2df820e48a89&chksm=cf22244af855ad5c71822cb33e828ce652c6f34202096a9344922b86dcbc08076d7922acde5f&token=1990771297&lang=zh_CN#rd) 6 | - [我们为什么要分库分表?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498625&idx=1&sn=0d7bd9d1b46eeff4c715a6761355e9b0&chksm=cf2224a8f855adbea8931c8e011711f6c70cffeef8ddf8b87729c710eacef11b46eef80fda36&token=1990771297&lang=zh_CN#rd) 7 | - [聊聊高可用的 11 个关键技巧](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498784&idx=1&sn=aad1c00d6eafb0c1f08959612c69959a&chksm=cf222309f855aa1f71ef9cf470bfa72ac73365c401ec7c7d0c3b241a9116c3112f83760793e8&token=1990771297&lang=zh_CN#rd) 8 | - [看一遍就理解:分布式事务详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498358&idx=1&sn=aa6c7ceb61b73267d68d1b4fb7ccc2ed&chksm=cf22255ff855ac495861d57df276517e89779006267fa8413fe925cc15b0c3e0b0f1b1a5675e&token=1990771297&lang=zh_CN#rd) 9 | - [几种主流的分布式定时任务,你知道哪些?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498121&idx=1&sn=e3d7e4f5297c7b2390b412a9bafc3385&chksm=cf2226a0f855afb669cde8d7f400fb334bd4c75a8c672d1208667387d03d2dfd24884e60b825&token=1990771297&lang=zh_CN#rd) 10 | - [redis分布式锁的8大坑,记得拿小本本记下来啦](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495390&idx=1&sn=87cc1567c709cfa67b43dd8d273bb426&chksm=cf2231f7f855b8e17919f7763469d87c47d9b4c4ad25aba7e6ff60fa33b048bc47a4afd287fc&token=1990771297&lang=zh_CN#rd) 11 | - [框架篇:分布式一致性解决方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490468&idx=2&sn=91b8e5dd2ce3db218708b5c736fce700&chksm=cf21c48df8564d9b30164e1dbf9b5ebcc1847a9450d08ee146c98eb53107af475149ad12a748&token=1990771297&lang=zh_CN#rd) 12 | - [这三年被分布式坑惨了,曝光十大坑](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488553&idx=2&sn=fa13e9698e59f5a5485d3d3d4b8ef2b1&chksm=cf21cb00f8564216277806780c64e13c48fe32009f588349b3365afa8de97bd8ef192507bd50&token=1990771297&lang=zh_CN#rd) -------------------------------------------------------------------------------- /原创诗集/夏天的风我永远记得.md: -------------------------------------------------------------------------------- 1 | 夜深了,宁静了。 2 | 朝南的窗, 3 | 我轻轻地打开了。 4 | 夏夜的风, 5 | 温柔且粘人, 6 | 穿过头发, 7 | 吻着耳朵, 8 | 感觉特别舒服。 9 | 10 | 想起了小时候, 11 | 老家屋子热得发烫~ 12 | 我牵着母亲的大手, 13 | 抱着小枕头, 14 | 裹着小凉席, 15 | 一步两步三步, 16 | 走向楼顶,睡觉。 17 | 18 | 那时候, 19 | 漫天的繁星活泼且迷人, 20 | 对着我不停眨着眼, 21 | 像大眼睛的小女孩子, 22 | 害羞地浅笑。 23 | 月亮也圆圆大大的, 24 | 像个暖心的大姐姐, 25 | 马上要抱着我入眠。 26 | 27 | 那时候, 28 | 萤火虫似一盏盏小油灯, 29 | 照亮的我小脸蛋。 30 | 知了在吵闹过后, 31 | 也安静了下来, 32 | 周围像人过港空的街道。 33 | 飞机划过苍穹, 34 | 留下隆隆的声音, 35 | 给这个夜,留下美好的快照。 36 | 37 | 母亲在我耳旁喃喃细语, 38 | 赶紧睡觉吧, 39 | 明天跟我去田里, 40 | 收割水稻。 41 | 42 | 一听,我心就乐了。 43 | 想到田里有活泼的稻花鱼, 44 | 有早出晚归的小田螺, 45 | 还有喜欢偷吃稻谷的小麻雀。 46 | 慢慢地,进入了梦乡, 47 | 带着对母亲的思念~ 48 | 49 | 最后,提前祝天底下所有的母亲,母亲节快乐~ -------------------------------------------------------------------------------- /后端思维篇/README.md: -------------------------------------------------------------------------------- 1 | ## 后端思维篇(持续更新中) 2 | 3 | 公众号:捡田螺的小男孩 4 | 5 | - [后端思想篇:设计好接口的36个锦囊!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499388&idx=1&sn=49a22120a3238e13ad7c3d3b73d9e453&chksm=cf222155f855a8434026b2c460d963c406186578c2527ca8f2bb829bbe849d87a2392a525a9b&scene=178&cur_album_id=2396778860463161350#rd) 6 | - [后端思维篇:手把手教你写一个并行调用模板](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499504&idx=1&sn=bb62226e6cffeb1859efb0100c796050&chksm=cf2221d9f855a8cf23f75cb51c1a407578fb0f279e96ddae74b5b8c84f2f5dc71762425b17cb&scene=178&cur_album_id=2396778860463161350#rd) 7 | - [后端思维篇:如何应用设计模式优化代码](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499524&idx=1&sn=cb4cc48a3e8d9a54b0ebc4c7ad517f14&chksm=cf22202df855a93b37327856ee88b0bf5f6ed7da67964438fc2cf747666260d5026dd62d4a17&scene=178&cur_album_id=2396778860463161350#rd) 8 | - [后端思维篇:统一参数校验、异常处理、结果返回](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499708&idx=1&sn=808979c495acd9344732d147c0ad40d3&chksm=cf222095f855a983f31d5f6abf401fa3b5967f8839c6775d35cefc5cc6244fb4135563ff1090&scene=178&cur_album_id=2396778860463161350#rd) 9 | - [后端思维篇:如何抽一个观察者模板](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247500159&idx=1&sn=a5328372e580b22c939a5b3084aef164&chksm=cf221e56f85597401e8c99b8dd1bc1af97fcf69207ceaa04c5c26e028ac47d1658b79ae32291&scene=178&cur_album_id=2396778860463161350#rd) 10 | - [后端思维篇:后端思维专栏:通过层层代码去重,我又搞了一个通用模板](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247506942&idx=1&sn=ae14ed5cc179f73ea0b2f37c73ad8da4&chksm=c1e02672f697af645943ea8ee53b7cef6257ebbc21d2b77058994e98bdb1e107ad313e29e8c3&token=134957671&lang=zh_CN#rd) 11 | -------------------------------------------------------------------------------- /后端思维篇/后端思维一:设计接口的36个锦囊.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/后端思维篇/后端思维一:设计接口的36个锦囊.md -------------------------------------------------------------------------------- /后端思维篇/后端思维篇二:手把手教你实现一个并行调用模板.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/后端思维篇/后端思维篇二:手把手教你实现一个并行调用模板.md -------------------------------------------------------------------------------- /大厂面试真题/README.MD: -------------------------------------------------------------------------------- 1 | ## 1. 面试真题 2 | 3 | 关注公众号:捡田螺的小男孩 4 | ​ 5 | - [oppo后端16连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498750&idx=1&sn=19fe8b4fff28fe81db14e733053bbc74&chksm=cf2224d7f855adc1d0984980a4e3de31fe33329164a472ca8d8255a8a80b69b2e23850811323&token=2001057130&lang=zh_CN#rd) 6 | - [小厂后端十连问(附答案)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498316&idx=1&sn=7749b78293b7b2af51eda99844e08a56&chksm=cf222565f855ac7324232e2af459f8b6e6eb5fd5b272c2b29bda08cc579421b6704a0de94b2e&token=2001057130&lang=zh_CN#rd) 7 | - [腾讯云后端15连问!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498171&idx=1&sn=f5a7ec25a569822be0f73fbcd413e8ba&chksm=cf222692f855af84fba419166fcd4235c0e78af3a2e1ec4c723a4efb1bd1ad6f8a5b9404c599&token=2001057130&lang=zh_CN#rd) 8 | - [社招后端21连问(三年工作经验一面)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498084&idx=1&sn=96c8148cfeeeb16668ed9e03fa9131cc&chksm=cf22264df855af5b6e81b93738cca28989226a53ec702fcfaa0cc5004dded4208c5ee5ea844a&token=2001057130&lang=zh_CN#rd) 9 | - [一份热乎乎的字节面试真题](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497742&idx=1&sn=18765e7356f446a7f2521f45b467d5d3&chksm=cf222727f855ae31dd2029e3219814211336c41d9228d271a583d3691ddadca586529aca9302&token=2001057130&lang=zh_CN#rd) 10 | - [面试必备:虾皮服务端15连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497256&idx=1&sn=3b799c2d514aa25e85a6faa60d639a0b&chksm=cf222901f855a017b73356b99b830b8800a7a9172fab891c5759d8dd69a270872ea9480c0b7c&token=2001057130&lang=zh_CN#rd) 11 | - [宇宙条一面:十道经典面试题解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495342&idx=1&sn=54e1c0c16a6467001524c34818025331&chksm=cf223187f855b89140db5ca429e6efc19d0111abf7f36b78a0ecd73b00fded1ff1e7ba32a6f1&token=2001057130&lang=zh_CN#rd) 12 | - [蚂蚁金服一面:十道经典面试题解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247493270&idx=1&sn=1c78a81d6e1bd0f0fd947fe8c3a33e32&chksm=cf2239bff855b0a9627855f20a17799e0506eb7548a409bfa0ee0450328d7519ec70f7b962cc&token=2001057130&lang=zh_CN#rd) 13 | - [田螺精品面试PDF发布](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499943&idx=1&sn=fe869c0a97a306e42830336fe74e17a6&chksm=cf221f8ef8559698781709bfbccbb85087286e48434905fb18bec3a3ec0af7329c2a1632c230&token=1990771297&lang=zh_CN#rd) -------------------------------------------------------------------------------- /大厂面试真题/腾讯后端面试真题.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 最近有个好朋友换工作了,面了腾讯后端,跟他要了份面试真题,大家一起来探讨一下,哈哈~ 3 | 4 | ## 腾讯后端一面 5 | ### ① JVM内存模型 6 | 7 | 这个可以复习一下《深入理解Java虚拟机》第12章(Java内存模型和线程)哈~,也可以看看我之前的文章哈~[JVM常见面试题解析](https://juejin.im/post/5d35ca5b518825449c64bc31) 8 | 9 | JVM内存结构: 10 | ![](https://user-gold-cdn.xitu.io/2020/4/26/171b3b57de3b838c?w=727&h=478&f=png&s=203974) 11 | 12 | Java内存模型图: 13 | ![](https://user-gold-cdn.xitu.io/2020/4/26/171b3a11106f2cad?w=823&h=759&f=png&s=231498) 14 | 15 | ### ②cms和g1有没有了解过,它们有什么区别 16 | 17 | - CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用; 18 | - G1收集器收集范围是老年代和新生代,不需要结合其他收集器使用; 19 | - CMS收集器以最小的停顿时间为目标的收集器; 20 | - G1收集器可预测垃圾回收的停顿时间 21 | - CMS收集器是使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片 22 | - G1收集器使用的是“标记-整理”算法,进行了空间整合,降低了内存空间碎片。 23 | 24 | 这个点是可以看《深入理解Java虚拟机》第三章,垃圾收集器与内存分配策略哈 25 | 26 | ### ③谈谈你对垃圾回收的了解,什么时候发生垃圾回收,回收过程 27 | 28 | 可以讲JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代,如Minor GC,Major GC,full GC这几个讲清楚,还有对象存活判断方法,还有垃圾回收算法,复制算法等等 29 | 30 | 这个点也是可以看《深入理解Java虚拟机》第三章,垃圾收集器与内存分配策略哈 31 | 32 | ### ④ 对于数据的一致性是怎么保证的 33 | 34 | - 这个如果是我的思路的话,我会谈缓存与数据库的一致性,可以看看我之前这篇文章 35 | 36 | [并发环境下,先操作数据库还是先操作缓存?](https://juejin.im/post/5d4a3196f265da03ab423727) 37 | 38 | - 也可以谈谈分布式事务下的数据一致性,也可以看看之前我的这篇文章 39 | 40 | [后端程序员必备:分布式事务基础篇](https://juejin.im/post/5e47bc14518825491b11bef4) 41 | 42 | ### ⑤ Redis集群有没有了解过,主从和选举是怎么样子的 43 | 这个可以回答这些关键词,主从复制 ,哨兵机制等这些~可以看看网上这篇啦,或者亲爱的读者,去网上看一下资料哈~ 44 | [Redis 主从复制架构和Sentinel哨兵机制](https://aiylqy.com/archives/213.html) 45 | 46 | ### ⑥ 看你们公司使用的是MySQL,你们使用的是哪种存储引擎,为什么?MyISAM和InnoDB的区别 47 | 48 | - MyISAM:如果执行大量的SELECT,MyISAM是更好的选择 49 | - InnoDB:如果你的数据执行大量的INSERT或UPDATE,出于性能方面的考虑,应该使用InnoDB表 50 | - mysiam表不支持外键,而InnoDB支持 51 | 52 | MyISAM适合: 53 | 1. 做很多count 的计算; 54 | 2. 插入不频繁,查询非常频繁; 55 | 3. 没有事务。 56 | 57 | InnoDB适合: 58 | 1. 列表内容 可靠性要求比较高,或者要求事务; 59 | 2. 表更新和查询都相当的频繁,并且行锁定的机会比较大的情况。 60 | 61 | ### ⑦ 索引的底层数据结构是什么,为什么选择这种数据结构 62 | 63 | 可以看看网上的这篇,写得不错~ 64 | [MySQL索引为什么要用B+树实现?](https://mp.weixin.qq.com/s?__biz=MzIxMzk3Mjg5MQ==&mid=2247483916&idx=1&sn=bfc33b53f8176e6f4d7e64c087ad36a4&chksm=97afe0f8a0d869eeaa14d8b26eca9d6fa09f9fda4557b40cb22ebe75851aa4dfb67d822233d9&scene=0&subscene=90&sessionid=1539434820&ascene=7&devicetype=andro) 65 | 66 | ### ⑧SQL优化,怎么判断需要优化,从哪些方面着手优化 67 | 68 | 从索引角度出发,就很多点可以讲, 69 | 这个可以看看我的这两篇文章哈~ 70 | 71 | - [后端程序员必备:书写高质量SQL的30条建议](https://juejin.im/post/5e624d156fb9a07ca80ab6f2) 72 | - [后端程序员必备:索引失效的十大杂症](https://juejin.im/post/5de99dd2518825125e1ba49d) 73 | 74 | ### ⑨ 手写代码:设计一个分布式自增id生成服务 75 | 76 | 可以去网上找一下答案哈,这个我也没什么思路~参考分库分表一些想法?nginx负载均衡一些想法?哈哈,亲爱的读者,如果你会的话,可不可以告诉我呢 77 | 78 | ## 腾讯后端二面: 79 | ### ①有没有了解过网络安全问题,常见的网络攻击有哪些,原理是什么,可以怎么解决 80 | 81 | XSS,跨站脚本攻击?CSRF,跨站请求伪造?DDOS,分布式拒绝服务攻击?SQL注入? 82 | 83 | 对于SQL注入,可以进行后台处理,比如,使用预编译语句PreparedStatement进行预处理,又比如Mybatis映射语句中,用#{xxx}而不是${} 84 | 85 | ### ②平时在开发接口或者设计项目的时候如何保证安全性的 86 | - 签名 87 | - 加密 88 | - ip检测限流? 89 | - 接口幂等 90 | - 特殊字符实现过滤 防止xss、sql注入的攻击? 91 | 92 | ### ③使用Redis集群时可能会存在什么问题 93 | 数据一致性问题 94 | 95 | ### ④有没有了解过cap和base原则 96 | 97 | #### CAP理论 98 | CAP理论作为分布式系统的基础理论,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),这三个要素最多只能同时实现两点。 99 | ![](https://user-gold-cdn.xitu.io/2019/7/20/16c0f6675fee8222?w=302&h=289&f=png&s=25862) 100 | 101 | **一致性(C:Consistency):** 102 | 103 | 一致性是指数据在多个副本之间能否保持一致的特性。例如一个数据在某个分区节点更新之后,在其他分区节点读出来的数据也是更新之后的数据。 104 | 105 | **可用性(A:Availability):** 106 | 107 | 可用性是指系统提供的服务必须一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。这里的重点是"有限时间内"和"返回结果"。 108 | 109 | **分区容错性(P:Partition tolerance):** 110 | 111 | 分布式系统在遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务。 112 | 113 | 选择 | 说明 | 114 | -|-| 115 | CA | 放弃分区容错性,加强一致性和可用性,其实就是传统的单机数据库的选择 | 116 | AP | 放弃一致性,分区容错性和可用性,这是很多分布式系统设计时的选择 | 117 | CP | 放弃可用性,追求一致性和分区容错性,网络问题会直接让整个系统不可用 | 118 | 119 | #### BASE 理论 120 | 121 | BASE 理论, 是对CAP中AP的一个扩展,对于我们的业务系统,我们考虑牺牲一致性来换取系统的可用性和分区容错性。BASE是Basically Available(基本可用),Soft state(软状态),和 Eventually consistent(最终一致性)三个短语的缩写。 122 | 123 | **Basically Available** 124 | 125 | 基本可用:通过支持局部故障而不是系统全局故障来实现的。如将用户分区在 5 个数据库服务器上,一个用户数据库的故障只影响这台特定主机那 20% 的用户,其他用户不受影响。 126 | 127 | **Soft State** 128 | 129 | 软状态,状态可以有一段时间不同步 130 | 131 | **Eventually Consistent** 132 | 133 | 最终一致,最终数据是一致的就可以了,而不是时时保持强一致。 134 | 135 | ### ⑤zk是如何保证一致性的 136 | 可以看这本书哈~ 137 | 《从paxos到Zookeeper分布式一致性原理与实践》, 138 | 139 | 也可以看这篇文章: 140 | [浅析Zookeeper的一致性原理](https://zhuanlan.zhihu.com/p/25594630) 141 | 142 | ### ⑥你如何设计一个能抗住大流量的系统,说说设计方案 143 | 144 | nginx负载均衡,流量防卫兵sentinel,服务拆分,缓存,消息队列,集群、限流、降级这些都可以搬出来啦~ 145 | 146 | ### ⑦有没有了解过缓存策略有哪些 147 | 148 | - Cache-Aside 149 | - Read-Through 150 | - Write-Through 151 | - Write-Behind 152 | 153 | 有兴趣还是可以看看我这篇文章,哈哈 154 | 155 | [并发环境下,先操作数据库还是先操作缓存?](https://juejin.im/post/5d4a3196f265da03ab423727) 156 | 157 | 158 | ## 个人公众号 159 | 160 | ![](https://user-gold-cdn.xitu.io/2019/7/28/16c381c89b127bbb?w=344&h=344&f=jpeg&s=8943) 161 | 162 | - 如果你是个爱学习的好孩子,可以关注我公众号,一起学习讨论。 163 | - 如果你觉得本文有哪些不正确的地方,可以评论,也可以关注我公众号,私聊我,大家一起学习进步哈。 -------------------------------------------------------------------------------- /大厂面试真题/阿里一面,给了几条SQL,问需要执行几次树搜索操作?.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/大厂面试真题/阿里一面,给了几条SQL,问需要执行几次树搜索操作?.md -------------------------------------------------------------------------------- /大厂面试真题/顺丰科技面试真题.md: -------------------------------------------------------------------------------- 1 | ## 一面: 2 | ### ①说说你觉得最有挑战的项目,有哪些难点,你是怎么解决的 3 | ### ②刚才你提到使用缓存,为什么要采用Redis,当初是怎么样技术选型的 4 | ### ③如何保证缓存和数据库一致性 5 | ### ④项目中为什么要使用到ES,ES有什么优势,什么是倒排索引 6 | ### ⑤说说你对垃圾回收的认识 7 | ### ⑥谈谈MySQL的事务 8 | 9 | ## 二面: 10 | ### ①Redis如何实现分布式锁,使用redis分布式锁需要注意什么 11 | ### ②Redis集群用的是什么方式,怎么做动态扩容,原理是什么 12 | ### ③你知道分布式事务吗?有哪些方案,你们公司是采用哪种方案 13 | ### ④怎么做SQL优化 14 | ### ⑤怎么做限流,可以在什么层面上做限流,限流算法有哪些 15 | ### ⑥dubbo的rpc原理 -------------------------------------------------------------------------------- /工作总结/CAS乐观锁解决并发问题的一次实践.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 最近做新项目,货币充值消耗,送礼竞争勋章等都使用了CAS解决并发问题,所以做一下笔记,谈谈CAS,大家一起互相学习。 3 | 4 | ## 乐观锁,悲观锁: 5 | 6 | 讨论CAS的话,先来说有一下乐观锁,悲观锁。 7 | 8 | **悲观锁**:每次去取数据,很悲观,都觉得会被别人修改,所以在拿数据的时候都会上锁。简言之,共享资源每次都只给一个线程使用,其他线程阻塞,等第一个线程用完后再把资源转让给其他线程。synchronized和ReentranLock等都是悲观锁思想的体现。 9 | 10 | **乐观锁**:每次去取数据,都很乐观,觉得不会被被人修改。因此每次都不上锁,但是在更新的时候,就会看别人有没有在这期间去更新这个数据,如果有更新就重新获取,再进行判断,一直循环,直到拿到没有被修改过的数据。CAS(Compare and Swap 比较并交换)就是乐观锁的一种实现方式。 11 | 12 | ## CAS算法: 13 | ### CAS涉及三个操作数 14 | 15 | 1.需要读写的内存地址V 16 | 17 | 2.进行比较的预期原值A 18 | 19 | 3.拟写入的新值B 20 | 21 | 如果内存位置的值V与预期原A值相匹配,那么处理器会自动将该位置值更新为新值B。CAS思想:要进行更新时,认为位置V上的值还是跟A值相等,如果是是相等,就认为它没有被别的线程更改过,即可更新为B值。否则,认为它已经被别的线程修改过,不更新为B的值,返回当前位置V最新的值。 22 | 23 | ### JDK源码中,CAS思想体现: 24 | 反编译Unsafe类(用Java Decompiler工具) 25 | ![](https://user-gold-cdn.xitu.io/2019/6/16/16b60b9ba784610d?w=1319&h=856&f=png&s=54787) 26 | 从源码中可以发现,内部使用自旋的方式进行CAS更新 27 | 28 | ## 业务场景以及CAS的应用: 29 | 假设多人A,B,C等给D送礼,送总价值最多的那个人,可以成为佩带D的守护皇冠,D的守护皇冠有且只有一个。如果他们同时在给D送礼,送礼价值互相超越,即存在并发问题。 30 | 31 | **解决思路:** 参考乐观锁原理 32 | - 设置乐观锁失败后尝试次数n次 33 | - 先查询旧的守护者,即旧的送礼最大价值者。 34 | - 如果当前旧的守护者不为空,构造当前送礼者为新守护者。 35 | - 将新的守护者去跟旧的守护者比较送礼的价值,尝试更新数据库。 36 | - 如果发现更新时,旧的最大送礼价值发生改变了,放弃更新,退出循环,重新尝试(n--)。 37 | - 如果当前旧的守护者为空,表示以前还没有守护,直接将新的守护插入表。 38 | - 如果插入表失败,表示在插入过程中,数据被更改了,表明有新的记录抢先成为守护。 39 | - 那么,重新尝试(n--),直到次数n用完。 40 | 41 | **抢占守护流程图:** 42 | ![](https://user-gold-cdn.xitu.io/2019/6/16/16b60d362b5f3670?w=856&h=940&f=png&s=50030) 43 | 44 | **代码实现:** 45 | 46 | ![](https://user-gold-cdn.xitu.io/2019/6/16/16b60d434c953379?w=1494&h=1033&f=png&s=656168) 47 | 48 | ## CAS存在的一些问题: 49 | ### 1.ABA问题, 50 | 并发环境下,假设初始条件是A,去修改数据时,发现是A就会执行修改。但是看到的虽然是A,中间可能发生了A变B,B又变回A的情况。此时A已经非彼A,数据即使成功修改,也可能有问题。 51 | ### 2.CPU开销 52 | 自旋CAS,如果一直循环执行,一直不成功,会给CPU带来非常大的执行开销。所以上面抢占守护的例子,设置了尝试的执行次数n,避免一直循环 -------------------------------------------------------------------------------- /工作总结/Mysql中,21个写SQL的好习惯.md: -------------------------------------------------------------------------------- 1 | ### 前言 2 | 每一个好习惯都是一笔财富,本文分SQL后悔药, SQL性能优化,SQL规范优雅三个方向,分享写SQL的21个好习惯,谢谢阅读,加油哈~ 3 | 4 | github地址,感谢每颗star 5 | > https://github.com/whx123/JavaHome 6 | 7 | 公众号:**捡田螺的小男孩** 8 | 9 | 10 | ### 1. 写完SQL先explain查看执行计划(SQL性能优化) 11 | 12 | 日常开发写SQL的时候,尽量养成这个好习惯呀:写完SQL后,用explain分析一下,尤其注意走不走索引。 13 | ``` 14 | explain select * from user where userid =10086 or age =18; 15 | ``` 16 | 17 | ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/758773bfbe904d5ba801bc83d81a6bbc~tplv-k3u1fbpfcp-watermark.image) 18 | 19 | 20 | ### 2、操作delete或者update语句,加个limit(SQL后悔药) 21 | 22 | 23 | 在执行删除或者更新语句,尽量加上limit,以下面的这条 SQL 为例吧: 24 | ``` 25 | delete from euser where age > 30 limit 200; 26 | ``` 27 | 28 | 因为加了limit 主要有这些好处: 29 | 30 | ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e14bfc881f21454ca40981b777f56e3e~tplv-k3u1fbpfcp-watermark.image) 31 | 32 | 33 | - **降低写错SQL的代价**, 你在命令行执行这个SQL的时候,如果不加limit,执行的时候一个**不小心手抖**,可能数据全删掉了,如果**删错**了呢?加了limit 200,就不一样了。删错也只是丢失200条数据,可以通过binlog日志快速恢复的。 34 | - **SQL效率很可能更高**,你在SQL行中,加了limit 1,如果第一条就命中目标return, 没有limit的话,还会继续执行扫描表。 35 | - **避免了长事务**,delete执行时,如果age加了索引,MySQL会将所有相关的行加写锁和间隙锁,所有执行相关行会被锁住,如果删除数量大,会直接影响相关业务无法使用。 36 | - **数据量大的话,容易把CPU打满** ,如果你删除数据量很大时,不加 limit限制一下记录数,容易把cpu打满,导致越删越慢的。 37 | 38 | 39 | ### 3. 设计表的时候,所有表和字段都添加相应的注释(SQL规范优雅) 40 | 41 | 这个好习惯一定要养成啦,设计数据库表的时候,所有表和字段都添加相应的注释,后面更容易维护。 42 | 43 | **正例:** 44 | ``` 45 | CREATE TABLE `account` ( 46 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id', 47 | `name` varchar(255) DEFAULT NULL COMMENT '账户名', 48 | `balance` int(11) DEFAULT NULL COMMENT '余额', 49 | `create_time` datetime NOT NULL COMMENT '创建时间', 50 | `update_time` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 51 | PRIMARY KEY (`id`), 52 | KEY `idx_name` (`name`) USING BTREE 53 | ) ENGINE=InnoDB AUTO_INCREMENT=1570068 DEFAULT CHARSET=utf8 ROW_FORMAT=REDUNDANT COMMENT='账户表'; 54 | ``` 55 | 56 | **反例:** 57 | ``` 58 | CREATE TABLE `account` ( 59 | `id` int(11) NOT NULL AUTO_INCREMENT, 60 | `name` varchar(255) DEFAULT NULL, 61 | `balance` int(11) DEFAULT NULL, 62 | `create_time` datetime NOT NULL , 63 | `update_time` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP, 64 | PRIMARY KEY (`id`), 65 | KEY `idx_name` (`name`) USING BTREE 66 | ) ENGINE=InnoDB AUTO_INCREMENT=1570068 DEFAULT CHARSET=utf8; 67 | ``` 68 | 69 | ### 4. SQL书写格式,关键字大小保持一致,使用缩进。(SQL规范优雅) 70 | 71 | 正例: 72 | ``` 73 | SELECT stu.name, sum(stu.score) 74 | FROM Student stu 75 | WHERE stu.classNo = '1班' 76 | GROUP BY stu.name 77 | ``` 78 | 79 | 反例: 80 | ``` 81 | SELECT stu.name, sum(stu.score) from Student stu WHERE stu.classNo = '1班' group by stu.name. 82 | ``` 83 | 显然,统一关键字大小写一致,使用缩进对齐,会使你的SQL看起来更优雅~ 84 | 85 | ### 5. INSERT语句标明对应的字段名称(SQL规范优雅) 86 | 87 | 反例: 88 | ``` 89 | insert into Student values ('666','捡田螺的小男孩','100'); 90 | ``` 91 | 正例: 92 | ``` 93 | insert into Student(student_id,name,score) values ('666','捡田螺的小男孩','100'); 94 | ``` 95 | 96 | ### 6. 变更SQL操作先在测试环境执行,写明详细的操作步骤以及回滚方案,并在上生产前review。(SQL后悔药) 97 | 98 | - 变更SQL操作先在测试环境测试,避免有语法错误就放到生产上了。 99 | - 变更Sql操作需要写明详细操作步骤,尤其有依赖关系的时候,如:先修改表结构再补充对应的数据。 100 | - 变更Sql操作有回滚方案,并在上生产前,review对应变更SQL。 101 | 102 | ### 7.设计数据库表的时候,加上三个字段:主键,create_time,update_time。(SQL规范优雅) 103 | 104 | 105 | 反例: 106 | ``` 107 | CREATE TABLE `account` ( 108 | `name` varchar(255) DEFAULT NULL COMMENT '账户名', 109 | `balance` int(11) DEFAULT NULL COMMENT '余额', 110 | ) ENGINE=InnoDB AUTO_INCREMENT=1570068 DEFAULT CHARSET=utf8 ROW_FORMAT=REDUNDANT COMMENT='账户表'; 111 | ``` 112 | 正例: 113 | ``` 114 | CREATE TABLE `account` ( 115 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id', 116 | `name` varchar(255) DEFAULT NULL COMMENT '账户名', 117 | `balance` int(11) DEFAULT NULL COMMENT '余额', 118 | `create_time` datetime NOT NULL COMMENT '创建时间', 119 | `update_time` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 120 | PRIMARY KEY (`id`), 121 | KEY `idx_name` (`name`) USING BTREE 122 | ) ENGINE=InnoDB AUTO_INCREMENT=1570068 DEFAULT CHARSET=utf8 ROW_FORMAT=REDUNDANT COMMENT='账户表'; 123 | ``` 124 | 理由: 125 | - 主键一定要加上的,没有主键的表是没有灵魂的 126 | - 创建时间和更新时间的话,还是建议加上吧,详细审计、跟踪记录,都是有用的。 127 | 128 | 阿里开发手册也提到这个点,如图 129 | ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8a71648ba03c4f0fa6772a3c03cd99f2~tplv-k3u1fbpfcp-watermark.image) 130 | 131 | 132 | ### 8. 写完SQL语句,检查where,order by,group by后面的列,多表关联的列是否已加索引,优先考虑组合索引。(SQL性能优化) 133 | 134 | 反例: 135 | ``` 136 | select * from user where address ='深圳' order by age ; 137 | ``` 138 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1320d5f64b2640ff8221251505cacf14~tplv-k3u1fbpfcp-watermark.image) 139 | 140 | 正例: 141 | ``` 142 | 添加索引 143 | alter table user add index idx_address_age (address,age) 144 | ``` 145 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/480d9d01a71c40228696fd22850e111d~tplv-k3u1fbpfcp-watermark.image) 146 | 147 | ### 9.修改或删除重要数据前,要先备份,先备份,先备份(SQL后悔药) 148 | 149 | 如果要修改或删除数据,在执行SQL前一定要先备份要修改的数据,万一误操作,还能吃口**后悔药**~ 150 | 151 | ### 10. where后面的字段,留意其数据类型的隐式转换(SQL性能优化) 152 | 153 | **反例:** 154 | ``` 155 | //userid 是varchar字符串类型 156 | select * from user where userid =123; 157 | ``` 158 | ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/17ad6b34e5a646028d9a086fd7ef7db0~tplv-k3u1fbpfcp-watermark.image) 159 | 160 | **正例:** 161 | ``` 162 | select * from user where userid ='123'; 163 | ``` 164 | ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4bae5bd0b3564484b18e6f145ff65fd6~tplv-k3u1fbpfcp-watermark.image) 165 | 166 | **理由:** 167 | - 因为不加单引号时,是字符串跟数字的比较,它们类型不匹配,MySQL会做隐式的类型转换,把它们转换为浮点数再做比较,最后导致索引失效 168 | 169 | 170 | ### 11. 尽量把所有列定义为NOT NULL(SQL规范优雅) 171 | 172 | - **NOT NULL列更节省空间**,NULL列需要一个额外字节作为判断是否为 NULL 的标志位。 173 | - **NULL列需要注意空指针问题**,NULL列在计算和比较的时候,需要注意空指针问题。 174 | 175 | 176 | ### 12.修改或者删除SQL,先写WHERE查一下,确认后再补充 delete 或 update(SQL后悔药) 177 | 178 | 尤其在操作生产的数据时,遇到修改或者删除的SQL,先加个where查询一下,确认OK之后,再执行update或者delete操作 179 | 180 | ### 13.减少不必要的字段返回,如使用select <具体字段> 代替 select * (SQL性能优化) 181 | 182 | 反例: 183 | ``` 184 | select * from employee; 185 | ``` 186 | 正例: 187 | ``` 188 | select id,name from employee; 189 | ``` 190 | 理由: 191 | 192 | - 节省资源、减少网络开销。 193 | - 可能用到覆盖索引,减少回表,提高查询效率。 194 | 195 | 196 | ### 14.所有表必须使用Innodb存储引擎(SQL规范优雅) 197 | 198 | Innodb 支持事务,支持行级锁,更好的恢复性,高并发下性能更好,所以呢,没有特殊要求(即Innodb无法满足的功能如:列存储,存储空间数据等)的情况下,所有表必须使用Innodb存储引擎 199 | 200 | ### 15.数据库和表的字符集统一使用UTF8(SQL规范优雅) 201 | 202 | 统一使用UTF8编码 203 | - 可以避免乱码问题 204 | - 可以避免,不同字符集比较转换,导致的索引失效问题 205 | 206 | 如果是存储表情的,可以考虑 utf8mb4 207 | 208 | ### 16. 尽量使用varchar代替 char。(SQL性能优化) 209 | 210 | 反例: 211 | ``` 212 | `deptName` char(100) DEFAULT NULL COMMENT '部门名称' 213 | ``` 214 | 正例: 215 | ``` 216 | `deptName` varchar(100) DEFAULT NULL COMMENT '部门名称' 217 | ``` 218 | 理由: 219 | - 因为首先变长字段存储空间小,可以节省存储空间。 220 | 221 | 222 | ### 17. 如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。 (SQL规范优雅) 223 | 224 | 这个点,是阿里开发手册中,Mysql的规约。你的字段,尤其是表示枚举状态时,如果含义被修改了,或者状态追加时,为了后面更好维护,需要即时更新字段的注释。 225 | 226 | 227 | ### 18. SQL修改数据,养成begin + commit 事务的习惯;(SQL后悔药) 228 | 正例: 229 | ``` 230 | begin; 231 | update account set balance =1000000 232 | where name ='捡田螺的小男孩'; 233 | commit; 234 | ``` 235 | 反例: 236 | ``` 237 | update account set balance =1000000 238 | where name ='捡田螺的小男孩'; 239 | ``` 240 | 241 | 242 | ### 19. 索引命名要规范,主键索引名为 pk_ 字段名;唯一索引名为 uk _字段名 ; 普通索引名则为 idx _字段名。(SQL规范优雅) 243 | 244 | 说明: pk_ 即 primary key;uk _ 即 unique key;idx _ 即 index 的简称。 245 | 246 | 247 | ### 20. WHERE从句中不对列进行函数转换和表达式计算 248 | 249 | 假设loginTime加了索引 250 | 251 | **反例:** 252 | ``` 253 | select userId,loginTime from loginuser where Date_ADD(loginTime,Interval 7 DAY) >=now(); 254 | ``` 255 | **正例:** 256 | ``` 257 | explain select userId,loginTime from loginuser where loginTime >= Date_ADD(NOW(),INTERVAL - 7 DAY); 258 | ``` 259 | **理由:** 260 | - 索引列上使用mysql的内置函数,索引失效 261 | ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/07bd6e743d974638a70c2db91c6cc9f2~tplv-k3u1fbpfcp-watermark.image) 262 | 263 | 264 | 265 | ### 21.如果修改\更新数据过多,考虑批量进行。 266 | 267 | 反例: 268 | ``` 269 | delete from account limit 100000; 270 | ``` 271 | 正例: 272 | ``` 273 | for each(200次) 274 | { 275 | delete from account limit 500; 276 | } 277 | ``` 278 | 279 | 理由: 280 | - 大批量操作会会造成主从延迟。 281 | - 大批量操作会产生大事务,阻塞。 282 | - 大批量操作,数据量过大,会把cpu打满。 283 | 284 | ### 参考与感谢 285 | - [delete后加 limit是个好习惯么](https://blog.csdn.net/qq_39390545/article/details/107519747) 286 | - 《阿里开发手册》 287 | 288 | ### 公众号 289 | 后端技术栈公众号:捡田螺的小男孩 290 | 291 | 292 | 293 | 294 | 295 | -------------------------------------------------------------------------------- /工作总结/READEME.MD: -------------------------------------------------------------------------------- 1 | ## 工作总结 2 | 3 | - [工作总结!日志打印的15个建议](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494838&idx=1&sn=cdb15fd346bddf3f8c1c99f0efbd67d8&chksm=cf22339ff855ba891616c79d4f4855e228e34a9fb45088d7acbe421ad511b8d090a90f5b019f&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 4 | - [25种代码坏味道总结+优化示例](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490148&idx=1&sn=00a181bf74313f751b3ea15ebc303545&chksm=cf21c54df8564c5bc5b4600fce46619f175f7ae557956f449629c470a08e20580feef4ea8d53&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 5 | - [聊聊日常开发中,如何减少bug呢?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490662&idx=1&sn=d38a090611af7f64ee3c6a31331d5228&chksm=cf21c34ff8564a59e505e6edf3065a0fc506c6d2c96f492c8d8873cd46dedbe0704e43cb9c2e&token=1990771297&lang=zh_CN#rd) 6 | - [工作四年,分享50个让你代码更好的小建议](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488708&idx=1&sn=6e2e0a740f5d42a59641487a0bf1e3bf&chksm=cf21cbedf85642fbb485fa1c7bf9af21923d8503f2542b6f8283ce79ddc683f7d9e45da83100&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 7 | - [写代码有这16个好习惯,可以减少80%非业务的bug](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488097&idx=1&sn=eaca1f92ca3ccd9de00dbc4ef3e4029a&chksm=cf21cd48f856445e4cc24c1f8bcf18d1479bad0a37a87a2fb70717d8a4e65dcf7b4d5f83d24f&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 8 | - [Java日常开发的21个坑,你踩过几个?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488115&idx=1&sn=bdd4a4ca36bc7ea902106d058e8537fb&chksm=cf21cd5af856444cb36af600705615454b0aaa2b289b97ddb52d594556ac07a1915b73ecce19&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 9 | - [CAS乐观锁解决并发问题的一次实践](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487937&idx=1&sn=206a37bf6d6a7aa1d05674c479ed7a72&chksm=cf21cee8f85647fe7a082049a41c0f640f54976d2cdf4302b24c5517ca42b854eb84b13ece10&token=1990771297&lang=zh_CN#rd) 10 | - [写代码有这些想法,同事才不会认为你是复制粘贴程序员](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487961&idx=1&sn=e646231067968d9f58e6665914293f9a&chksm=cf21cef0f85647e6f3ff2feece004ac3bd979e37fe45103c88d0f299dfe632a5cf6dd547c1d9&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 11 | - [程序员必备:Java日期处理的十个坑](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487973&idx=1&sn=0f713413098fb579e5f200b829f71e89&chksm=cf21ceccf85647da450765d79bf5943da551c3be950447063b9f8c77c21bf2a39b99387a949b&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 12 | - [内存泄漏问题的分析和解决方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487986&idx=1&sn=d681a585ac489703788e3baa48eb9aa3&chksm=cf21cedbf85647cd23bbab9dfec63e6877f83c34efb19bd16075d5d90fea91d3f4a20fc77921&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 13 | - [程序员必备基础:加签验签](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488022&idx=1&sn=70484a48173d36006c8db1dfb74ab64d&chksm=cf21cd3ff8564429a1205f6c1d78757faae543111c8461d16c71aaee092fe3e0fed870cc5e0e&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 14 | - [记一次接口性能优化实践总结:优化接口性能的八个建议](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488004&idx=1&sn=00840efd9c0bd0a7f172b59eb2ca130f&chksm=cf21cd2df856443bf21d8e09cfe5c8452ecaf82e3c2210fca3b28829ded04defddcf63c0a59b&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 15 | - [程序员必备基础:如何安全传输存储用户密码?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488117&idx=1&sn=5d3d0eda0ed45f3f576e211de31ca3a9&chksm=cf21cd5cf856444af1407a94a2abf445265ca7c5f5855cfa1c223cb209e99040c7889621f231&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 16 | - [一次代码优化实践,用了模板方法+策略+工厂方法模式](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488061&idx=1&sn=1d9ab7954b03521ab81ecf033c0e5e50&chksm=cf21cd14f8564402b213f0ef908bbdb0e12fed4b281c5803b8e539cacb1551654194becfb7d6&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 17 | - [保证接口数据安全的10种方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247500285&idx=1&sn=7d0723f25d46e858859cfd79acb6fb9d&chksm=cf221ed4f85597c2093f81baa5fdedc65817bf2d23a7951236836b0f54c2335695cbed61cd13&token=1990771297&lang=zh_CN#rd) -------------------------------------------------------------------------------- /工作总结/一次代码优化.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 好久没分享工作总结啦,今天来一份代码优化总结。用模板方法+策略+工厂方法模式优化了代码,耐心点看完,应该对大家有帮助的~ 3 | 4 | 本文已经收录到github 5 | > https://github.com/whx123/JavaHome 6 | 7 | **公众号:捡田螺的小男孩** 8 | 9 | ### 优化代码前 10 | 先来了解一下类似的业务场景,简言之,就是:多个商户接入我们系统,都是走一个类似的流程通过http请求出去的。 11 | ![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b5ad1d5b5f57469894ecc83df77d68d9~tplv-k3u1fbpfcp-zoom-1.image) 12 | 13 | 优化前,每个公司对应一个句柄服务,伪代码如下: 14 | ``` 15 | // 商户A处理句柄 16 | CompanyAHandler implements RequestHandler { 17 | Resp hander(req){ 18 | //查询商户信息 19 | queryMerchantInfo(); 20 | //加签 21 | signature(); 22 | // http请求(走代理) 23 | httpRequestbyProxy() 24 | // 验签 25 | verify(); 26 | } 27 | } 28 | // 商户B处理句柄 29 | CompanyBHandler implements RequestHandler { 30 | Resp hander(Rreq){ 31 | //查询商户信息 32 | queryMerchantInfo(); 33 | //加签 34 | signature(); 35 | // http请求(不走代理) 36 | httpRequestbyDirect(); 37 | // 验签 38 | verify(); 39 | } 40 | } 41 | // 商户C处理句柄 42 | CompanyBHandler implements RequestHandler { 43 | Resp hander(Rreq){ 44 | //查询商户信息 45 | queryMerchantInfo(); 46 | // webservice 方式调用 47 | requestByWebservice(); 48 | } 49 | } 50 | ``` 51 | ### 优化代码思路 52 | 我的优化代码思路,是有**重复代码,先把它抽出来,或者公用变量,或者公用方法,伸着公用类**。所以呢,查询商户信息呀,加签呀,http请求呀先全部各抽成一个公用方法。你细心点会发现,连每个Handler处理过程都很类似的,大概都是查询商户信息+加签+http请求+验签,于是呢,可以直接把它们抽象成一个公用类呀~在这里就要引入模板方法模式咯 53 | 54 | #### 模板方法模式 55 | ``` 56 | 在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 57 | 这种类型的设计模式属于行为型模式。 58 | ``` 59 | 既然每个Handler处理,都是类似的流程,那**定义一个抽象类,把查询商户信息,加签,http请求,验签什么的,都放到里面去,俨然一个模板一样**。然后,因为有些商户走http代理,有些又没走代理,怎么办呢? 定义**一个抽象方法,给子类实现**嘛,因为能共用就放到父类(当前的抽象类),不能共用就放到子类嘛~代码如下: 60 | ``` 61 | abstract class AbstractCompanyCommonService implements ICompanyCommonService { 62 | //模板方法 63 | Resp handlerTempPlate(req){ 64 | //查询商户信息 65 | queryMerchantInfo(); 66 | // 加签 67 | signature(); 68 | //http 请求 69 | if(isRequestByProxy()){ 70 | httpProxy(); 71 | }else{ 72 | httpDirect(); 73 | } 74 | // 验签 75 | verifySinature(); 76 | } 77 | // Http是否走代理 78 | abstract boolean isRequestByProxy(); 79 | } 80 | ``` 81 | 82 | 子类商户A实现: 83 | ``` 84 | CompanyAServiceImpl extends AbstractCompanyCommonService{ 85 | Resp hander(req){ 86 | return handlerTempPlate(req); 87 | } 88 | //公司A是走代理的 89 | boolean isRequestByProxy(){ 90 | return true; 91 | } 92 | ``` 93 | 子类商户B实现: 94 | ``` 95 | CompanyBServiceImpl extends AbstractCompanyCommonService{ 96 | Resp hander(req){ 97 | return handlerTempPlate(req); 98 | } 99 | //公司B是不走代理的 100 | boolean isRequestByProxy(){ 101 | return false; 102 | } 103 | ``` 104 | 105 | #### 策略模式 106 | 心细的读者会发现,甚至提出疑问,**你的商户C的服务实现跟你定义的公用模板,不太一样呢**,那当然,实际开发中,不跟你定义的模板一样太常见了,需求是产品提的嘛,又不是根据你模板提的,是代码服务于需求的。好了,不多说啦,我使用了策略模式,来优化这个问题。 107 | ``` 108 | 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 109 | ``` 110 | 策略模式理解起来其好抽象对不对?我个人理解,其实策略模式就是定义一个方法(所谓算法),给子类自己去实现。实际上就是**定义个方法/接口,让子类自己去实现**。看代码吧: 111 | ``` 112 | // 定义一个方法,把策略交给子类去实现。 113 | interface ICompanyCommonService{ 114 | Resp hander(req); 115 | } 116 | ``` 117 | 前面商户A和商户B还是不变,使用抽象类AbstractCompanyCommonService的模板,模板不满足商户C,商户C只能自己去实现咯,各个子类自己去实现的行为,就是策略模式的体现呢,如下: 118 | 119 | ``` 120 | CompanyCServiceImpl extends AbstractCompanyCommonService{ 121 | Res hander(req){ 122 | //查询商户信息 123 | queryMerchantInfo(); 124 | requestByWebservice(); 125 | } 126 | //随意了,你都不走模板了 127 | boolean isRequestByProxy(){ 128 | return false; 129 | } 130 | ``` 131 | 132 | #### 工厂方法模式 133 | 商户A、B、C服务怎么被管理呢,之前分别给A,B,C服务实现handler的,现在好了,都不知道怎么管理了,怎么知道调用哪个呢?别慌,解决方案是**工厂方法模式**。 134 | ``` 135 | 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。 136 | ``` 137 | 工厂方法模式具体实现就是:接口定义一个枚举,每个服务实现都重新实现枚举,设置唯一标志枚举,再交给spring容器管理。看代码咯: 138 | ``` 139 | interface ICompanyCommonService{ 140 | Resp hander(req); 141 | CompanyEnum getCompanyEnum(); 142 | } 143 | 144 | CompanyAServiceImpl extends AbstractCompanyCommonService{ 145 | Resp hander(req){ 146 | return handlerTempPlate(req); 147 | } 148 | //公司A是走代理的 149 | boolean isRequestByProxy(){ 150 | return true; 151 | } 152 | CompanyEnum getCompanyEnum(){ 153 | return CompanyEnum.A; 154 | } 155 | 156 | CompanyBServiceImpl extends AbstractCompanyCommonService{ 157 | Resp hander(req){ 158 | return handlerTempPlate(req); 159 | } 160 | //公司B是不走代理的 161 | boolean isRequestByProxy(){ 162 | return false; 163 | } 164 | CompanyEnum getCompanyEnum(){ 165 | return CompanyEnum.B; 166 | } 167 | ``` 168 | 来来来,工厂方法模式出炉咯: 169 | ``` 170 | @Component 171 | public class CompanyServiceFactory implements ApplicationContextAware { 172 | 173 | private static Map map = new HashMap<>(); 174 | 175 | @Override 176 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 177 | Map tempMap = applicationContext.getBeansOfType(ICompanyCommonService.class); 178 | tempMap.values().forEach(iCompanyCommonService -> 179 | map.put(iCompanyCommonService.getCompanyEnum(), iCompanyCommonService)); 180 | } 181 | 182 | public Resp handler(req) { 183 | return map.get(CompanyEnum.getCompanyEnum(req.getCompanyFlag()).hander(req); 184 | } 185 | } 186 | ``` 187 | ### 最后建议 188 | 最后,不要为了使用设计模式生搬硬套,而是优化代码过程中,发现这个设计模式刚好适用,才去用的哈。附上最后的代码咯: 189 | ``` 190 | @Service 191 | public class CompanyHandler implements RequestHandler { 192 | @Autowire 193 | private CompanyServiceFactory companyServiceFactory; 194 | 195 | Resp hander(req){ 196 | return companyServiceFactory.handler(req); 197 | } 198 | } 199 | ``` 200 | 201 | ### 个人公众号 202 | 203 | ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3e531344ab1a417188a4d8ece4148c64~tplv-k3u1fbpfcp-zoom-1.image) 204 | 205 | -------------------------------------------------------------------------------- /工作总结/优化代码的几个小技巧.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 最近看了《重构-改善既有代码的设计》这本书,总结了优化代码的几个小技巧,给大家分享一下。 3 | ## 提炼函数(适当抽取小函数) 4 | 5 | ### 定义 6 | **提炼函数**就是将一段代码放进一个独立函数中,并让函数名称解释该函数用途。 7 | 8 | 一个过于冗长的函数或者一段需要注释才能让人理解用途的代码,可以考虑把它切分成一个**功能明确的函数单元**,并定义**清晰简短的函数名**,这样会让代码变得更加优雅。 9 | 10 | 11 | ### 优化例子 12 | **提炼函数之前:** 13 | ``` 14 | private String name; 15 | private Vector orders = new Vector(); 16 | 17 | public void printOwing() { 18 | //print banner 19 | System.out.println("****************"); 20 | System.out.println("*****customer Owes *****"); 21 | System.out.println("****************"); 22 | 23 | //calculate totalAmount 24 | Enumeration env = orders.elements(); 25 | double totalAmount = 0.0; 26 | while (env.hasMoreElements()) { 27 | Order order = (Order) env.nextElement(); 28 | totalAmount += order.getAmout(); 29 | } 30 | 31 | //print details 32 | System.out.println("name:" + name); 33 | System.out.println("amount:" + totalAmount); 34 | } 35 | ``` 36 | **提炼函数之后:** 37 | 38 | 以上那段代码,可以抽成**print banner,calculate totalAmount,print details三个功能**的单一函数,如下: 39 | ``` 40 | private String name; 41 | private Vector orders = new Vector(); 42 | 43 | public void printOwing() { 44 | 45 | //print banner 46 | printBanner(); 47 | //calculate totalAmount 48 | double totalAmount = getTotalAmount(); 49 | //print details 50 | printDetail(totalAmount); 51 | } 52 | 53 | void printBanner(){ 54 | System.out.println("****************"); 55 | System.out.println("*****customer Owes *****"); 56 | System.out.println("****************"); 57 | } 58 | 59 | double getTotalAmount(){ 60 | Enumeration env = orders.elements(); 61 | double totalAmount = 0.0; 62 | while (env.hasMoreElements()) { 63 | Order order = (Order) env.nextElement(); 64 | totalAmount += order.getAmout(); 65 | } 66 | return totalAmount; 67 | } 68 | 69 | void printDetail(double totalAmount){ 70 | System.out.println("name:" + name); 71 | System.out.println("amount:" + totalAmount); 72 | } 73 | 74 | ``` 75 | 76 | ## 内联函数(适当去除多余函数) 77 | ### 定义 78 | **内联函数**就是在函数调用点插入函数本体,然后移除该函数。 79 | 80 | 上一小节介绍了**提炼函数**代码优化方式,以**简短清晰的小函数**为荣。但是呢,小函数是不是越多越好呢?肯定不是啦,有时候你会遇到某些函数,其内部代码和函数名称同样清晰,这时候呢你可以考虑**内联函数**优化一下了。 81 | 82 | ### 优化例子 83 | **内联函数之前** 84 | ``` 85 | int getRating(){ 86 | return moreThanFiveDeliveries() ? 2 : 1; 87 | } 88 | boolean moreThanFiveDeliveries(){ 89 | return numberOfLateDeliveries >5; 90 | } 91 | ``` 92 | **内联函数之后** 93 | ``` 94 | int getRating(){ 95 | return numberOfLateDeliveries >5 ? 2 : 1; 96 | } 97 | ``` 98 | 99 | ## 内联临时变量(去除多余临时变量) 100 | 101 | ### 定义 102 | **内联临时变量**将所有对该变量的引用动作,替换为对它赋值的那个表达式自身。 103 | 104 | ### 优化例子 105 | **内联临时变量之前** 106 | ``` 107 | double basePice = anOrder.basePrice(); 108 | return basePice >888; 109 | ``` 110 | **内联临时变量之后** 111 | ``` 112 | return anOrder.basePrice() >888; 113 | ``` 114 | 115 | ## 引入解释性变量 116 | 117 | ### 定义 118 | **引入解释性变量** 就是将该复杂表达式(或其中一部分)的结果放进一个临时变量,以此变量名称来解释表达式用途。 119 | 120 | 有些表达式可能非常复杂难于阅读,在这种情况下,临时变量可以帮助你将表达式分解为可读的形式。 121 | 122 | 在比较复杂的条件逻辑中,你可以用**引入解释性变量**将每个条件子句提炼出来,以一个良好命名的临时变量来解释对应条件子句的意义。 123 | 124 | ### 优化例子 125 | **引入解释性变量之前** 126 | ``` 127 | if ((platform.toUpperCase().indexOf("mac") > -1) && 128 | (brower.toUpperCase().indexOf("ie") > -1) && 129 | wasInitializes() && resize > 0) { 130 | ...... 131 | } 132 | ``` 133 | 134 | **引入解释性变量之后** 135 | ``` 136 | final boolean isMacOS = platform.toUpperCase().indexOf("mac") > -1; 137 | final boolean isIEBrowser = brower.toUpperCase().indexOf("ie") > -1; 138 | final boolean wasResized = resize > 0; 139 | 140 | if (isMacOS && isIEBrowser && wasInitializes() && wasResized) { 141 | ...... 142 | } 143 | ``` 144 | 145 | ## 以字面常量取代魔法数 146 | ### 定义 147 | 创造一个常量,根据其意义为它命名,并将上述的字面数值替换为这个常量。 148 | 149 | 所谓魔法数是指拥有特殊意义,却又不能明确表现出这种意义的数字。如果你需要在**不同的地点引用同一个逻辑数**,每当该数字要修改时,会特别头疼,因为很可能会改漏。而**字面常量取代魔法数**可以解决这个头疼问题。 150 | 151 | ### 优化例子 152 | **以字面常量取代魔法数之前** 153 | ``` 154 | double getDiscountPrice(double price){ 155 | return price * 0.88; 156 | } 157 | ``` 158 | **以字面常量取代魔法数之后** 159 | ``` 160 | static final double DISCOUNT_CONSTANT=0.88; 161 | 162 | double getDiscountPrice(double price){ 163 | return price * DISCOUNT_CONSTANT; 164 | } 165 | ``` 166 | 167 | 168 | ## 用多态替代switch语句 169 | ### 定义 170 | **用多态替换switch语句** 就是利用Java面向对象的多态特点,使用state模式来替换switch语句。 171 | 172 | ### 优化例子 173 | **用多态替换switch语句之前** 174 | ``` 175 | int getArea() { 176 | switch (shape){ 177 | case SHAPE.CIRCLE: 178 | return 3.14 * _r * _r; break; 179 | case SHAPE.RECTANGEL; 180 | return width *,heigth; 181 | } 182 | } 183 | ``` 184 | **用多态替换switch语句之后** 185 | ``` 186 | class Shape { 187 | int getArea(){}; 188 | } 189 | 190 | class Circle extends Shape { 191 | int getArea() { 192 | return 3.14 * r * r; 193 | } 194 | } 195 | 196 | class Rectangel extends Shape { 197 | int getArea() { 198 | return width * heigth; 199 | } 200 | } 201 | ``` 202 | 203 | ## 将过多的参数对象化 204 | ### 定义 205 | **将过多的参数对象化**就是把涉及过多参数封装成一个对象传参。 206 | 207 | 一个方法有太多传参时,即难以阅读又难于维护。尤其针对dubbo远程调用这些方法,如果有过多参数,增加或者减少一个参数,都要修改接口,真的坑。如果把这些参数封装成一个对象,就很好维护了,不用修改接口。 208 | 209 | 210 | 211 | ### 优化例子 212 | **将过多的参数对象化之前:** 213 | ``` 214 | public int register(String username,String password,Integer age,String phone); 215 | ``` 216 | **将过多的参数对象化之后:** 217 | ``` 218 | public int register(RegisterForm from ); 219 | 220 | class RegisterForm{ 221 | private String username; 222 | private String password; 223 | private Integer age; 224 | private String phone; 225 | } 226 | ``` 227 | 228 | ## 参考与感谢 229 | 230 | - 《重构-改善既有代码的设计》 231 | 232 | 233 | ## 个人公众号 234 | 235 | ![](https://user-gold-cdn.xitu.io/2019/7/28/16c381c89b127bbb?w=344&h=344&f=jpeg&s=8943) 236 | 237 | - 如果你是个爱学习的好孩子,可以关注我公众号,一起学习讨论。 238 | - 如果你觉得本文有哪些不正确的地方,可以评论,也可以关注我公众号,私聊我,大家一起学习进步哈。 -------------------------------------------------------------------------------- /工作总结/写代码有这16个习惯,规避80%的bug.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/工作总结/写代码有这16个习惯,规避80%的bug.md -------------------------------------------------------------------------------- /工作总结/工作四年,50个让你代码更好的小建议.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/工作总结/工作四年,50个让你代码更好的小建议.md -------------------------------------------------------------------------------- /工作总结/并发环境下,先操作数据库还是先操作缓存?.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 在分布式系统中,缓存和数据库同时存在时,如果有写操作的时候,先操作数据库还是先操作缓存呢?先思考一下,可能会存在哪些问题,再往下看。下面我分几种方案阐述。 3 | 4 | ## 缓存维护方案一 5 | 假设有一写(线程A)一读(线程B)操作,**先操作缓存,在操作数据库**。,如下流程图所示: 6 | ![](https://user-gold-cdn.xitu.io/2019/7/19/16c09e4c5c718c7a?w=849&h=513&f=png&s=52606) 7 | 8 | 1)线程A发起一个写操作,第一步del cache 9 | 10 | 2)线程A第二步写入新数据到DB 11 | 12 | 3)线程B发起一个读操作,cache miss, 13 | 14 | 4)线程B从DB获取最新数据 15 | 16 | 5)请求B同时set cache 17 | 18 | **这样看,没啥问题**。我们再看第二个流程图,如下: 19 | 20 | ![](https://user-gold-cdn.xitu.io/2019/7/19/16c09e12153a79b8?w=803&h=517&f=png&s=52726) 21 | 22 | 1)线程A发起一个写操作,第一步del cache 23 | 24 | 2)此时线程B发起一个读操作,cache miss 25 | 26 | 3)线程B继续读DB,读出来一个老数据 27 | 28 | 4)然后老数据入cache 29 | 30 | 5)线程A写入了最新的数据 31 | 32 | OK,酱紫,就有问题了吧,老数据入到缓存了,**每次读都是老数据啦,缓存与数据与数据库数据不一致**。 33 | 34 | ## 缓存维护方案二 35 | 双写操作,**先操作缓存,在操作数据库**。 36 | 37 | ![](https://user-gold-cdn.xitu.io/2019/7/19/16c09f09e4c1c292?w=610&h=512&f=png&s=45931) 38 | 39 | 1)线程A发起一个写操作,第一步set cache 40 | 41 | 2)线程A第二步写入新数据到DB 42 | 43 | 3)线程B发起一个写操作,set cache, 44 | 45 | 4)线程B第二步写入新数据到DB 46 | 47 | **这样看,也没啥问题。**,但是有时候可能事与愿违,我们再看第二个流程图,如下: 48 | 49 | ![](https://user-gold-cdn.xitu.io/2019/7/20/16c0eb335ee9b878?w=1001&h=676&f=png&s=71130) 50 | 51 | 1)线程A发起一个写操作,第一步set cache 52 | 53 | 2)线程B发起一个写操作,第一步setcache 54 | 55 | 3)线程B写入数据库到DB 56 | 57 | 4)线程A写入数据库到DB 58 | 59 | 执行完后,缓存保存的是B操作后的数据,数据库是A操作后的数据,**缓存和数据库数据不一致**。 60 | 61 | ## 缓存维护方案三 62 | 63 | 一写(线程A)一读(线程B)操作,**先操作数据库,再操作缓存**。 64 | 65 | ![](https://user-gold-cdn.xitu.io/2019/7/20/16c0ec1c874c19eb?w=1076&h=637&f=png&s=79727) 66 | 67 | 1)线程A发起一个写操作,第一步write DB 68 | 69 | 2)线程A第二步del cache 70 | 71 | 3)线程B发起一个读操作,cache miss 72 | 73 | 4)线程B从DB获取最新数据 74 | 75 | 5)线程B同时set cache 76 | 77 | 这种方案**没有明显的并发问题**,但是有可能**步骤二删除缓存失败**,虽然概率比较小,**优于方案一和方案二**,平时工作中也是使用方案三。 78 | 79 | 综上对比,我们一般采用方案三,但是有没有完美全解决方案三的弊端的方法呢? 80 | 81 | ## 缓存维护方案四 82 | 这个是方案三的改进方案,都是先操作数据库再操作缓存,我们来看一下流程图: 83 | ![](https://user-gold-cdn.xitu.io/2019/7/20/16c0f4116f0a062f?w=995&h=778&f=png&s=88742) 84 | 85 | 通过数据库的**binlog**来**异步淘汰key**,以mysql为例 86 | 可以**使用阿里的canal将binlog日志采集发送到MQ队列**里面,然后**通过ACK机制 87 | 确认处理** 这条更新消息,删除缓存,保证数据缓存一致性。 88 | 89 | 但是呢还有个**问题,如果是主从数据库呢**? 90 | 91 | ## 缓存维护方案五 92 | 93 | 主从DB问题:因为主从DB同步存在同时延时时间如果删除缓存之后,数据同步到备库之前已经有请求过来时,**会从备库中读到脏数据**,如何解决呢?解决方案如下流程图: 94 | 95 | ![](https://user-gold-cdn.xitu.io/2019/7/20/16c0f481dee559a5?w=1019&h=908&f=png&s=109517) 96 | 97 | ## 缓存维护总结 98 | 综上所述,在分布式系统中,缓存和数据库同时存在时,如果有写操作的时候,**先操作数据库,再操作缓存**。如下: 99 | 100 | (1)读取缓存中是否有相关数据 101 | 102 | (2)如果缓存中有相关数据value,则返回 103 | 104 | (3)如果缓存中没有相关数据,则从数据库读取相关数据放入缓存中key->value,再返回 105 | 106 | (4)如果有更新数据,则先更新数据,再删除缓存 107 | 108 | (5)为了保证第四步删除缓存成功,使用binlog异步删除 109 | 110 | (6)如果是主从数据库,binglog取自于从库 111 | 112 | (7)如果是一主多从,每个从库都要采集binlog,然后消费端收到最后一台binlog数据才删除缓存 113 | 114 | ## 个人公众号 115 | 116 | ![](https://user-gold-cdn.xitu.io/2019/7/28/16c381c89b127bbb?w=344&h=344&f=jpeg&s=8943) 117 | 118 | - 欢迎大家关注,大家一起学习,一起讨论哈。 119 | - github地址:https://github.com/whx123/JavaHome -------------------------------------------------------------------------------- /工作总结/日常工作中最容易犯的几个并发错误.md: -------------------------------------------------------------------------------- 1 | ### 前言 2 | 列举大家平时在工作中最容易犯的几个并发错误,都是在实际项目代码中看到的鲜活例子,希望对大家有帮助。 3 | 4 | ### First Blood 5 | 线上总是出现:**ERROR 1062 (23000) Duplicate entry 'xxx' for key 'yyy'**,我们来看一下有问题的这段代码: 6 | ```Java 7 | UserBindInfo info = selectFromDB(userId); 8 | if(info == null){ 9 | info = new UserBindInfo(userId,deviceId); 10 | insertIntoDB(info); 11 | }else{ 12 | info.setDeviceId(deviceId); 13 | updateDB(info); 14 | } 15 | ``` 16 | 在**并发情况**下,**第一步判断都为空,就会有2个或者多个线程进入插入数据库操作,** 17 | 这时候就出现了同一个ID插入多次。 18 | 19 | **正确处理姿势:** 20 | ```SQL 21 | insert into UserBindInfo values(#{userId},#{deviceId}) on duplicate key update deviceId=#{deviceId}多次的情况,导致插入失败。 22 | ``` 23 | 一般情况下,可以用**insert...on duplicate key update...** 解决这个问题。 24 | 25 | **注意:** 如果UserBindInfo表存在**主键以及一个以上的唯一索引**,在并发情况下,使用insert...on duplicate key,可能会产生死锁(Mysql5.7),可以这样处理: 26 | ```Java 27 | try{ 28 | UserBindInfoMapper.insertIntoDB(userBindInfo); 29 | }catch(DuplicateKeyException ex){ 30 | UserBindInfoMapper.update(userBindInfo); 31 | } 32 | ``` 33 | ### Double Kill 34 | **小心你的全局变量**,如下面这段代码: 35 | 36 | ``` 37 | public class GlobalVariableConcurrentTest { 38 | 39 | private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 40 | 41 | public static void main(String[] args) throws InterruptedException { 42 | ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000)); 43 | 44 | while (true){ 45 | threadPoolExecutor.execute(()->{ 46 | String dateString = sdf.format(new Date()); 47 | try { 48 | Date parseDate = sdf.parse(dateString); 49 | String dateString2 = sdf.format(parseDate); 50 | System.out.println(dateString.equals(dateString2)); 51 | } catch (ParseException e) { 52 | e.printStackTrace(); 53 | } 54 | }); 55 | } 56 | 57 | } 58 | 59 | } 60 | ``` 61 | 可以看到有异常抛出 62 | ![](https://user-gold-cdn.xitu.io/2019/12/22/16f2b89cbdff0933?w=1434&h=620&f=png&s=139563) 63 | 64 | 全局变量的SimpleDateFormat,在并发情况下,存在安全性问题,阿里Java规约明确要求谨慎使用它。 65 | 66 | **除了SimpleDateFormat,其实很多时候,面对全局变量,我们都需要考虑并发情况是否存在问题,如下** 67 | ```Java 68 | @Component 69 | public class Test { 70 | 71 | public static List desc = new ArrayList<>(); 72 | 73 | public List getDescByUserType(int userType) { 74 | if (userType == 1) { 75 | desc.add("普通会员不可以发送和查看邮件,请购买会员"); 76 | return desc; 77 | } else if (userType == 2) { 78 | desc.add("恭喜你已经是VIP会员,尽情的发邮件吧"); 79 | return desc; 80 | }else { 81 | desc.add("你的身份未知"); 82 | return desc; 83 | } 84 | } 85 | } 86 | ``` 87 | 因为desc是全局变量,在并发情况下,请求getDescByUserType方法,得到的可能并不是你想要的结果。 88 | 89 | 90 | ### Trible Kill 91 | 假设现在有如下业务:控制同一个用户访问某个接口的频率不能小于5秒。一般很容易想到**使用redis的 setnx操作来控制并发访问**,于是有以下代码: 92 | ```Java 93 | if(RedisOperation.setnx(userId, 1)){ 94 | RedisOperation.expire(userId,5,TimeUnit.SECONDS)); 95 | //执行正常业务逻辑 96 | }else{ 97 | return “访问过于频繁”; 98 | } 99 | ``` 100 | 假设执行完setnx操作,还没来得及设置expireTime,机器重启或者突然崩溃,将会发生死锁。该用户id,后面执行setnx永远将为false,**这可能让你永远损失那个用户**。 101 | 102 | 那么怎么解决这个问题呢,可以考虑用**SET key value NX EX max-lock-time** ,它是一种在 Redis 中实现锁的方法,是原子性操作,不会像以上代码分两步执行,先set再expire,它是**一步到位**。 103 | 104 | **客户端执行以上的命令:** 105 | - 如果服务器返回 OK ,那么这个客户端获得锁。 106 | - 如果服务器返回 NIL ,那么客户端获取锁失败,可以在稍后再重试。 107 | - 设置的过期时间到达之后,锁将自动释放 108 | 109 | ### Quadra Kill 110 | 我们看一下有关ConcurrentHashMap的一段代码,如下: 111 | ```Java 112 | //全局变量 113 | Map map = new ConcurrentHashMap(); 114 | 115 | Integer value = count.get(k); 116 | if(value == null){ 117 | map.put(k,1); 118 | }else{ 119 | map.put(k,value+1); 120 | } 121 | ``` 122 | 假设两条线程都进入 **value==null**,这一步,得出的结果是不是会变小?OK,客官先稍作休息,闭目养神一会,我们验证一下,请看一个demo: 123 | ```Java 124 | public static void main(String[] args) { 125 | for (int i = 0; i < 1000; i++) { 126 | testConcurrentMap(); 127 | } 128 | } 129 | private static void testConcurrentMap() { 130 | final Map count = new ConcurrentHashMap<>(); 131 | ExecutorService executorService = Executors.newFixedThreadPool(2); 132 | final CountDownLatch endLatch = new CountDownLatch(2); 133 | Runnable task = ()-> { 134 | for (int i = 0; i < 5; i++) { 135 | Integer value = count.get("k"); 136 | if (null == value) { 137 | System.out.println(Thread.currentThread().getName()); 138 | count.put("k", 1); 139 | } else { 140 | count.put("k", value + 1); 141 | } 142 | } 143 | endLatch.countDown(); 144 | }; 145 | 146 | executorService.execute(task); 147 | executorService.execute(task); 148 | 149 | try { 150 | endLatch.await(); 151 | if (count.get("k") < 10) { 152 | System.out.println(count); 153 | } 154 | } catch (Exception e) { 155 | e.printStackTrace(); 156 | } 157 | ``` 158 | 表面看,运行结果应该都是10对吧,好的,我们再看运行结果 159 | : 160 | ![](https://user-gold-cdn.xitu.io/2019/7/20/16c0d5d23cea0033?w=729&h=267&f=png&s=36205) 161 | 162 | 163 | 运行结果出现了5,所以这样实现是有并发问题的,那么正确的实现姿势是啥呢? 164 | ```java 165 | Map map = new ConcurrentHashMap(); 166 | V v = map.get(k); 167 | if(v == null){ 168 | v = new V(); 169 | V old = map. putIfAbsent(k,v); 170 | if(old != null){ 171 | v = old; 172 | } 173 | } 174 | 175 | ``` 176 | 可以考虑使用**putIfAbsent**解决这个问题 177 | 178 | (1)如果key是新的记录,那么会向map中添加该键值对,并返回null。 179 | 180 | (2)如果key已经存在,那么不会覆盖已有的值,返回已经存在的值 181 | 182 | 我们再来看看以下代码以及运行结果: 183 | ``` 184 | public static void main(String[] args) { 185 | for (int i = 0; i < 1000; i++) { 186 | testConcurrentMap(); 187 | } 188 | } 189 | 190 | private static void testConcurrentMap() { 191 | ExecutorService executorService = Executors.newFixedThreadPool(2); 192 | final Map map = Maps.newConcurrentMap(); 193 | final CountDownLatch countDownLatch = new CountDownLatch(2); 194 | 195 | Runnable task = ()-> { 196 | AtomicInteger oldValue; 197 | for (int i = 0; i < 5; i++) { 198 | oldValue = map.get("k"); 199 | if (null == oldValue) { 200 | AtomicInteger initValue = new AtomicInteger(0); 201 | oldValue = map.putIfAbsent("k", initValue); 202 | if (oldValue == null) { 203 | oldValue = initValue; 204 | } 205 | } 206 | oldValue.incrementAndGet(); 207 | } 208 | countDownLatch.countDown(); 209 | }; 210 | 211 | executorService.execute(task); 212 | executorService.execute(task); 213 | 214 | try { 215 | countDownLatch.await(); 216 | System.out.println(map); 217 | } catch (Exception e) { 218 | e.printStackTrace(); 219 | } 220 | } 221 | ``` 222 | ![](https://user-gold-cdn.xitu.io/2019/7/20/16c0e5565754c1f6?w=768&h=520&f=png&s=52989) 223 | 224 | 225 | ### Penta Kill 226 | 现有如下业务场景:用户手上有一张现金券,可以兑换相应的现金, 227 | #### 错误示范一 228 | ```Java 229 | if(isAvailable(ticketId){ 230 | 1、给现金增加操作 231 | 2、deleteTicketById(ticketId) 232 | }else{ 233 | return “没有可用现金券” 234 | } 235 | ``` 236 | **解析:** 假设有两条线程A,B兑换现金,执行顺序如下: 237 | 238 | ![](https://user-gold-cdn.xitu.io/2019/7/20/16c0e675c7ea6200?w=720&h=801&f=png&s=74320) 239 | 240 | - 1.线程A加现金 241 | - 2.线程B加现金 242 | - 3.线程A删除票标志 243 | - 4.线程B删除票标志 244 | 245 | 显然,**这样有问题了,已经给用户加了两次现金了**。 246 | 247 | #### 错误示范2 248 | ``` 249 | if(isAvailable(ticketId){ 250 | 1、deleteTicketById(ticketId) 251 | 2、给现金增加操作 252 | }else{ 253 | return “没有可用现金券” 254 | } 255 | ``` 256 | 并发情况下,如果一条线程,第一步deleteTicketById删除失败了,也会多添加现金。 257 | 258 | #### 正确处理方案 259 | ``` 260 | if(deleteAvailableTicketById(ticketId) == 1){ 261 | 1、给现金增加操作 262 | }else{ 263 | return “没有可用现金券” 264 | } 265 | ``` 266 | 267 | ### 个人公众号 268 | 269 | ![](https://user-gold-cdn.xitu.io/2019/7/28/16c381c89b127bbb?w=344&h=344&f=jpeg&s=8943) 270 | 271 | - 如果你是个爱学习的好孩子,可以关注我公众号,一起学习讨论。 272 | - 如果你觉得本文有哪些不正确的地方,可以评论,也可以关注我公众号,私聊我,大家一起学习进步哈。 -------------------------------------------------------------------------------- /工作总结/日志打印规范.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/工作总结/日志打印规范.md -------------------------------------------------------------------------------- /工作总结/程序员必备基础:如何安全传输存储用户密码.md.bak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/工作总结/程序员必备基础:如何安全传输存储用户密码.md.bak -------------------------------------------------------------------------------- /工作总结/聊聊日常开发中,如何减少bug呢?.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/工作总结/聊聊日常开发中,如何减少bug呢?.md -------------------------------------------------------------------------------- /工作总结/聊聊日常开发中,如何减少bug呢?.md.bak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/工作总结/聊聊日常开发中,如何减少bug呢?.md.bak -------------------------------------------------------------------------------- /工作总结/记一次接口性能优化实践总结:优化接口性能的八个建议.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 最近对外接口偶现504超时问题,真枪实弹搞了一次接口性能优化。在这里结合优化过程,总结了接口优化的八个要点,希望对大家有帮助呀~ 3 | - 数据量比较大,批量操作数据入库 4 | - 耗时操作考虑异步处理 5 | - 恰当使用缓存 6 | - 优化程序逻辑、代码 7 | - SQL优化 8 | - 压缩传输内容 9 | - 考虑使用文件/MQ等其他方式暂存,异步再落地DB 10 | - 跟产品讨论需求最恰当,最舒服的实现方式 11 | 12 | 嘻嘻,先看一下我们对外转账接口的大概流程吧 13 | ![](https://user-gold-cdn.xitu.io/2020/5/30/1726375c0d0162f3?w=1393&h=586&f=png&s=50348) 14 | 15 | ### 1. 数据量比较大,批量操作数据入库 16 | 批量插入优化方法应该是我们最容易想到的啦~ 17 | 18 | **优化前** 19 | 20 | ``` 21 | //for循环单笔入库 22 | for(TransDetail detail:list){ 23 | insert(detail); 24 | } 25 | ``` 26 | **优化后** 27 | 28 | ``` 29 | // 批量入库,mybatis demo实现 30 | 31 | insert into trans_detail( id,amount,payer,payee) values 32 | ( 33 | #{item.id}, #{item.amount}, 34 | #{item.payer},#{item.payee} 35 | ) 36 | 37 | 38 | ``` 39 | **性能对比** 40 | | 单位(ms) | for循环单笔入库 | 批量入库 | 41 | |-----|-----|------| 42 | | 500条 | 611 | 449 | 43 | | 1000条 | 1420 | 1128 | 44 | 45 | **解析** 46 | - 批量插入性能更好,更加省时间,为什么呢? 47 | 48 | ``` 49 | 打个比喻:假如你需要搬一万块砖到楼顶,你有一个电梯,电梯一次可以放适量的砖(最多放500), 50 | 你可以选择一次运送一块砖,也可以一次运送500,你觉得哪种方式发方便,时间消耗小? 51 | ``` 52 | 53 | 54 | ### 2.耗时操作考虑异步处理 55 | 56 | **优化前:** 57 | 58 | 59 | 60 | 61 | **优化后:** 62 | 63 | ![](https://user-gold-cdn.xitu.io/2020/5/30/172636b9f5238802?w=999&h=644&f=png&s=67707) 64 | 65 | **性能对比:** 66 | 67 | 假设一个联行号匹配6ms 68 | | |同步| 异步| 69 | |-----|-----|------| 70 | | 500条 | 3000ms | 0 | 71 | | 1000条| 6000ms | 0| 72 | 73 | 74 | **解析:** 75 | - 因为联行号匹配比较耗时,放在异步处理的话,可以同步联机返回可以省掉这部分时间,大大提升接口性能,并且不会影响到转账主流程功能。 76 | - 除了这个例子,平时我们类似功能,如用户注册成功后,短信邮件通知,也是可以异步处理的,这个优化点香饽饽的~ 77 | - 所以,太耗时的操作,在不影响主流程功能的情况下,可以考虑开子线程异步处理的啦。 78 | 79 | ### 3.恰当使用缓存 80 | 在适当的业务场景,恰当地使用缓存,是可以大大提高接口性能的。这里的缓存包括:Redis,JVM本地缓存,memcached,或者Map等。 81 | 82 | 这次转账接口,使用到缓存啦,举个简单例子吧~ 83 | 84 | **优化前:** 85 | 86 | 87 | ![](https://user-gold-cdn.xitu.io/2020/5/30/17263c548352f480?w=396&h=719&f=png&s=36528) 88 | 89 | **优化后:** 90 | 91 | 92 | ![](https://user-gold-cdn.xitu.io/2020/5/30/17263cc73ccd1e76?w=723&h=785&f=png&s=70764) 93 | 94 | **解析:** 95 | - 把热点数据放到缓存,不用每次查询都去DB拉取,节省了这部分查SQL的耗时,美滋滋呀~ 96 | - 当然,不是什么数据都适合放到缓存的哦,访问比较频繁的热点数据才考虑缓存起来呢~ 97 | 98 | ### 4.优化程序逻辑、代码 99 | 优化程序逻辑,优化程序代码,是可以节省耗时的。 100 | 101 | 我这里也就本次的转账接口优化,举个例子吧~(优化前,联行号查询了两次) 102 | 103 | **优化前:** 104 | 105 | ``` 106 | 107 | punlic void process(Req req){ 108 | //检验参数,包括联行号(前端传来的payeeBankNo可以为空,但是如果后端没匹配到,会抛异常) 109 | checkTransParams(Req req); 110 | //Save DB 111 | saveTransDetail(req); 112 | } 113 | 114 | void checkTransParams(Req req){ 115 | //check Amount,and so on. 116 | checkAmount(req.getamount); 117 | //check payeebankNo 118 | if(Utils.isEmpty(req.getPayeeBankNo())){ 119 | String payeebankNo = getPayeebankNo(req.getPayeeAccountNo); 120 | if(Utils.isEmpty(payeebankNo){ 121 | throws Exception(); 122 | } 123 | } 124 | } 125 | 126 | int saveTransDetail(req){ 127 | String payeebankNo = getPayeebankNo(req.getPayeeAccountNo); 128 | req.setPayeeBankNo(payeebankNo); 129 | insert(req); 130 | ... 131 | } 132 | 133 | ``` 134 | **优化后:** 135 | 136 | ``` 137 | void checkTransParams(Req req){ 138 | //check Amount,and so on. 139 | checkAmount(req.getamount); 140 | //check payeebankNo 141 | if(Utils.isEmpty(req.getPayeeBankNo())){ 142 | String payeebankNo = getPayeebankNo(req.getPayeeAccountNo); 143 | if(Utils.isEmpty(payeebankNo){ 144 | throws Exception(); 145 | } 146 | } 147 | //查询到有联行号,直接设置进去啦,这样等下入库不用再差多一次 148 | req.setPayeeBankNo(payeebankNo); 149 | } 150 | 151 | int saveTransDetail(req){ 152 | insert(req); 153 | ... 154 | } 155 | ``` 156 | 157 | **解析:** 158 | - 对于优化程序逻辑、代码,是可以降低接口耗时的。以上demo只是一个很简单的例子,就是优化前payeeBankNo查询了两次,但是其实只查一次就可以了。很多时候,我们都知道这个点,但就是到写代码自己又忘记了呀~所以,写代码的时候,留点心吧,优化你的程序逻辑、代码哦。 159 | - 除了以上demo这点,还有其它的,如优化if复杂的逻辑条件,考虑是否可以调整顺序,或者for循环,是否重复实例化对象等等,这些都是我们需要关注的优化点。 160 | 161 | 之前我这篇文章,也提了几个优化点啦,有兴趣的朋友可以看一下哈~ 162 | 163 | [写代码有这些想法,同事才不会认为你是复制粘贴程序员](https://juejin.im/post/5dfe2e72518825125f39a2de#heading-1) 164 | 165 | ### 5.优化SQL 166 | 167 | 很多时候,你的接口性能瓶颈就在SQL这里,慢查询需要我们重点关注的呢。 168 | 169 | 我们可以通过这些方式优化我们的SQL: 170 | - 加索引 171 | - 避免返回不必要的数据 172 | - 优化sql结构 173 | - 分库分表 174 | - 读写分离 175 | 176 | 有兴趣的朋友可以看一下我这篇文章呢,很详细的SQL优化点: 177 | 178 | [后端程序员必备:书写高质量SQL的30条建议](https://juejin.im/post/5e624d156fb9a07ca80ab6f2) 179 | 180 | 181 | ### 6.压缩传输内容 182 | 183 | 压缩传输内容,让文件更小,因此传输会更快啦。10M带宽,传输10k的报文,一般比传输1M的会快呀;打个比喻,一匹千里马,它驮着一百斤的货跑得快,还是驮着10斤的货物跑得快? 184 | 185 | 186 | 传输100个转账对账对象耗时: 187 | 188 | 传输500个转账对象的耗时: 189 | 190 | 191 | **解析:** 192 | - 如果你的接口性能不好,然后传输报文很大,这时候是可以考虑压缩文件内容传输的,最后优化效果可能很不错哦~ 193 | 194 | 195 | ### 7.考虑使用文件/MQ等其他方式暂存,异步再落地DB 196 | 如果数据太大,落地数据库实在是慢,可以考虑先用文件的方式保存,或者考虑MQ,先落地,再异步保存到数据库~ 197 | > 本次转账接口,如果是并发开启,10个并发度,每个批次1000笔数据,数据库插入会特别耗时,大概10秒左右,这个跟我们的数据库机制有关,并发情况下,因为优先保证同步,所以并行的插入变成串行啦,就很耗时。 198 | 199 | **优化前:** 200 | 201 | ![](https://user-gold-cdn.xitu.io/2020/5/30/172649d6bd1017bf?w=704&h=490&f=png&s=43540) 202 | 203 | **优化后:** 204 | 205 | ![](https://user-gold-cdn.xitu.io/2020/5/30/17264a156b8b9691?w=778&h=518&f=png&s=50459) 206 | **解析:** 207 | - 如果你的耗时瓶颈就在数据库插入操作这里了,那就考虑文件保存或者MQ或者其他方式暂存吧,文件保存数据,对比一下耗时,有时候会有意想不到的效果哦。 208 | 209 | 210 | ### 8.跟产品讨论需求最恰当,最舒服的实现方式 211 | 这点个人觉得还是很重要的,有些需求需要好好跟产品沟通的。 212 | 213 | > 比如有个用户连麦列表展示的需求,产品说要展示所有的连麦信息,如果一个用户的连麦列表信息好大,你拉取所有连麦数据回来,接口性能就降下来啦。如果产品打桩分析,会发现,一般用户看连麦列表,也就看前几页~因此,奸笑,哈哈~ 其实,那个超大分页加载问题也是类似的。即limit +一个超大的数,一般会很慢的~~ 214 | 215 | ### 总结 216 | 217 | 本文呢,基于一次对外接口耗时优化的实践,15秒降到4秒左右,总结了优化接口性能的八个点,希望对大家日常开发有帮助哦~嘻嘻,有兴趣可以逛逛我的github哈,本文会收藏到github里滴 218 | > https://github.com/whx123/JavaHome 219 | 220 | 221 | ### 公众号 222 | ![](https://user-gold-cdn.xitu.io/2020/5/16/1721b50d00331393?w=900&h=500&f=png&s=389569) 223 | - 欢迎关注我个人公众号,交个朋友,一起学习哈~ 224 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /方案设计/README.MD: -------------------------------------------------------------------------------- 1 | - [实现一个刷数任务,需要思考哪些维度?](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247508890&idx=1&sn=919b8a794eb4902d958ae13d1f424737&chksm=c1e05e16f697d700ee9f79e087279de6312222b8e45887d976a572b01599f1177b358ade265b&token=337310304&lang=zh_CN#rd) 2 | - [手把手教你写设计方案](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247507937&idx=1&sn=33fd37f28675ce756e5d048b99254fcb&chksm=c1e0226df697ab7b4907fb2815c8dd2d195ea04c03a2f8fd0697c9a15a81fc639e5c5f7dab1b&token=337310304&lang=zh_CN#rd) 3 | - [简易版,基于JWT 实现登录认证](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247508057&idx=1&sn=06b6fee69c63afbe7ebd2f81a3627341&chksm=c1e05dd5f697d4c32e38bcb58c2ecba8115ea7f94a17bc197bcb7b042a18ef07fef0f0e03878&token=337310304&lang=zh_CN#rd) 4 | - [高并发系统设计的15个建议](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247508062&idx=1&sn=71e9647479ea71e8660d6ba48616c122&chksm=c1e05dd2f697d4c45ffd09e07fd40770e3d11591fa2b53161cd38da908cd55e41a38d5192605&token=337310304&lang=zh_CN#rd) 5 | - [面试必备:聊聊分布式锁的多种实现!](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247506434&idx=1&sn=c6ae1ec19558626897295bbe41304b62&chksm=c1e0278ef697ae989b14f4746d1049be976d1d5744e4f9c7ec6e17d006f206edcc1c47a3e862&token=337310304&lang=zh_CN#rd) 6 | - [并发环境下,先操作数据库还是先操作缓存?](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247508208&idx=1&sn=ac92523e33b478ad83560471338742f4&chksm=c1e05d7cf697d46aba95dc6661a8acbea0c894e44a793d054648b552a73b404aa3344d8a4826&token=337310304&lang=zh_CN#rd) 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /生产问题分析/README.MD: -------------------------------------------------------------------------------- 1 | ## 生产问题分析 2 | 3 | - [内存泄漏问题的分析和解决方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487986&idx=1&sn=d681a585ac489703788e3baa48eb9aa3&chksm=cf21cedbf85647cd23bbab9dfec63e6877f83c34efb19bd16075d5d90fea91d3f4a20fc77921&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 4 | - [生产问题分析!delete in子查询不走索引?!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495170&idx=1&sn=ce914de3abdb0d887e286b680b25111f&chksm=cf22312bf855b83d31a00da110626747df8e69fca1bc310642c56e39d663b006a8105f9fb1e1&token=1495321435&lang=zh_CN&scene=21#wechat_redirect) 5 | - [手把手教你分析Mysql死锁问题](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487979&idx=1&sn=588c83d77a8851f3b3c18cd68ed9c454&chksm=cf21cec2f85647d4a77cc239ae9a4cfd31bb8832be3d98540a08ea8b4a1f46b38cf736210a02&token=1495321435&lang=zh_CN&scene=21#wechat_redirect) 6 | - [线程池运用不当的一次线上事故](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487992&idx=1&sn=733335f2f69d743712915abc99f83b1d&chksm=cf21ced1f85647c7ab8c5d8bc4e8206b04acb5fd4feb94b8d088a782ed458b82aab69dba82aa&token=1990771297&lang=zh_CN#rd) 7 | - [盘点MySQL慢查询的12个原因](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499624&idx=1&sn=561b9cb7fe831ca7cb2d9fd65691e85e&chksm=cf222041f855a957ac50c0a53baaec6d26be32427259b2974450620f33a8c834419fe535e83d&token=1990771297&lang=zh_CN#rd) 8 | - [线程池如何监控,才能帮助开发者快速定位线上错误?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497444&idx=1&sn=1b2cc8b4685413149e46c814e468c6e6&chksm=cf2229cdf855a0db5f2da881d27c69f11c69480552985baa2a08cbe4d5a48bad7fb31a78dd5a&token=1990771297&lang=zh_CN#rd) 9 | - [数据库死锁排查思路分享](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247507770&idx=1&sn=b84b20aca057b34d511a501ff91941b5&chksm=c1e022b6f697aba05248128cb82f93aed341b1cc80e6d568c7150a4ffa6775692c7c9fa423a3&token=337310304&lang=zh_CN#rd) 10 | -------------------------------------------------------------------------------- /生产问题分析/线程池运用不当导致的问题.md: -------------------------------------------------------------------------------- 1 | 在高并发、异步化等场景,线程池的运用可以说无处不在。线程池从本质上来讲,即通过空间换取时间,因为线程的创建和销毁都是要消耗资源和时间的,对于大量使用线程的场景,使用池化管理可以延迟线程的销毁,大大提高单个线程的复用能力,进一步提升整体性能。 2 | 3 | 今天遇到了一个比较典型的线上问题,刚好和线程池有关,另外涉及到死锁、jstack命令的使用、JDK不同线程池的适合场景等知识点,同时整个调查思路可以借鉴,特此记录和分享一下。 4 | 5 | ### 01 业务背景描述 6 | 7 | 该线上问题发生在广告系统的核心扣费服务,首先简单交代下大致的业务流程,方便理解问题。 8 | 9 | ![](https://user-gold-cdn.xitu.io/2020/5/16/1721c846df1f475c?w=867&h=595&f=png&s=54867) 10 | 绿框部分即扣费服务在广告召回扣费流程中所处的位置,简单理解:当用户点击一个广告后,会从C端发起一次实时扣费请求(CPC,按点击扣费模式),扣费服务则承接了该动作的核心业务逻辑:包括执行反作弊策略、创建扣费记录、click日志埋点等。 11 | 12 | ### 问题现象和业务影响 13 | 12月2号晚上11点左右,我们收到了一个线上告警通知:扣费服务的线程池任务队列大小远远超出了设定阈值,而且队列大小随着时间推移还在持续变大。详细告警内容如下: 14 | 15 | ![](https://user-gold-cdn.xitu.io/2020/5/16/1721c850a5df4320?w=680&h=707&f=png&s=53613) 16 | 相应的,我们的广告指标:点击数、收入等也出现了非常明显的下滑,几乎同时发出了业务告警通知。其中,点击数指标对应的曲线表现如下: 17 | 18 | ![](https://user-gold-cdn.xitu.io/2020/5/16/1721c855a6de5cdb?w=1080&h=412&f=png&s=234814) 19 | 20 | 该线上故障发生在流量高峰期,持续了将近30分钟后才恢复正常。 21 | 22 | ### 03 问题调查和事故解决过程 23 | 24 | 下面详细说下整个事故的调查和分析过程。 25 | 26 | 第1步:收到线程池任务队列的告警后,我们第一时间查看了扣费服务各个维度的实时数据:包括服务调用量、超时量、错误日志、JVM监控,均未发现异常。 27 | 28 | 第2步:然后进一步排查了扣费服务依赖的存储资源(mysql、redis、mq),外部服务,发现了事故期间存在大量的数据库慢查询。 29 | 30 | ![](https://user-gold-cdn.xitu.io/2020/5/16/1721c8603d6d92c4?w=1080&h=282&f=png&s=76572) 31 | 32 | 上述慢查询来自于事故期间一个刚上线的大数据抽取任务,从扣费服务的mysql数据库中大批量并发抽取数据到hive表。因为扣费流程也涉及到写mysql,猜测这个时候mysql的所有读写性能都受到了影响,果然进一步发现insert操作的耗时也远远大于正常时期。 33 | 34 | ![](https://user-gold-cdn.xitu.io/2020/5/16/1721c8649c804151?w=1080&h=264&f=png&s=102892) 35 | 36 | 第3步:我们猜测数据库慢查询影响了扣费流程的性能,从而造成了任务队列的积压,所以决定立马暂定大数据抽取任务。但是很奇怪:停止抽取任务后,数据库的insert性能恢复到正常水平了,但是阻塞队列大小仍然还在持续增大,告警并未消失。 37 | 38 | 第4步:考虑广告收入还在持续大幅度下跌,进一步分析代码需要比较长的时间,所以决定立即重启服务看看有没有效果。为了保留事故现场,我们保留了一台服务器未做重启,只是把这台机器从服务管理平台摘掉了,这样它不会接收到新的扣费请求。 39 | 40 | 果然重启服务的杀手锏很管用,各项业务指标都恢复正常了,告警也没有再出现。至此,整个线上故障得到解决,持续了大概30分钟 41 | ### 04 问题根本原因的分析过程 42 | 43 | 下面再详细说下事故根本原因的分析过程。 44 | 45 | 第1步:第二天上班后,我们猜测那台保留了事故现场的服务器,队列中积压的任务应该都被线程池处理掉了,所以尝试把这台服务器再次挂载上去验证下我们的猜测,结果和预期完全相反,积压的任务仍然都在,而且随着新请求进来,系统告警立刻再次出现了,所以又马上把这台服务器摘了下来。 46 | 47 | 第2步:线程池积压的几千个任务,经过1个晚上都没被线程池处理掉,我们猜测应该存在死锁情况。所以打算通过jstack命令dump线程快照做下详细分析。 48 | 49 | ``` 50 | #找到扣费服务的进程号 51 | $ jstack pid > /tmp/stack.txt 52 | 53 | # 通过进程号dump线程快照,输出到文件中 54 | $ jstack pid > /tmp/stack.txt 55 | ``` 56 | 在jstack的日志文件中,立马发现了:用于扣费的业务线程池的所有线程都处于waiting状态,线程全部卡在了截图中红框部分对应的代码行上,这行代码调用了countDownLatch的await()方法,即等待计数器变为0后释放共享锁。 57 | 58 | ![](https://user-gold-cdn.xitu.io/2020/5/16/1721c879cee5024e?w=1080&h=575&f=png&s=746973) 59 | 第3步:找到上述异常后,距离找到根本原因就很接近了,我们回到代码中继续调查,首先看了下业务代码中使用了newFixedThreadPool线程池,核心线程数设置为25。针对newFixedThreadPool,JDK文档的说明如下: 60 | 61 | ``` 62 | 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。如果在所有线程处于活跃状态时提交新任务,则在有可用线程之前,新任务将在队列中等待。 63 | ``` 64 | 关于newFixedThreadPool,核心包括两点: 65 | 66 | ``` 67 | 1、最大线程数 = 核心线程数,当所有核心线程都在处理任务时,新进来的任务会提交到任务队列中等待; 68 | 69 | 2、使用了无界队列:提交给线程池的任务队列是不限制大小的,如果任务被阻塞或者处理变慢,那么显然队列会越来越大。 70 | ``` 71 | 72 | 所以,进一步结论是:核心线程全部死锁,新进的任务不对涌入无界队列,导致任务队列不断增加。 73 | 74 | 第4步:到底是什么原因导致的死锁,我们再次回到jstack日志文件中提示的那行代码做进一步分析。下面是我简化过后的示例代码 75 | 76 | ``` 77 | /** 78 |  * 执行扣费任务 79 |  */ 80 | public Result executeDeduct(ChargeInputDTO chargeInput) { 81 |   ChargeTask chargeTask = new ChargeTask(chargeInput); 82 |   bizThreadPool.execute(() -> chargeTaskBll.execute(chargeTask )); 83 |   return Result.success(); 84 | } 85 | 86 | /* 87 |  * 扣费任务的具体业务逻辑 88 |  */ 89 | public class ChargeTaskBll implements Runnable { 90 | 91 |   public void execute(ChargeTask chargeTask) { 92 |      // 第一步:参数校验 93 |      verifyInputParam(chargeTask); 94 | 95 |      // 第二步:执行反作弊子任务 96 |      executeUserSpam(SpamHelper.userConfigs); 97 | 98 |      // 第三步:执行扣费 99 |      handlePay(chargeTask); 100 | 101 |      // 其他步骤:点击埋点等 102 |      ... 103 |   } 104 | } 105 | 106 | /** 107 |  * 执行反作弊子任务 108 |  */ 109 | public void executeUserSpam(List configs) { 110 |   if (CollectionUtils.isEmpty(configs)) { 111 |     return; 112 |   } 113 | 114 |   try { 115 |     CountDownLatch latch = new CountDownLatch(configs.size()); 116 |     for (SpamUserConfigDO config : configs) { 117 |       UserSpamTask task = new UserSpamTask(config,latch); 118 |       bizThreadPool.execute(task); 119 |     } 120 |     latch.await(); 121 |   } catch (Exception ex) { 122 |     logger.error("", ex); 123 |   } 124 | } 125 | ``` 126 | 127 | 通过上述代码,大家能否发现死锁是怎么发生的呢?根本原因在于:一次扣费行为属于父任务,同时它又包含了多次子任务:子任务用于并行执行反作弊策略,而父任务和子任务使用的是同一个业务线程池。当线程池中全部都是执行中的父任务时,并且所有父任务都存在子任务未执行完,这样就会发生死锁。下面通过1张图再来直观地看下死锁的情况: 128 | 129 | ![](https://user-gold-cdn.xitu.io/2020/5/16/1721c8931f5898d0?w=880&h=378&f=png&s=75278) 130 | 假设核心线程数是2,目前正在执行扣费父任务1和2。另外,反作弊子任务1和3都执行完了,反作弊子任务2和4都积压在任务队列中等待被调度。因为反作弊子任务2和4没执行完,所以扣费父任务1和2都不可能执行完成,这样就发生了死锁,核心线程永远不可能释放,从而造成任务队列不断增大,直到程序OOM crash。 131 | 132 | 死锁原因清楚后,还有个疑问:上述代码在线上运行很长时间了,为什么现在才暴露出问题呢?另外跟数据库慢查询到底有没有直接关联呢? 133 | 134 | 暂时我们还没有复现证实,但是可以推断出:上述代码一定存在死锁的概率,尤其在高并发或者任务处理变慢的情况下,概率会大大增加。数据库慢查询应该就是导致此次事故出现的导火索。 135 | 136 | ### 05 解决方案 137 | 138 | 弄清楚根本原因后,最简单的解决方案就是:增加一个新的业务线程池,用来隔离父子任务,现有的线程池只用来处理扣费任务,新的线程池用来处理反作弊任务。这样就可以彻底避免死锁的情况了。 139 | 140 | ### 06 问题总结 141 | 142 | 回顾事故的解决过程以及扣费的技术方案,存在以下几点待继续优化: 143 | 144 | 1、使用固定线程数的线程池存在OOM风险,在阿里巴巴Java开发手册中也明确指出,而且用的词是『不允许』使用Executors创建线程池。 而是通过ThreadPoolExecutor去创建,这样让写的同学能更加明确线程池的运行规则和核心参数设置,规避资源耗尽的风险。 145 | 146 | 2、广告的扣费场景是一个异步过程,通过线程池或者MQ来实现异步化处理都是可选的方案。另外,极个别的点击请求丢失不扣费从业务上是允许的,但是大批量的请求丢弃不处理且没有补偿方案是不允许的。后续采用有界队列后,拒绝策略可以考虑发送MQ做重试处理。 -------------------------------------------------------------------------------- /程序人生&面试建议/README.MD: -------------------------------------------------------------------------------- 1 | ## 程序人生 2 | 3 | - [跟大家聊聊天,我周末都在干啥](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247493222&idx=1&sn=29eb95b01b54bed2abbcf5a72285b38a&chksm=cf22394ff855b059b29ffb562e22d8ecc048caa743eb5c6257ad474676940ba8d36840f075ed&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 4 | - [跟大家聊聊如何学习](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495313&idx=1&sn=7f521db08e84b07177d847c60071d709&chksm=cf2231b8f855b8ae765f2dd584994836c0b74ce0ef761653233c3af04f38b4a1aa1833f7a55a&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 5 | - [写了两年文章,终于破万!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247489788&idx=1&sn=66efbc1b718915bfd8996b521d317a55&chksm=cf21c7d5f8564ec3928957d3c23959f5cb99d9f9bd2c1bab0dcf1750a6a017c3869189a3651a&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 6 | - [夏天的风,我永远记得~](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487989&idx=2&sn=9eb923d4c8c22bee1a408e4f86983f65&chksm=cf21cedcf85647cac6fe4bfa6d732856fd0335f4fcadad4d1e0dd10702e95905e06c9e38e8e8&token=162724582&lang=zh_CN&scene=21#wechat_redirect) -------------------------------------------------------------------------------- /程序人生&面试建议/金三银四,给面试者的十大建议.md: -------------------------------------------------------------------------------- 1 | ### 一、提前复习好你的专业知识 2 | 专业知识是最为重要的一点,拥有了坚实的专业基础,你才能迈向成功的彼岸。 3 | 4 | 因此,面试之前,一定一定要复习好专业知识。对自己学过的知识,要做一个概括,放在脑海中。茶余饭后,复习一下,做到随便看到一道基础题目,心中都能有个答案。 5 | 6 | 比如,一道最基本基础题,ArrayList和LinkedList有什么区别?如果你是做Java后台开发的,应该都会了吧,哈哈,不会的赶紧复习一下,哈哈哈。 7 | 8 | 所以,面试前还是像图下这位小伙子一样,好好复习,哈哈~ 9 | ![](https://user-gold-cdn.xitu.io/2020/2/14/17043f9e65f6c1a2?w=1919&h=1080&f=png&s=1153224) 10 | 11 | ### 二、对简历上写的项目一定要足够了解,把握其中的亮点。 12 | 你在简历上的信息,就是面试官了解你的窗口。你写上去的项目,自己一定一定要了解清楚来龙去脉。如果把别人很厉害的项目copy上去,面试官一问你三不知,那就露馅啦~ 13 | 14 | 同时,简历上需要沉淀一些有内容的东西,需要有些亮点。当然,简历上的亮点并不一定是酝酿百年的女儿红,也可以是你自己含辛茹苦酿造出的米酒,只要有你汗水的味道体现在里面就可以啦。 15 | 16 | 也就是说,你的简历不一定就需要是github上几百star的项目,也可以是你自己负责设计的一些有意思的项目,甚至一个小小的挂号系统,只要你能在里面,倾注了你的思考与汗水,并且让面试官感受得到,那面试这场战役就赢一半了。 17 | 18 | 最后,放一下我之前项目中(个人觉得是个小亮点),用CAS思想解决实际并发问题的实践~ 19 | 20 | [CAS乐观锁解决并发问题的一次实践](https://juejin.im/post/5d0616ade51d457756536791) 21 | 22 | 23 | ### 三、面试一定要杜绝过度紧张,要心平气和去对待。 24 | 面试过程一定要杜绝过度紧张,紧张可能会导致你发挥失常,让你基础的问题都忘了怎么回答,最后与理想offer失之交臂。 25 | 26 | 有点小紧张也是可以接受的,这一点会促使你认真地准备,但是过度紧张就适得其反啦。 27 | 28 | 因此,面试前两天,你可以跑一下步,深呼吸几下,或者心理面想一下过去一些美妙的事情,或者运动一下,或者唱个歌,弹一下琴等都可以。 29 | 30 | 平时如果觉得生活压抑,或者紧张的话,我会弹弹吉他,唱唱歌,哈哈 31 | ![](https://user-gold-cdn.xitu.io/2020/2/14/17043f1531a9fc6e?w=1080&h=2248&f=png&s=580499) 32 | ### 四、面试前充分了解公司以及工作岗位内容 33 | 面试前,多点了解公司是做什么业务的,以及工作岗位的主要工作内容。结合招聘要求,提前想一下面试官可能问的问题,换位思考以及延伸思考。比如,你面的是一间银行的开发岗,该银行用到自研的MQ通讯,那么,你需要准备好https相关的面试题,消息中间件的相关面试题等等。 34 | 35 | 如:https原理是什么?谈谈RocketMQ消息顺序和重复消费问题等。 36 | 37 | ### 五、面试过程中,把面试官引到自己熟悉的领域,重拳出击。 38 | 39 | 面试过程中,需要学会把面试官引到自己熟悉的领域。 40 | 41 | 打个比方,假如你对索引这一块特别熟悉的话,面试官让你介绍你做了项目/什么项目优化时,你可以举例通过索引做了慢查询优化等等,这时候,面试官十之八九会问你索引相关问题,这时候,你可以把覆盖索引、最左匹配原则、聚族索引、回表、查询优化统统搬出来啦。 42 | 43 | 最好就是结合一些流程图、原理图分析自己优化过程,让面试官知道你的思考轨迹,这时候,面试官才更容易认可你。 44 | 45 | 在这里,我忍不住想分享自己之前话的美美的一张图,InnoDb 逻辑存储结构图,哈哈,如下所示: 46 | ![](https://user-gold-cdn.xitu.io/2020/2/14/17043eabf1e65973?w=1215&h=901&f=png&s=396084) 47 | 48 | ### 六、有认识的人内推比在boss直聘、拉钩等,通过概率会高点。 49 | 50 | 如果你要面一个大厂,有认识的人内推最好不过啦,其实内推过得概率大很多的。因为,大家都懂一个道理,优秀的人旁边,也是一些优秀的人,正所谓**物以类聚,人以群分**。 51 | 所以,多数HR也是这样挑人的,如果你通过内推获得面试机会,好好表现吧,骚年。 52 | 53 | 54 | ![](https://user-gold-cdn.xitu.io/2020/2/14/1704426243d9ab07?w=255&h=255&f=png&s=104800) 55 | 56 | ### 七、在工作时一点一字积累,在面试时,一字一词表现。 57 | 58 | 这句话意思就是说,在学习工作过程中,我们需要一点一点积累,尤其实一些细节的地方,容易犯错的地方。然后,面试的时候,把这些细节,在面试官面前展示出来,吐字清晰,一字一词地表现。酱紫的话,面试官内心会对你加分的,觉得你是个有心人. 59 | 60 | 比如,一下是我工作中代码细节的总结,有兴趣可以看看哈~ 61 | 62 | [写代码有这些想法,同事才不会认为你是复制粘贴程序员](https://juejin.im/post/5dfe2e72518825125f39a2de) 63 | ### 八、面试尽量拿多几个offer,这样才能掌握主动权,不然HR可能会压榨你。 64 | 面试找工作,对待offer。需要吃着碗里的,看着锅里的。要不然,如果你只有一个offer,HR跟你谈薪的时候,很可能会压榨你的价值。拿多几个offer,可以有谈薪的资本。 65 | 66 | ![](https://user-gold-cdn.xitu.io/2020/2/14/1704423b8e7982ac?w=800&h=450&f=png&s=613615) 67 | 68 | ### 九、多点刷专业笔试题,程序员的话,争取成为leetcode的常客。 69 | 阿里、腾讯、头条这些公司,面试经常要求手动写编程题,所以,作为面试者,要想过关,一定需要多点刷题,尤其是leetcode官网上面的题,不求每道题都能背下来,但是至少,每种类型的题目,你都需要知道思路,需要知道大概怎么实现吧,如:动态规划问题、链表操作等等。 70 | 71 | ### 十、学习一门乐器,坚持一项运动。 72 | 学习一门乐器,是为了让生活多一份诗意,坚持一项运动,是为了身体健康。 73 | 74 | 学习一门乐器,在面试官看来会加分的,因为年会可以上去表演哈哈~ 坚持一项运动,也是加分的,因为一般公司都有运动小组,篮球小组,或者羽毛球,如果面试官也跟你一样爱好篮球,说不定,你们就可以聊聊今年湖人总冠军了,哈哈。并且,运动的人最阳光啦,哈哈,不信你看我~ 75 | ![](https://user-gold-cdn.xitu.io/2020/2/14/170440b9ea8d6349?w=1620&h=1080&f=png&s=2382365) 76 | 77 | ### 个人公众号 78 | 79 | ![](https://user-gold-cdn.xitu.io/2019/7/28/16c381c89b127bbb?w=344&h=344&f=jpeg&s=8943) 80 | 81 | - 觉得写得好的小伙伴给个点赞+关注啦,谢谢~ 82 | - 同时非常期待小伙伴们能够关注我公众号,后面慢慢推出更好的干货~嘻嘻 83 | -------------------------------------------------------------------------------- /程序员必备工具篇/README.MD: -------------------------------------------------------------------------------- 1 | 2 | ## 程序员工具篇 3 | 4 | - [用代码画时序图!YYDS](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247500478&idx=1&sn=ec674e3eadba9bb87849292f46f84989&chksm=cf221d97f8559481fae8f0e1871ae19499568b3e49980e92018c4a5acdcf743a37da79c2436d&token=1990771297&lang=zh_CN#rd) 5 | - [程序员必备基础:Git 命令全方位学习](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488013&idx=1&sn=7011a51a347e3da2cf8f8540b4d9a5d6&chksm=cf21cd24f8564432d74bc13551ebdeae71a71ea31e339c7a8f1f42f181078b5192475d598626&token=1569911403&lang=zh_CN&scene=21#wechat_redirect) 6 | - [MyBatis 插件原理与实战](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498815&idx=1&sn=737e8f92ff526dac408af7a409f3a3d4&chksm=cf222316f855aa007fe16f7bca0636c552f238deb766bb54c34db7b633c13451fc91a4fe8a3e&token=1990771297&lang=zh_CN#rd) 7 | - [更快的Maven来了,速度提升了8倍!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497470&idx=1&sn=7a3a5bb48f7d3b1a627460b698e7e9a0&chksm=cf2229d7f855a0c1e892c23f7690e6ab1a745040142672b982a3934c8307901d0be03dff3cff&token=1990771297&lang=zh_CN#rd) 8 | - [因为知道了30+款在线工具,我的工作效率提升500%!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488041&idx=1&sn=26d55c23ecd439860c4d9865bec61976&chksm=cf21cd00f8564416fe991974d24a51798d925b2e79d62935accf02aa6895c7b02adf48e9e207&token=1990771297&lang=zh_CN#rd) -------------------------------------------------------------------------------- /缓存Redis总结/README.MD: -------------------------------------------------------------------------------- 1 | ## 缓存 2 | 3 | - [大厂经典面试题:Redis为什么这么快?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490736&idx=1&sn=95377e729b27f0afefbaa5f20239fc9d&chksm=cf21c399f8564a8ff5239fbaa86d616a48086b47b3bb03c8ccc1d3cc066e41c75e16638c3fc8&token=1495321435&lang=zh_CN&scene=21#wechat_redirect) 4 | - [美团二面:Redis与MySQL双写一致性如何保证?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490243&idx=1&sn=ff11c3aab9ada3b16d7f2b57c846d567&chksm=cf21c5eaf8564cfc59e3d0d56fd02b0f5513015005f498381be4d12db462442a49aabe4159ef&token=1495321435&lang=zh_CN&scene=21#wechat_redirect) 5 | - [使用Redis,你必须知道的21个注意要点](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488325&idx=1&sn=6d9bbe5bf2f2f2904755de5c786fb21b&chksm=cf21cc6cf856457a9d23b3e25ec48107a582e709f05964dfdb5ba77e9a239d8307334c485fdf&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 6 | - [七种方案!探讨Redis分布式锁的正确使用姿势](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488142&idx=1&sn=79a304efae7a814b6f71bbbc53810c0c&chksm=cf21cda7f85644b11ff80323defb90193bc1780b45c1c6081f00da85d665fd9eb32cc934b5cf&token=162724582&lang=zh_CN&scene=21#wechat_redirect) 7 | - [2W字!详解20道Redis经典面试题!(珍藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494124&idx=1&sn=c185f7d999d5f006608d05707a8a7eea&chksm=cf2236c5f855bfd329c6e2ee27f23f8131ebcd312960190a10f1a819d67f07a21a08ad17f263&token=162724582&lang=zh_CN&scene=21#wechat_redirect) -------------------------------------------------------------------------------- /缓存Redis总结/七种方案对比分布式锁.md: -------------------------------------------------------------------------------- 1 | ### 前言 2 | 日常开发中,秒杀下单、抢红包等等业务场景,都需要用到分布式锁。而Redis非常适合作为分布式锁使用。本文将分七个方案展开,跟大家探讨Redis分布式锁的正确使用方式。如果有不正确的地方,欢迎大家指出哈,一起学习一起进步。 3 | 4 | 5 | 公众号:**捡田螺的小男孩** 6 | 7 | - 什么是分布式锁 8 | - 方案一:SETNX + EXPIRE 9 | - 方案二:SETNX + value值是(系统时间+过期时间) 10 | - 方案三:使用Lua脚本(包含SETNX + EXPIRE两条指令) 11 | - 方案四:SET的扩展命令(SET EX PX NX) 12 | - 方案五:SET EX PX NX + 校验唯一随机值,再释放锁 13 | - 方案六: 开源框架:Redisson 14 | - 方案七:多机实现的分布式锁Redlock 15 | 16 | - github地址,感谢每颗star 17 | > https://github.com/whx123/JavaHome 18 | 19 | 20 | ### 什么是分布式锁 21 | 22 | > 分布式锁其实就是,控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。 23 | 24 | 我们先来看下,一把靠谱的分布式锁应该有哪些特征: 25 | 26 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/42884a1613344c11be5fef3b9e8ed7c5~tplv-k3u1fbpfcp-zoom-1.image) 27 | 28 | - **互斥性**: 任意时刻,只有一个客户端能持有锁。 29 | - **锁超时释放**:持有锁超时,可以释放,防止不必要的资源浪费,也可以防止死锁。 30 | - **可重入性**:一个线程如果获取了锁之后,可以再次对其请求加锁。 31 | - **高性能和高可用**:加锁和解锁需要开销尽可能低,同时也要保证高可用,避免分布式锁失效。 32 | - **安全性**:锁只能被持有的客户端删除,不能被其他客户端删除 33 | 34 | ### Redis分布式锁方案一:SETNX + EXPIRE 35 | 36 | 提到Redis的分布式锁,很多小伙伴马上就会想到```setnx```+ ```expire```命令。即先用```setnx```来抢锁,如果抢到之后,再用```expire```给锁设置一个过期时间,防止锁忘记了释放。 37 | 38 | > SETNX 是SET IF NOT EXISTS的简写.日常命令格式是SETNX key value,如果 key不存在,则SETNX成功返回1,如果这个key已经存在了,则返回0。 39 | 40 | 假设某电商网站的某商品做秒杀活动,key可以设置为key_resource_id,value设置任意值,伪代码如下: 41 | 42 | ``` 43 | if(jedis.setnx(key_resource_id,lock_value) == 1){ //加锁 44 | expire(key_resource_id,100); //设置过期时间 45 | try { 46 | do something //业务请求 47 | }catch(){ 48 |   } 49 |   finally { 50 | jedis.del(key_resource_id); //释放锁 51 | } 52 | } 53 | ``` 54 | 但是这个方案中,```setnx```和```expire```两个命令分开了,**不是原子操作**。如果执行完```setnx```加锁,正要执行```expire```设置过期时间时,进程crash或者要重启维护了,那么这个锁就“长生不老”了,**别的线程永远获取不到锁啦**。 55 | 56 | 57 | ### Redis分布式锁方案二:SETNX + value值是(系统时间+过期时间) 58 | 59 | 为了解决方案一,**发生异常锁得不到释放的场景**,有小伙伴认为,可以把过期时间放到```setnx```的value值里面。如果加锁失败,再拿出value值校验一下即可。加锁代码如下: 60 | ``` 61 | long expires = System.currentTimeMillis() + expireTime; //系统时间+设置的过期时间 62 | String expiresStr = String.valueOf(expires); 63 | 64 | // 如果当前锁不存在,返回加锁成功 65 | if (jedis.setnx(key_resource_id, expiresStr) == 1) { 66 | return true; 67 | } 68 | // 如果锁已经存在,获取锁的过期时间 69 | String currentValueStr = jedis.get(key_resource_id); 70 | 71 | // 如果获取到的过期时间,小于系统当前时间,表示已经过期 72 | if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) { 73 | 74 | // 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间(不了解redis的getSet命令的小伙伴,可以去官网看下哈) 75 | String oldValueStr = jedis.getSet(key_resource_id, expiresStr); 76 | 77 | if (oldValueStr != null && oldValueStr.equals(currentValueStr)) { 78 | // 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才可以加锁 79 | return true; 80 | } 81 | } 82 | 83 | //其他情况,均返回加锁失败 84 | return false; 85 | } 86 | ``` 87 | 88 | 这个方案的优点是,巧妙移除```expire```单独设置过期时间的操作,把**过期时间放到setnx的value值**里面来。解决了方案一发生异常,锁得不到释放的问题。但是这个方案还有别的缺点: 89 | 90 | > - 过期时间是客户端自己生成的(System.currentTimeMillis()是当前系统的时间),必须要求分布式环境下,每个客户端的时间必须同步。 91 | > - 如果锁过期的时候,并发多个客户端同时请求过来,都执行jedis.getSet(),最终只能有一个客户端加锁成功,但是该客户端锁的过期时间,可能被别的客户端覆盖 92 | > - 该锁没有保存持有者的唯一标识,可能被别的客户端释放/解锁。 93 | 94 | 95 | ### Redis分布式锁方案三:使用Lua脚本(包含SETNX + EXPIRE两条指令) 96 | 97 | 实际上,我们还可以使用Lua脚本来保证原子性(包含setnx和expire两条指令),lua脚本如下: 98 | ``` 99 | if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then 100 | redis.call('expire',KEYS[1],ARGV[2]) 101 | else 102 | return 0 103 | end; 104 | ``` 105 | 加锁代码如下: 106 | 107 | ``` 108 | String lua_scripts = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then" + 109 | " redis.call('expire',KEYS[1],ARGV[2]) return 1 else return 0 end"; 110 | Object result = jedis.eval(lua_scripts, Collections.singletonList(key_resource_id), Collections.singletonList(values)); 111 | //判断是否成功 112 | return result.equals(1L); 113 | ``` 114 | 这个方案还是有缺点的哦,至于哪些缺点,你先思考一下。也可以想下。跟方案二对比,哪个更好? 115 | 116 | ### Redis分布式锁方案方案四:SET的扩展命令(SET EX PX NX) 117 | 118 | 除了使用,使用Lua脚本,保证```SETNX + EXPIRE```两条指令的原子性,我们还可以巧用Redis的SET指令扩展参数!(```SET key value[EX seconds][PX milliseconds][NX|XX]```),它也是原子性的! 119 | 120 | > SET key value[EX seconds][PX milliseconds][NX|XX] 121 | > - NX :表示key不存在的时候,才能set成功,也即保证只有第一个客户端请求才能获得锁,而其他客户端请求只能等其释放锁,才能获取。 122 | > - EX seconds :设定key的过期时间,时间单位是秒。 123 | > - PX milliseconds: 设定key的过期时间,单位为毫秒 124 | > - XX: 仅当key存在时设置值 125 | 126 | 伪代码demo如下: 127 | ``` 128 | if(jedis.set(key_resource_id, lock_value, "NX", "EX", 100s) == 1){ //加锁 129 | try { 130 | do something //业务处理 131 | }catch(){ 132 |   } 133 |   finally { 134 | jedis.del(key_resource_id); //释放锁 135 | } 136 | } 137 | ``` 138 | 139 | 但是呢,这个方案还是可能存在问题: 140 | 141 | - 问题一:**锁过期释放了,业务还没执行完**。假设线程a获取锁成功,一直在执行临界区的代码。但是100s过去后,它还没执行完。但是,这时候锁已经过期了,此时线程b又请求过来。显然线程b就可以获得锁成功,也开始执行临界区的代码。那么问题就来了,临界区的业务代码都不是严格串行执行的啦。 142 | - 问题二:**锁被别的线程误删**。假设线程a执行完后,去释放锁。但是它不知道当前的锁可能是线程b持有的(线程a去释放锁时,有可能过期时间已经到了,此时线程b进来占有了锁)。那线程a就把线程b的锁释放掉了,但是线程b临界区业务代码可能都还没执行完呢。 143 | 144 | ### 方案五:SET EX PX NX + 校验唯一随机值,再删除 145 | 146 | 既然锁可能被别的线程误删,那我们给value值设置一个标记当前线程唯一的随机数,在删除的时候,校验一下,不就OK了嘛。伪代码如下: 147 | ``` 148 | if(jedis.set(key_resource_id, uni_request_id, "NX", "EX", 100s) == 1){ //加锁 149 | try { 150 | do something //业务处理 151 | }catch(){ 152 |   } 153 |   finally { 154 | //判断是不是当前线程加的锁,是才释放 155 | if (uni_request_id.equals(jedis.get(key_resource_id))) { 156 | jedis.del(lockKey); //释放锁 157 | } 158 | } 159 | } 160 | ``` 161 | 162 | 在这里,**判断是不是当前线程加的锁**和**释放锁**不是一个原子操作。如果调用jedis.del()释放锁的时候,可能这把锁已经不属于当前客户端,会解除他人加的锁。 163 | 164 | ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9237655e6b1a47038d2774231e507e11~tplv-k3u1fbpfcp-watermark.image) 165 | 166 | 为了更严谨,一般也是用lua脚本代替。lua脚本如下: 167 | ``` 168 | if redis.call('get',KEYS[1]) == ARGV[1] then 169 | return redis.call('del',KEYS[1]) 170 | else 171 | return 0 172 | end; 173 | ``` 174 | 175 | ### Redis分布式锁方案六:Redisson框架 176 | 177 | 方案五还是可能存在**锁过期释放,业务没执行完**的问题。有些小伙伴认为,稍微把锁过期时间设置长一些就可以啦。其实我们设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。 178 | 179 | 当前开源框架Redisson解决了这个问题。我们一起来看下Redisson底层原理图吧: 180 | 181 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/367cd1a7a3fb4d398988e4166416d71d~tplv-k3u1fbpfcp-zoom-1.image) 182 | 183 | 184 | 只要线程一加锁成功,就会启动一个```watch dog```看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了**锁过期释放,业务没执行完**问题。 185 | 186 | ### Redis分布式锁方案七:多机实现的分布式锁Redlock+Redisson 187 | 188 | 前面六种方案都只是基于单机版的讨论,还不是很完美。其实Redis一般都是集群部署的: 189 | 190 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7349794feeee458aa71c27f27a0b2428~tplv-k3u1fbpfcp-watermark.image) 191 | 192 | 如果线程一在Redis的master节点上拿到了锁,但是加锁的key还没同步到slave节点。恰好这时,master节点发生故障,一个slave节点就会升级为master节点。线程二就可以获取同个key的锁啦,但线程一也已经拿到锁了,锁的安全性就没了。 193 | 194 | 为了解决这个问题,Redis作者 antirez提出一种高级的分布式锁算法:Redlock。Redlock核心思想是这样的: 195 | > 搞多个Redis master部署,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是与在Redis单实例,使用相同方法来获取和释放锁。 196 | 197 | 我们假设当前有5个Redis master节点,在5台服务器上面运行这些Redis实例。 198 | 199 | ![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0df0a36c7ccd439291a8a869ff4ddad3~tplv-k3u1fbpfcp-watermark.image) 200 | 201 | RedLock的实现步骤:如下 202 | > - 1.获取当前时间,以毫秒为单位。 203 | > - 2.按顺序向5个master节点请求加锁。客户端设置网络连接和响应超时时间,并且超时时间要小于锁的失效时间。(假设锁自动失效时间为10秒,则超时时间一般在5-50毫秒之间,我们就假设超时时间是50ms吧)。如果超时,跳过该master节点,尽快去尝试下一个master节点。 204 | > - 3.客户端使用当前时间减去开始获取锁时间(即步骤1记录的时间),得到获取锁使用的时间。当且仅当超过一半(N/2+1,这里是5/2+1=3个节点)的Redis master节点都获得锁,并且使用的时间小于锁失效时间时,锁才算获取成功。(如上图,10s> 30ms+40ms+50ms+4m0s+50ms) 205 | > - 如果取到了锁,key的真正有效时间就变啦,需要减去获取锁所使用的时间。 206 | > - 如果获取锁失败(没有在至少N/2+1个master实例取到锁,有或者获取锁时间已经超过了有效时间),客户端要在所有的master节点上解锁(即便有些master节点根本就没有加锁成功,也需要解锁,以防止有些漏网之鱼)。 207 | 208 | 简化下步骤就是: 209 | - 按顺序向5个master节点请求加锁 210 | - 根据设置的超时时间来判断,是不是要跳过该master节点。 211 | - 如果大于等于三个节点加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功啦。 212 | - 如果获取锁失败,解锁! 213 | 214 | Redisson实现了redLock版本的锁,有兴趣的小伙伴,可以去了解一下哈~ 215 | 216 | ### 公众号 217 | - 欢迎关注公众号:捡田螺的小男孩 218 | 219 | ### 参考与感谢 220 | 221 | - [redis系列:分布式锁](https://juejin.cn/post/6844903656911798285 "redis系列:分布式锁") 222 | - [浅析 Redis 分布式锁解决方案](https://www.infoq.cn/article/dvaaj71f4fbqsxmgvdce "浅析 Redis 分布式锁解决方案") 223 | - [细说Redis分布式锁🔒](https://juejin.cn/post/6844904082860146695#heading-3 "细说Redis分布式锁🔒") 224 | - [Redlock:Redis分布式锁最牛逼的实现](https://mp.weixin.qq.com/s?__biz=MzU5ODUwNzY1Nw==&mid=2247484155&idx=1&sn=0c73f45f2f641ba0bf4399f57170ac9b&scene=21#wechat_redirect) 225 | 226 | -------------------------------------------------------------------------------- /缓存Redis总结/使用Redis的21个注意点.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whx123/JavaHome/c46280c44013c05ee50a6c04cfcbdabaef943c39/缓存Redis总结/使用Redis的21个注意点.md -------------------------------------------------------------------------------- /茶余饭后谈编程学习周报/茶余饭后编程周报二.md: -------------------------------------------------------------------------------- 1 | **问题一:** 2 | 3 | 我现在用了一个静态线程池,用于整个请求共享。设置的参数是核心线程10个,最大线程147个,阻塞队列2048,策略是超过就丢弃并且抛异常。 4 | 当qps在1000的时候,由于一个请求我需要分成三次去请求不同的接口,就相当于有3000个调用接口的请求。 但是请求用的是实时接口,如果该接口响应很慢比如10秒钟,那么这147个线程就全部挂起了,没有空余的线程去处理新的业务了。整个系统的响应就非常慢了。 5 | 6 | 群上回复答案: 7 | > - 响应很慢的接口走异步,把返回的数据丢到queue里面。 8 | > - 应该是10秒的接口得改 9 | > - 接口慢了的话,看下能不能用redis 10 | > - 合并请求,减少网络传输时间 11 | > - 优化接口肯定需要,从源头解决 12 | 13 | 我觉得,先理解好线程池原理吧,就是,来个请求就拿个核心线程去处理,如果核心线程用完了,就放队列,队列满了,就拿非核心线程去处理,处理不过来之后,就根据策略去处理,线程干完活又会被拉取干别的~然后呢,就算每秒以前个请求过来,最多也是线程池供应不过来,然后抛弃一部分请求,跟响应慢不是一个因果关系。 14 | 15 | ![](https://imgkr2.cn-bj.ufileos.com/5f138381-05f3-4aa2-a344-98e8b970a920.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=yJuf5adRW5I%252BSl3%252FWX1oyVr9%252Ffo%253D&Expires=1603008626) 16 | 17 | 18 | 其实,如果每个请求30毫秒处理完,可能你的线程池也够用了。但是呢,如果你一个请求,接口处理10s,也就是说10秒,它没干完活,没法接别的活,肯定系统就处理不过来那么多请求啦,所以最后结果就是,一部分请求就会抛弃,因为线程池抛弃策略抛弃啦。回过头,你的系统响应慢,就是你的接口耗时问题呀,你需要优化它。 19 | 20 | 可以看下我这两篇文章。线程池和如何接口性能优化的~ 21 | 22 | 23 | 24 | 关于线程池和接口性能优化的,可以看下我之前这两篇文章哈 25 | - [面试必备:Java线程池解析](https://mp.weixin.qq.com/s?__biz=MzIwOTE2MzU4NA==&mid=2247483728&idx=1&sn=0221dfd5eb7862c0aa749a7038b39307&chksm=9779457fa00ecc69d3bb554ccc1daa8aa204b16b15587759cf1f148bda51907f6f57418e150f&token=1319249232&lang=zh_CN#rd) 26 | 27 | - [记一次接口性能优化实践总结:优化接口性能的八个建议](https://mp.weixin.qq.com/s?__biz=MzIwOTE2MzU4NA==&mid=2247484426&idx=1&sn=7265be5a5c37e71e65a2e42c999d3f72&chksm=97794025a00ec93379ad537353dd58f9149f801e100786a2b9c8cf37257e6d863d7ce4b87a5e&token=1319249232&lang=zh_CN#rd) -------------------------------------------------------------------------------- /茶余饭后谈编程学习周报/茶余饭后谈编程周报一.md: -------------------------------------------------------------------------------- 1 | ### 问题一:websocket接收客户端的图片,断开问题 2 | 3 | >当时用websocket接收客户端的图片,总是断开连接,tomcat websocket默认的接收字节消息最大是8192 bytes,由于图片有100k+,所以会断开连接。 4 | 5 | **解决方法:** 6 | 7 | 是客户端建立连接后,用session.setMaxBinaryMessageBufferSize()方法修改默认的字节大小 8 | 9 | ![](https://user-gold-cdn.xitu.io/2020/7/4/1731a4cdd2291bc9?w=800&h=491&f=png&s=201414) 10 | 11 | ### 问题二: Redis连接超时怎么排查 12 | - 1.到最大连接数上限了(看Redis配置) 13 | - 2.项目中连接的从来没释放过(Redis满载情况,看代码逻辑,是否有死循环) 14 | - 3.基本配置,开放的端口配置(看服务端是否临时关闭) 15 | - 4.服务器不稳定(ping一下或者用工具查看网络波动情况) 16 | 17 | ### 问题三:多个list的交集有没有优雅的方法 18 | 用谷歌的工具包,guava交并补 19 | -------------------------------------------------------------------------------- /茶余饭后谈编程学习周报/茶余饭后谈编程周报三.md: -------------------------------------------------------------------------------- 1 | ### 问题一: netty内存泄漏 2 | 3 | **Magee:有没有大佬了解这个异常的啊,使用的springcloudgateway, 底层没有改动** 4 | > ERROR io.netty.util.ResourceLeakDetector -LEAK: ByteBuf.release() was not called before it's garbage-collected. See http://netty.io/wiki/reference-counted-objects.html for more information. 5 | 6 | 7 | **Shawn:** 8 | > ByteBuf.release()之后没有通知垃圾回收 9 | 10 | **百里半:** 11 | > - 内存泄漏了,应该是你们重写了他们的方法,在最后没有release. 12 | > - netty有自己的内存检测机制,你这个报错不速度解决,过一会服务就不可用了 13 | > - 我遇到过,docker直接挂掉 14 | > - 解决方法:找到你们重写的地方,把release加上,怎么加可以参考netty源码里面release怎么用的就好了 15 | 16 | ### 问题二:消息队列应用场景 17 | **spring:** 18 | > 请教下,消息队列你们在项目中实际用在哪些场景? 19 | 20 | **张大树:** 21 | > - 削峰,减少数据库压力. 22 | > - 假设有10万个请求过来了,然后数据需要入库,但是如果同时写库会有压力,就可以自己生产,自己消费mq ,异步入库,前提是可以异步入库 23 | -------------------------------------------------------------------------------- /高并发/README.MD: -------------------------------------------------------------------------------- 1 | ## 高并发 2 | - [美团二面:Redis与MySQL双写一致性如何保证?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490243&idx=1&sn=ff11c3aab9ada3b16d7f2b57c846d567&chksm=cf21c5eaf8564cfc59e3d0d56fd02b0f5513015005f498381be4d12db462442a49aabe4159ef&token=1495321435&lang=zh_CN&scene=21#wechat_redirect) 3 | - [Redis主从、哨兵、 Cluster集群一锅端!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498254&idx=1&sn=6489360c3b226df9811e66cb40ec7656&chksm=cf222527f855ac3112628bcec7730064fee3fdbe869fbd0a7410c22766a0c036a7e5c1a69fa0&token=1990771297&lang=zh_CN#rd) 4 | - [面试必备:聊聊MySQL的主从](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497982&idx=1&sn=bb589329cceb5462fc41f66ec63dbf56&chksm=cf2227d7f855aec16dd4d3b3425c0401850eeaf2c9cdc82e82722d38a00c24ee9ccfa3353774&token=1990771297&lang=zh_CN#rd) 5 | - [聊聊幂等设计](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497427&idx=1&sn=2ed160c9917ad989eee1ac60d6122855&chksm=cf2229faf855a0ecf5eb34c7335acdf6420426490ee99fc2b602d54ff4ffcecfdab24eeab0a3&token=1990771297&lang=zh_CN#rd) 6 | - [聊聊接口性能优化的11个小技巧](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497361&idx=1&sn=a0a2b0f92804921ba3d31b6236f275c2&chksm=cf2229b8f855a0aec650f4e0c3f105aa08e52fabbc54807dd37fefc4873749698b2b1445b59f&token=1990771297&lang=zh_CN#rd) 7 | - [面试必备:秒杀场景九个细节](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247493227&idx=1&sn=10e5064d7d224c69dce400e90cd44de6&chksm=cf223942f855b0541ada22a312e0d4ffbc99df463678247a0dede3ef16eb81e3344a4a54ceaf&token=1990771297&lang=zh_CN#rd) --------------------------------------------------------------------------------