├── README-BACK.md ├── README.md └── src ├── 01-java-interview ├── 01-java-base-interview │ ├── 01.md │ ├── 01Collection.md │ ├── README.md │ └── images │ │ └── 01Collection.jpg ├── 02-java-data-base-interview │ └── README.md ├── 03-java-thread-interview │ ├── ProcessAndThread.md │ └── README.md ├── 04-java-base-written │ └── README.md └── README.md ├── 02-data-structure-and-algorithm ├── README.md └── Recursion.md ├── 03-java-base ├── Collection.md ├── HashMap(One).md ├── IterableAndIterator.md ├── List-ArrayList.md ├── List.md ├── ProcessAndThread.md ├── README.md ├── String(one).md └── images │ ├── Collection.png │ ├── Iterable方法.png │ └── Iterator方法.png ├── 04-java-advanced ├── AutoboxingAndUnboxing.md └── README.md ├── 05-java-becoming-god └── README.md ├── 06-data-base ├── PessimisticLockAndOptimisticLock.md ├── README.md ├── acid.md ├── groupby.md ├── isolation.md ├── join.md └── rowlock_tablelock.md ├── 07-front-end └── other.md ├── 08-frame └── other.md ├── 09-framework └── README.md ├── 10-micro-service-and-devops └── other.md ├── 11-test └── other.md ├── 12-project └── other.md ├── 13-operation └── other.md ├── 14-tools └── other.md ├── 15-agreement ├── NetworkSevenLayerProtocol.md └── README.md ├── 16-learning-materials └── other.md ├── 17-other ├── KimSanYinFour.md ├── README.md ├── WebsiteCollection.md ├── learning_resource.md └── pay-image │ └── README.md ├── README.md ├── UpdateLog.md └── temp.md /README-BACK.md: -------------------------------------------------------------------------------- 1 | ****# :loudspeaker: Coder-Programming(Coder编程)介绍 # 2 | 3 | > 文章为导航页面,记录一些编程开发中的一些相关知识,希望能与大家一起进步! 4 | ## :mega: 我的相关文章会发表在以下博客或论坛 ## 5 | 6 | 7 |

8 | 微信群 9 | 公众号 10 | 掘金 11 | 投稿 12 | 投稿 13 | 投稿 14 | 投稿 15 | 投稿 16 | 投稿 17 | 投稿 18 | 投稿 19 |

20 | 21 | 22 | 23 | 24 | ## :mega: 1.面试题and笔试题 ## 25 | 26 | > JAVA相关面试题,祝各位找到好工作! 27 | 28 | | :notebook:JAVA基础面试题 | :notebook:数据库面试题 | :notebook:多线程面试题 | :notebook:微服务面试题 |:notebook:Spring面试题 | 29 | | :------:| :------: | :------: | :------: |:------: | 30 | | [JAVA基础面试题](src/01-java-interview/README.md)| [数据库面试题](src/01-java-interview/README.md) | [多线程面试题](src/01-java-interview/README.md) | [微服务面试题](src/01-java-interview/README.md) | [其他算法](src/01-java-interview/README.md) | 31 | 32 | | :notebook:SpringMVC面试题 | :notebook:SpringBoot面试题 |:notebook:SpringCloud面试题 |:notebook:Redis面试题 |:notebook:设计模式面试题 | 33 | | :------:| :------: | :------: | :------: |:------: | 34 | | [SpringMVC面试题](src/01-java-interview/README.md)| [SpringBoot面试题](src/01-java-interview/README.md) | [SpringCloud面试题](src/01-java-interview/README.md) | [Redis面试题](src/01-java-interview/README.md) | [设计模式面试题](src/01-java-interview/README.md) | 35 | 36 | | :notebook:JAVA基础笔试题 | 37 | | :------:| 38 | | [JAVA基础笔试题](src/01-java-interview/04-java-base-written/README.md)| 39 | 40 | ## :mega: 2.数据结构与算法 ## 41 | 42 | > 搜索/排序/高级算法/大数据算法/其他算法文章。搞定数据结构与算法,逼格分分钟提升! 43 | 44 | | :beer:搜索 | :beer:基础排序 | :beer:高级算法 | :beer:大数据算法 |:beer:其他算法 | 45 | | :------:| :------: | :------: | :------: |:------: | 46 | | [基础搜索](src/02-data-structure-and-algorithm/other.md)| [基础排序](src/02-data-structure-and-algorithm/other.md) | [高级算法](src/02-data-structure-and-algorithm/other.md) | [大数据算法](src/02-data-structure-and-algorithm/other.md) | [其他算法](src/02-data-structure-and-algorithm/other.md) | 47 | 48 | 49 | ## :coffee: 3.Java基础 ## 50 | 51 | > Java基础文章,基础扎实才是王道! 52 | 53 | | :book:环境搭建 | :book:开发工具 | :book:面向对象编程 | :book:Java集合 | :book:Java多线程 | :book: IO流| :book:其他基础知识 | 54 | | :------:| :------:| :------:| :------: | :------: |:------: | :------: | 55 | | [基础环境](src/03-java-base/README.md) | [Eclipse/IDEA](src/03-java-base/README.md) | [面向对象编程](src/03-java-base/README.md) | [Java集合](src/03-java-base/README.md) | [Java多线程](src/03-java-base/other.md) | [IO流](src/03-java-base/other.md) |[其他基础知识](src/03-java-base/other.md) | 56 | 57 | 58 | ## :coffee: 4.Java进阶 ## 59 | 60 | > Java进阶,提升实力,提高自身竞争力! 61 | 62 | |:memo:设计模式 | :memo:Servlet+Tomcat | :memo:JSP | :memo:XML和JSON | 63 | | :------:| :------:| :------:| :------: | 64 | | [设计模式](src/04-java-advanced/other.md) | [Servlet+Tomcat](src/04-java-advanced/other.md) | [JSP](src/04-java-advanced/other.md) | [XML和JSON](src/04-java-advanced/other.md) | 65 | 66 | | :memo:过滤器和拦截器 | :memo:AJAX | :memo:JAVA8新特性 | :memo:JAVA9新特性 | 67 | |:------: |:------: |:------: |:------: | 68 | |[过滤器和拦截器](src/04-java-advanced/other.md) | [AJAX](src/04-java-advanced/other.md) | [JAVA8新特性](src/04-java-advanced/other.md) | [JAVA9新特性](src/04-java-advanced/other.md) | 69 | 70 | 71 | 72 | ## :coffee: 5.Java成神 ## 73 | 74 | > Java成神,深入源码,分析内核,成神之路!! 75 | 76 | | :ledger: 设计模式 | :ledger: Spring| :ledger: SpringMVC | :ledger:JVM | :ledger: 高并发 | 77 | | :------:| :------:| :------:| :------: | :------: | 78 | | [源码结合设计模式实例](src/05-java-becoming-god/README.md) | [Spring源码分析](src/05-java-becoming-god/README.md) | [SpringMVC源码分析](src/05-java-becoming-god/README.md) | [JVM](src/05-java-becoming-god/README.md) | [高并发](src/05-java-becoming-god/README.md) | 79 | 80 | 81 | ## :mega: 6.数据库 ## 82 | 83 | > 数据存储,掌握数据的源头! 84 | 85 | | :fish_cake:关系型数据库(MySQL/Oracle) | :fish_cake:非关系型数据库(Redis) | 86 | | :------:| :------: | 87 | | [关系型数据库(MySQL/Oracle)](src/06-data-base/README.md) | [Redis从入门到实战](src/other.md) | 88 | 89 | 90 | ## :mega: 7.前端技术## 91 | 92 | > 前端技术,优化UI设计,展示最美的一面! 93 | 94 | | :ring:HTML5 | :ring:CSS | :ring:JavaScript/Jquery | 95 | | :------:| :------: | :------: | 96 | | [HTML5入门](src/other.md) | [CSS](src/other.md) | [JavaScript/Jquery](src/other.md) | 97 | 98 | 99 | ## :mega: 8.企业框架 ## 100 | 101 | > 企业级常用框架,轻松学会稳就业! 102 | 103 | | :jack_o_lantern:Hibernate | :jack_o_lantern:Struts2 | :jack_o_lantern:Spring | :jack_o_lantern:SpringMVC | 104 | | :------:| :------: | :------: |:------: | 105 | | [Hibernate](src/other.md) | [Struts2](src/other.md) | [Spring](src/other.md) |[SpringMVC](src/other.md) | 106 | 107 | | :jack_o_lantern:SpringSecurity |:jack_o_lantern:Mybatis | :jack_o_lantern:SpringBoot |:jack_o_lantern:SpringCloud | 108 | |:------: |:------: |:------: |:------: | 109 | |[SpringSecurity](src/other.md) |[Mybatis](src/other.md) |[SpringBoot实战](src/other.md) | [SpringCloud实战](src/other.md) | 110 | 111 | 112 | ## :mega: 9.系统架构 ## 113 | 114 | > 系统架构,学习中间件,扩展自身综合实力! 115 | 116 | | :tv:Config配置中心 | :tv:Apollo配置中心 | :tv:Rabbitmq消息队列 | :tv:Cat分布式消息监控 | 117 | | :------:| :------: | :------: |:------: | 118 | | [Spring-Cloud-Config](src/other.md) | [携程Apollo配置中心](src/other.md) | [Rabbitmq消息队列](src/other.md) |[大众Cat分布式消息监控](src/other.md)| 119 | 120 | |:tv:XXL-分布式定时任务 |:tv:网关 | :tv:负载均衡 |:tv:API文档 |:tv:读写分离 | 121 | |:------: |:------: |:------: |:------: |:------: | 122 | | [XXL-分布式定时任务](src/other.md) |[Zuul/GateWay/Kong](src/other.md) |[Nginx/F5](src/other.md) | [Swagger/Yapi](src/other.md) | [MyCat](src/other.md) | 123 | 124 | ## :mega: 10.微服务与DevOps ## 125 | 126 | > DevOps,让开发更便捷,让流程更智能! 127 | 128 | | :microscope:GitLab | 129 | | :------:| 130 | | [GitLab](src/other.md) | 131 | 132 | ## :mega: 11.测试## 133 | 134 | > 测试,减少BUG,提高系统稳定性! 135 | 136 | | :video_camera:单元测试 | :video_camera:接口测试 | :video_camera:性能测试/稳定性测试 | 137 | | :------:| :------: | :------: | 138 | | [单元测试](src/other.md) | [接口测试](src/other.md) | [性能测试/稳定性测试](src/other.md) | 139 | 140 | 141 | ## :mega: 12.项目实战 ## 142 | 143 | > 项目实战,提高自身综合实力! 144 | 145 | | :computer:OCP项目实战 | 146 | | :------:| 147 | | [OCP项目实战](src/17-other/README.md) | 148 | 149 | 150 | ## :mega: 13.运维 ## 151 | 152 | | :video_camera:Linux入门 | :video_camera:容器技术 | :video_camera:Paas平台 | 153 | | :------:| :------:| :------:| 154 | | [Linux入门](src/other.md) | [Docker容器](src/other.md) | [kubernetes容器化云平台](src/other.md) | 155 | 156 | 157 | ## :mega: 14.工具 ## 158 | 159 | > 包括 Eclipse、IDEA、Maven、UML、SVN、以及一些其他工具 160 | 161 | | :sound:Eclipse| :sound:IDEA | :sound:SVN | :sound:Maven | :sound:UML | :sound:其他工具 | 162 | | :------:| :------: | :------: |:------: |:------: |:------: | 163 | | [Eclipse](src/other.md) |[IDEA使用教程](src/other.md) | [SVN使用教程](src/other.md) |[Maven使用教程](src/other.md) |[UML使用教程](src/other.md) |[其他工具](src/other.md) | 164 | 165 | 166 | ## :mega: 15.网络协议 ## 167 | 168 | > 网络协议,了解协议之美! 169 | 170 | | :clipboard:网络协议|:clipboard:HTTP/HTTPS | :clipboard:Oauth2| 171 | | :------:| :------:| :------:| 172 | | [网络协议](src/15-agreement/README.md) | [HTTP/HTTPS协议](src/15-agreement/README.md) | [Oauth2协议](src/15-agreement/README.md) | 173 | 174 | ## :mega: 16.学习资料 ## 175 | 176 | > 学习资料,有需要的同学关注微信公众号,回复对应的数字或文字领取! 177 | 178 | 除了技术文章,还可能感兴趣的资源: 179 | 180 | | :art: 书籍资料 | :art:视频资源 | :art:技术文章 | :art:微信文章导航 | 181 | | :------:| :------: | :------:| :------: | 182 | | [书籍资料](src/other.md) | [视频资源](src/other.md) | [技术文章](src/other.md) | [微信文章导航](src/other.md) | 183 | 184 | ## :mega: 17.其他 ## 185 | 186 | > 其他,扩展大家阅读知识! 187 | 188 | | :bell:Web安全|:bell:管理知识 | 189 | | :------:| :------:| 190 | | [Web安全](src/other.md) | [PMP第六版教程](src/17-other/README.md) | 191 | 192 | ## 公众号 193 | 194 | ![公众号](https://oscimg.oschina.net/oscnet/12fe7873f956cdb992a3df19db0c84bf6f7.jpg) 195 | 196 | 197 | # :mega: 最后: # 198 | 199 | 200 | > 如果觉得还不错,能够帮得到你的话! 201 | > 帮忙点赞并Start ~ 202 | > 欢迎关注公众号:Coder编程 推送最新的**干货**技术文章,进入公众号回复“1”加入**Java交流群**! 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :loudspeaker: Coder-Programming(Coder编程)介绍 # 2 | 3 | ### 注明:大部分文章从网络搜集,如有侵权,请联系作者进行删除! 4 | 5 | ## 千呼万唤始出来!我的个人博客终于出炉了! 6 | - 博客地址:https://www.coder-programming.cn/ 7 | - 欢迎大家多多点击,多多支持!往后我会陆续完善博客文章!供大家一起学习一起进步! 8 | 9 | > 文章为导航页面,记录一些编程开发中的一些相关知识,希望能与大家一起进步! 10 | ## :mega: 我的相关文章会发表在以下博客或论坛 ## 11 | 12 | 13 |

14 | 微信群 15 | 公众号 16 | 掘金 17 | CSDN 18 | 博客园 19 | 开源中国 20 | 简书- 21 | 知乎 22 | 头条 23 | 开发者头条 24 | segmentfault 25 |

26 | 27 | 28 | 29 | 30 | ## :mega: 1.面试题and笔试题 ## 31 | 32 | > JAVA相关面试题,祝各位找到好工作! 33 | 34 | | :notebook:JAVA基础面试题 | :notebook:数据库面试题 | :notebook:多线程面试题 | :notebook:微服务面试题 |:notebook:Spring面试题 | 35 | | :------:| :------: | :------: | :------: |:------: | 36 | | [JAVA基础面试题](src/01-java-interview/README.md)| [数据库面试题](src/01-java-interview/README.md) | [多线程面试题](src/01-java-interview/README.md) | [微服务面试题](src/01-java-interview/README.md) | [其他算法](src/01-java-interview/README.md) | 37 | 38 | | :notebook:SpringMVC面试题 | :notebook:SpringBoot面试题 |:notebook:SpringCloud面试题 |:notebook:Redis面试题 |:notebook:设计模式面试题 | 39 | | :------:| :------: | :------: | :------: |:------: | 40 | | [SpringMVC面试题](src/01-java-interview/README.md)| [SpringBoot面试题](src/01-java-interview/README.md) | [SpringCloud面试题](src/01-java-interview/README.md) | [Redis面试题](src/01-java-interview/README.md) | [设计模式面试题](src/01-java-interview/README.md) | 41 | 42 | | :notebook:JAVA基础笔试题 | 43 | | :------:| 44 | | [JAVA基础笔试题](src/01-java-interview/04-java-base-written/README.md)| 45 | 46 | 47 | ## :coffee: 3.Java基础 ## 48 | 49 | > Java基础文章,基础扎实才是王道! 50 | 51 | | :book:环境搭建 | :book:开发工具 | :book:面向对象编程 | :book:Java集合 | :book:Java多线程 | :book: IO流| :book:其他基础知识 | 52 | | :------:| :------:| :------:| :------: | :------: |:------: | :------: | 53 | | [基础环境](src/03-java-base/README.md) | [Eclipse/IDEA](src/03-java-base/README.md) | [面向对象编程](src/03-java-base/README.md) | [Java集合](src/03-java-base/README.md) | [Java多线程](src/03-java-base/other.md) | [IO流](src/03-java-base/other.md) |[其他基础知识](src/03-java-base/other.md) | 54 | 55 | 56 | ## :coffee: 4.Java进阶 ## 57 | 58 | > Java进阶,提升实力,提高自身竞争力! 59 | 60 | |:memo:设计模式 | :memo:Servlet+Tomcat | :memo:JSP | :memo:XML和JSON | 61 | | :------:| :------:| :------:| :------: | 62 | | [设计模式](src/04-java-advanced/other.md) | [Servlet+Tomcat](src/04-java-advanced/other.md) | [JSP](src/04-java-advanced/other.md) | [XML和JSON](src/04-java-advanced/other.md) | 63 | 64 | | :memo:过滤器和拦截器 | :memo:AJAX | :memo:JAVA8新特性 | :memo:JAVA9新特性 | 65 | |:------: |:------: |:------: |:------: | 66 | |[过滤器和拦截器](src/04-java-advanced/other.md) | [AJAX](src/04-java-advanced/other.md) | [JAVA8新特性](src/04-java-advanced/other.md) | [JAVA9新特性](src/04-java-advanced/other.md) | 67 | 68 | 69 | 70 | ## :coffee: 5.Java成神 ## 71 | 72 | > Java成神,深入源码,分析内核,成神之路!! 73 | 74 | | :ledger: 设计模式 | :ledger: Spring| :ledger: SpringMVC | :ledger:JVM | :ledger: 高并发 | 75 | | :------:| :------:| :------:| :------: | :------: | 76 | | [源码结合设计模式实例](src/05-java-becoming-god/README.md) | [Spring源码分析](src/05-java-becoming-god/README.md) | [SpringMVC源码分析](src/05-java-becoming-god/README.md) | [JVM](src/05-java-becoming-god/README.md) | [高并发](src/05-java-becoming-god/README.md) | 77 | 78 | 79 | ## :mega: 6.数据库 ## 80 | 81 | > 数据存储,掌握数据的源头! 82 | 83 | | :fish_cake:关系型数据库(MySQL/Oracle) | :fish_cake:非关系型数据库(Redis) | 84 | | :------:| :------: | 85 | | [关系型数据库(MySQL/Oracle)](src/06-data-base/README.md) | [Redis从入门到实战](src/other.md) | 86 | 87 | 88 | ## :mega: 7.前端技术## 89 | 90 | > 前端技术,优化UI设计,展示最美的一面! 91 | 92 | | :ring:HTML5 | :ring:CSS | :ring:JavaScript/Jquery | 93 | | :------:| :------: | :------: | 94 | | [HTML5入门](src/other.md) | [CSS](src/other.md) | [JavaScript/Jquery](src/other.md) | 95 | 96 | 97 | ## :mega: 8.企业框架 ## 98 | 99 | > 企业级常用框架,轻松学会稳就业! 100 | 101 | | :jack_o_lantern:Hibernate | :jack_o_lantern:Struts2 | :jack_o_lantern:Spring | :jack_o_lantern:SpringMVC | 102 | | :------:| :------: | :------: |:------: | 103 | | [Hibernate](src/other.md) | [Struts2](src/other.md) | [Spring](src/other.md) |[SpringMVC](src/other.md) | 104 | 105 | | :jack_o_lantern:SpringSecurity |:jack_o_lantern:Mybatis | :jack_o_lantern:SpringBoot |:jack_o_lantern:SpringCloud | 106 | |:------: |:------: |:------: |:------: | 107 | |[SpringSecurity](src/other.md) |[Mybatis](src/other.md) |[SpringBoot实战](src/other.md) | [SpringCloud实战](src/other.md) | 108 | 109 | 110 | ## :mega: 9.系统架构 ## 111 | 112 | > 系统架构,学习中间件,扩展自身综合实力! 113 | 114 | | :tv:Config配置中心 | :tv:Apollo配置中心 | :tv:Rabbitmq消息队列 | :tv:Cat分布式消息监控 | 115 | | :------:| :------: | :------: |:------: | 116 | | [Spring-Cloud-Config](src/other.md) | [携程Apollo配置中心](src/other.md) | [Rabbitmq消息队列](src/other.md) |[大众Cat分布式消息监控](src/other.md)| 117 | 118 | |:tv:XXL-分布式定时任务 |:tv:网关 | :tv:负载均衡 |:tv:API文档 |:tv:读写分离 | 119 | |:------: |:------: |:------: |:------: |:------: | 120 | | [XXL-分布式定时任务](src/other.md) |[Zuul/GateWay/Kong](src/other.md) |[Nginx/F5](src/other.md) | [Swagger/Yapi](src/other.md) | [MyCat](src/other.md) | 121 | 122 | ## :mega: 10.微服务与DevOps ## 123 | 124 | > DevOps,让开发更便捷,让流程更智能! 125 | 126 | | :microscope:GitLab | 127 | | :------:| 128 | | [GitLab](src/other.md) | 129 | 130 | ## :mega: 11.测试## 131 | 132 | > 测试,减少BUG,提高系统稳定性! 133 | 134 | | :video_camera:单元测试 | :video_camera:接口测试 | :video_camera:性能测试/稳定性测试 | 135 | | :------:| :------: | :------: | 136 | | [单元测试](src/other.md) | [接口测试](src/other.md) | [性能测试/稳定性测试](src/other.md) | 137 | 138 | 139 | ## :mega: 12.项目实战 ## 140 | 141 | > 项目实战,提高自身综合实力! 142 | 143 | | :computer:OCP项目实战 | 144 | | :------:| 145 | | [OCP项目实战](src/17-other/README.md) | 146 | 147 | 148 | ## :mega: 13.运维 ## 149 | 150 | | :video_camera:Linux入门 | :video_camera:容器技术 | :video_camera:Paas平台 | 151 | | :------:| :------:| :------:| 152 | | [Linux入门](src/other.md) | [Docker容器](src/other.md) | [kubernetes容器化云平台](src/other.md) | 153 | 154 | 155 | ## :mega: 14.工具 ## 156 | 157 | > 包括 Eclipse、IDEA、Maven、UML、SVN、以及一些其他工具 158 | 159 | | :sound:Eclipse| :sound:IDEA | :sound:SVN | :sound:Maven | :sound:UML | :sound:其他工具 | 160 | | :------:| :------: | :------: |:------: |:------: |:------: | 161 | | [Eclipse](src/other.md) |[IDEA使用教程](src/other.md) | [SVN使用教程](src/other.md) |[Maven使用教程](src/other.md) |[UML使用教程](src/other.md) |[其他工具](src/other.md) | 162 | 163 | 164 | ## :mega: 15.网络协议 ## 165 | 166 | > 网络协议,了解协议之美! 167 | 168 | | :clipboard:网络协议|:clipboard:HTTP/HTTPS | :clipboard:Oauth2| 169 | | :------:| :------:| :------:| 170 | | [网络协议](src/15-agreement/README.md) | [HTTP/HTTPS协议](src/15-agreement/README.md) | [Oauth2协议](src/15-agreement/README.md) | 171 | 172 | ## :mega: 16.学习资料 ## 173 | 174 | > 学习资料,有需要的同学关注微信公众号,回复对应的数字或文字领取! 175 | 176 | 除了技术文章,还可能感兴趣的资源: 177 | 178 | | :art: 书籍资料 | :art:视频资源 | :art:技术文章 | :art:微信文章导航 | 179 | | :------:| :------: | :------:| :------: | 180 | | [书籍资料](src/other.md) | [视频资源](src/other.md) | [技术文章](src/other.md) | [微信文章导航](src/other.md) | 181 | 182 | ## :mega: 17.其他 ## 183 | 184 | > 其他,扩展大家阅读知识! 185 | 186 | | :bell:Web安全|:bell:管理知识 | 187 | | :------:| :------:| 188 | | [Web安全](src/other.md) | [PMP第六版教程](src/17-other/README.md) | 189 | 190 | ## 公众号 191 | 192 | ![公众号](https://oscimg.oschina.net/oscnet/12fe7873f956cdb992a3df19db0c84bf6f7.jpg) 193 | 194 | 195 | # :mega: 最后: # 196 | 197 | 198 | > 如果觉得还不错,能够帮得到你的话! 199 | > 帮忙点赞并Start ~ 200 | > 欢迎关注公众号:Coder编程 推送最新的**干货**技术文章,进入公众号回复“1”加入**Java交流群**! 201 | -------------------------------------------------------------------------------- /src/01-java-interview/01-java-base-interview/01.md: -------------------------------------------------------------------------------- 1 | 何谓悲观锁与乐观锁 2 | 乐观锁对应于生活中乐观的人总是想着事情往好的方向发展,悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展。这两种人各有优缺点,不能不以场景而定说一种人好于另外一种人。 3 | 4 | 悲观锁 5 | 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。 6 | 7 | 乐观锁 8 | 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。 9 | 10 | 两种锁的使用场景 11 | 从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。 12 | 13 | 乐观锁常见的两种实现方式 14 | 乐观锁一般会使用版本号机制或CAS算法实现。 15 | 16 | 1. 版本号机制 17 | 一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。 18 | 19 | 举一个简单的例子: 20 | 假设数据库中帐户信息表中有一个 version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100 。 21 | 22 | 操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 50(50(100-$50 )。 23 | 在操作员 A 操作的过程中,操作员B 也读入此用户信息( version=1 ),并从其帐户余额中扣除 20(20(100-$20 )。 24 | 操作员 A 完成了修改工作,将数据版本号加一( version=2 ),连同帐户扣除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本大于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。 25 | 操作员 B 完成了操作,也将版本号加一( version=2 )试图向数据库提交数据( balance=$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 2 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须大于记录当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。 26 | 这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员A 的操作结果的可能。 27 | 28 | 2. CAS算法 29 | 即compare and swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三个操作数 30 | 31 | 需要读写的内存值 V 32 | 进行比较的值 A 33 | 拟写入的新值 B 34 | 当且仅当 V 的值等于 A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。 35 | 36 | 乐观锁的缺点 37 | ABA 问题是乐观锁一个常见的问题 38 | 39 | 1 ABA 问题 40 | 如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 “ABA”问题。 41 | 42 | JDK 1.5 以后的 AtomicStampedReference 类就提供了此种能力,其中的 compareAndSet 方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。 43 | 44 | 2 循环时间长开销大 45 | 自旋CAS(也就是不成功就一直循环执行直到成功)如果长时间不成功,会给CPU带来非常大的执行开销。 如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。 46 | 47 | 3 只能保证一个共享变量的原子操作 48 | CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。但是从 JDK 1.5开始,提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用AtomicReference类把多个共享变量合并成一个共享变量来操作。 49 | 50 | CAS与synchronized的使用情景 51 | 简单的来说CAS适用于写比较少的情况下(多读场景,冲突一般较少),synchronized适用于写比较多的情况下(多写场景,冲突一般较多) 52 | 53 | 对于资源竞争较少(线程冲突较轻)的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。 54 | 对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。 55 | 补充: Java并发编程这个领域中synchronized关键字一直都是元老级的角色,很久之前很多人都会称它为 “重量级锁” 。但是,在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的 偏向锁 和 轻量级锁 以及其它各种优化之后变得在某些情况下并不是那么重了。synchronized的底层实现主要依靠 Lock-Free 的队列,基本思路是 自旋后阻塞,竞争切换后继续竞争锁,稍微牺牲了公平性,但获得了高吞吐量。在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。 56 | -------------------------------------------------------------------------------- /src/01-java-interview/01-java-base-interview/01Collection.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | > 欢迎关注微信公众号:*Coder编程* 3 | > 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 4 | 5 | 本章主要介绍Collection集合相关知识,结合面试中会提到的相关问题进行知识点的梳理。希望能帮到大家~ 6 | 7 | **基于JDK1.8,如有错误,还望大家能够指出!** 8 | 9 | 涉及的Collection集合相关面试题 10 | - [1.什么是集合?](#1.什么是集合?) 11 | - [2.JAVA中集合类型都有哪些?各有什么特点?](#2.JAVA中集合类型都有哪些?各有什么特点?) 12 | - [3.说一说集合的父类Collection?](#3.说一说集合的父类Collection?) 13 | - [4.数组和集合都有哪些区别?](#4.数组和集合都有哪些区别?) 14 | - [5.说一说迭代器Iterator?](#5.说一说迭代器Iterator?) 15 | - [6.Collection接口中几种重要的类和接口简介?](#6.Collection接口中几种重要的类和接口简介?) 16 | 17 | 18 | ### 1.什么是集合? 19 | 20 | **来自百度百科的回答:** 21 | 集合类存放于java.util包中。 22 | 集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用(reference)。 23 | 集合类型主要有3种:set(集)、list(列表)和map(映射)。 24 | 集合接口分为:Collection和Map,list、set实现了Collection接口 25 | 26 | 27 | 28 | ### 2.JAVA中集合类型都有哪些?各有什么特点? 29 | 30 | Collection两大体系:链表List、集合Set 31 | List特点:元素有序;元素可以重复;元素都有索引(角标) 32 | 33 | List里存放的对象是有序的,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。 34 | 因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。 35 | 36 | Set特点:元素无序;元素不可以重复; 37 | 38 | Set里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。 39 | 40 | 同时集合中还有另外一种类型:Map(映射)。 41 | 42 | Map特点:键值对;键不可以重复;值可以重复; 43 | 44 | Map集合中存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。 45 | 46 | 47 | 48 | ### 3.说一说集合的父类Collection? 49 | 50 | #### 3.1 Collection 体系结构图 51 | 52 | ![](https://github.com/CoderMerlin/coder-programming/blob/master/src/01-java-interview/01-java-base-interview/images/01Collection.jpg?raw=true) 53 | 54 | Collection 55 | ├List (有序集合,允许相同元素和null) 56 | │├LinkedList (非同步,允许相同元素和null,遍历效率低插入和删除效率高) 57 | │├ArrayList (非同步,允许相同元素和null,实现了动态大小的数组,遍历效率高,用的多) 58 | │└Vector(同步,允许相同元素和null,效率低) 59 | │ └Stack(继承自Vector,实现一个后进先出的堆栈) 60 | └Set (无序集合,不允许相同元素,最多有一个null元素) 61 |      |-HashSet(无序集合,不允许相同元素,最多有一个null元素) 62 | 63 | Map (没有实现collection接口,key不能重复,value可以重复,一个key映射一个value) 64 | ├Hashtable (实现Map接口,同步,不允许null作为key和value,用自定义的类当作key的话要复写hashCode和eques方法,) 65 | ├HashMap (实现Map接口,非同步,允许null作为key和value,用的多) 66 | └WeakHashMap(实现Map接口) 67 | 68 | #### 3.2 Collection 中的主要方法 69 | 70 | (1)添加 71 | boolean add(E o); 72 | boolean add(Collection c); 73 | 74 | (2)删除 75 | boolean remove(Object o); 76 | boolean removeAll(Collection c) 77 | void clear(); 78 | 79 | (3)判断 80 | a.判断集合中是否有元素:boolean isEmpty(); 81 | b.判断集合中是否包含某个元素:boolean contains(Object o); 82 | c.判断集合中是否包含某些元素:boolean contains(Collection c); 83 | 84 | (4)获取 85 | a.获取集合中元素个数:int size(); 86 | b.遍历集合中所有元素:Iterator iterator(); 87 | c.判断两个集合中是否存在相同的元素并保留两个集合中相同的元素删除不同的元素:boolean retainAll(Collection c); 88 | 89 | (5)其他 90 | 将集合中元素转为数组: 91 | 92 | a. Ojbect[] toArray(); 93 | b. T[] toArray(); 泛型 94 | Java8新增方法: 95 | 在 JDK 8 以后,Collection 接口还提供了从集合获取连续的或者并行流: 96 | Stream stream() 97 | Stream parallelStream() 98 | 于Collection接口相关还有一个抽象类AbstractCollection: 99 | AbstractCollection是一个抽象类,实现了Collection接口的部分功能,实现了一些最基本的通用操作,把复杂的和业务相关的延迟到子类实现。 100 | 在AbstractCollection中,主要实现了contains(), isEmpty(), toArray(), remove(), clear() 这几个操作。有兴趣的同学可以自行研究下,逻辑都比较简单。 101 | 特别注意:List接口扩展了一个一些方法,其中最重要,也是用的最多的是: 102 | E get(int index) 返回指定索引的元素 103 | 104 | 105 | ### 4.数组和集合都有哪些区别? 106 | 107 | **数组特点:** 108 | 109 | 1.数组本质上就是一段连续的内存空间,用于记录多个类型相同的数据; 110 | 2.数据一旦声明完毕,则内存空间固定不变; 111 | 3.插入和删除操作不方便,可能会移动大量的元素并导致效率太低; 112 | 4.支持下标访问,可以实现随机访问; 113 | 5.数组中的元素可以是基本数据类型,也可以使用引用数据类型。 114 | 115 | **集合特点:** 116 | 117 | 1.内存空间可以不连续,数据类型可以不相同; 118 | 2.集合的内存空间可以动态的调整; 119 | 3.集合的插入删除操作可以不移动大量元素; 120 | 4.部分支持下标访问,部分不支持; 121 | 5.集合中的元素必须是引用数据类型(你存储的是简单的int,它会自动装箱成Integer); 122 | 123 | 可以看出数组和集合在数据的存储,访问,类型,长度等都有不同的地方。 124 | 125 | 126 | ### 5.说一说迭代器 Iterator? 127 | (1)通过集合对象获取其对应的Iterator对象 128 | (2)判断是否存在下一个元素 129 | (3)取出该元素并将迭代器对象指向下一个元素 130 | 131 | Iterator iterator():取出元素的方式:迭代器。 132 | 该对象必须依赖于具体容器,因为每一个容器的数据结构都不同。 133 | 所以该迭代器对象是在容器中进行内部实现的。 134 | 对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可,也就是iterator方法。 135 | **扩展知识:** 136 | ArrayList里面的iterator方法采用了设计模式中的——工厂方法模式! 137 | 有兴趣的同学可以后续看到我另外的文章“设计模式精讲”专栏。 138 | 139 | ### 6.Collection接口中几种重要的类和接口简介? 140 | 该问题与第二个问题类似~ 后续文章会有更详细的介绍! 141 | Collection两大体系:链表List、集合Set 另映射Map 142 | 143 | List接口及子类介绍 144 |   List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素, 145 | 这类似于Java的数组。 和下面要提到的Set不同,List允许有相同的元素。 除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比, 146 | ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。 147 |   实现List接口的常用类有: 148 | - LinkedList类 (底层数据结构是链表,线程不安全) 149 | - ArrayList类 (底层数据结构是数组,线程不安全) 150 | - Vector类  (底层数据结构是数组,线程安全) 151 | - Stack类 (顾名思义:栈,继承至Vector类并进行扩展) 152 | 在后续的文章中我们将一一详细介绍这些类的相关特性! 153 | 154 | Set接口及子类介绍 155 | Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。    156 | 注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。 157 | 实现Set接口的常用类有: 158 | - HashSet类 (底层数据结构是数组+单链表+红黑树,无序) 159 | - LinkedHashSet (底层数据结构是数组+单链表+红黑树+双向链表,无序) 160 | - TreeSet类 (底层数据结构红黑树,有序) 161 | 在后续的文章中我们将一一详细介绍这些类的相关特性! 162 | 163 | Map接口及子类介绍   164 | 注意:Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。 165 | Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。 166 | - Hashtable类 167 | - HashMap类    168 | 在后续的文章中我们将一一详细介绍这些类的相关特性! 169 | 170 | ## 文末 171 | 本章节介绍了Collection接口中的大部分可能在面试过程中会出现的内容, 172 | 并没有详细去介绍其子类及其实现相关的原理。这方面的内容会放在后续的章节中去详细介绍。 173 | 174 | > 欢迎关注微信公众号:*Coder编程* 175 | > 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 176 | -------------------------------------------------------------------------------- /src/01-java-interview/01-java-base-interview/README.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,欢迎关注微信公众号:Coder编程。内容将持续更新! # 2 | # :mega: 本章导航内容主要——JAVA面试中的基础知识! # 3 | 4 | 集合类面试题 5 | 6 | 涉及的Collection集合相关面试题 7 | - [1.什么是集合?](01Collection.md) 8 | - [2.JAVA中集合类型都有哪些?各有什么特点?](01Collection.md) 9 | - [3.说一说集合的父类Collection?](01Collection.md) 10 | - [4.数组和集合都有哪些区别?](01Collection.md) 11 | - [5.说一说迭代器Iterator?](01Collection.md) 12 | - [6.Collection接口中几种重要的类和接口简介?](01Collection.md) 13 | --- 14 | 15 | ### 2.2.1 说说List,Set,Map三者的区别? 16 | 17 | - **List(对付顺序的好帮手):** List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象 18 | - **Set(注重独一无二的性质):** 不允许重复的集合。不会有多个元素引用相同的对象。 19 | - **Map(用Key来搜索的专家):** 使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。 20 | 21 | ### 2.2.2 Arraylist 与 LinkedList 区别? 22 | 23 | - **1. 是否保证线程安全:** `ArrayList` 和 `LinkedList` 都是不同步的,也就是不保证线程安全; 24 | 25 | - **2. 底层数据结构:** `Arraylist` 底层使用的是 **`Object` 数组**;`LinkedList` 底层使用的是 **双向链表** 数据结构(JDK1.6之前为循环链表,JDK1.7取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!) 26 | 27 | - **3. 插入和删除是否受元素位置的影响:** ① **`ArrayList` 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。** 比如:执行`add(E e) `方法的时候, `ArrayList` 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(`add(int index, E element) `)时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② **`LinkedList` 采用链表存储,所以对于`add(E e)`方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置`i`插入和删除元素的话(`(add(int index, E element)`) 时间复杂度近似为`o(n))`因为需要先移动到指定位置再插入。** 28 | 29 | - **4. 是否支持快速随机访问:** `LinkedList` 不支持高效的随机元素访问,而 `ArrayList` 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于`get(int index) `方法)。 30 | 31 | - **5. 内存空间占用:** ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。 32 | 33 | #### **补充内容:RandomAccess接口** 34 | 35 | ```java 36 | public interface RandomAccess { 37 | } 38 | ``` 39 | 40 | 查看源码我们发现实际上 `RandomAccess` 接口中什么都没有定义。所以,在我看来 `RandomAccess` 接口不过是一个标识罢了。标识什么? 标识实现这个接口的类具有随机访问功能。 41 | 42 | 在 `binarySearch(`)方法中,它要判断传入的list 是否 `RamdomAccess` 的实例,如果是,调用`indexedBinarySearch()`方法,如果不是,那么调用`iteratorBinarySearch()`方法 43 | 44 | ```java 45 | public static 46 | int binarySearch(List> list, T key) { 47 | if (list instanceof RandomAccess || list.size() MAXIMUM_CAPACITY) 97 | initialCapacity = MAXIMUM_CAPACITY; 98 | if (loadFactor <= 0 || Float.isNaN(loadFactor)) 99 | throw new IllegalArgumentException("Illegal load factor: " + 100 | loadFactor); 101 | this.loadFactor = loadFactor; 102 | this.threshold = tableSizeFor(initialCapacity); 103 | } 104 | public HashMap(int initialCapacity) { 105 | this(initialCapacity, DEFAULT_LOAD_FACTOR); 106 | } 107 | ``` 108 | 109 | 下面这个方法保证了 HashMap 总是使用2的幂作为哈希表的大小。 110 | 111 | ```java 112 | /** 113 | * Returns a power of two size for the given target capacity. 114 | */ 115 | static final int tableSizeFor(int cap) { 116 | int n = cap - 1; 117 | n |= n >>> 1; 118 | n |= n >>> 2; 119 | n |= n >>> 4; 120 | n |= n >>> 8; 121 | n |= n >>> 16; 122 | return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; 123 | } 124 | ``` 125 | 126 | ### 2.2.6 HashMap 和 HashSet区别 127 | 128 | 如果你看过 `HashSet` 源码的话就应该知道:HashSet 底层就是基于 HashMap 实现的。(HashSet 的源码非常非常少,因为除了 `clone() `、`writeObject()`、`readObject()`是 HashSet 自己不得不实现之外,其他方法都是直接调用 HashMap 中的方法。 129 | 130 | | HashMap | HashSet | 131 | | :------------------------------: | :----------------------------------------------------------: | 132 | | 实现了Map接口 | 实现Set接口 | 133 | | 存储键值对 | 仅存储对象 | 134 | | 调用 `put()`向map中添加元素 | 调用 `add()`方法向Set中添加元素 | 135 | | HashMap使用键(Key)计算Hashcode | HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性, | 136 | 137 | ### 2.2.7 HashSet如何检查重复 138 | 139 | 当你把对象加入`HashSet`时,HashSet会先计算对象的`hashcode`值来判断对象加入的位置,同时也会与其他加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用`equals()`方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让加入操作成功。(摘自我的Java启蒙书《Head fist java》第二版) 140 | 141 | **hashCode()与equals()的相关规定:** 142 | 143 | 1. 如果两个对象相等,则hashcode一定也是相同的 144 | 2. 两个对象相等,对两个equals方法返回true 145 | 3. 两个对象有相同的hashcode值,它们也不一定是相等的 146 | 4. 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖 147 | 5. hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。 148 | 149 | **==与equals的区别** 150 | 151 | 1. ==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同 152 | 2. ==是指对内存地址进行比较 equals()是对字符串的内容进行比较 153 | 3. ==指引用是否相同 equals()指的是值是否相同 154 | 155 | 156 | ### 2.2.8 HashMap的底层实现 157 | 158 | #### JDK1.8之前 159 | 160 | JDK1.8 之前 `HashMap` 底层是 **数组和链表** 结合在一起使用也就是 **链表散列**。**HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。** 161 | 162 | **所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的 hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。** 163 | 164 | **JDK 1.8 HashMap 的 hash 方法源码:** 165 | 166 | JDK 1.8 的 hash方法 相比于 JDK 1.7 hash 方法更加简化,但是原理不变。 167 | 168 | ```java 169 | static final int hash(Object key) { 170 | int h; 171 | // key.hashCode():返回散列值也就是hashcode 172 | // ^ :按位异或 173 | // >>>:无符号右移,忽略符号位,空位都以0补齐 174 | return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); 175 | } 176 | ``` 177 | 178 | 对比一下 JDK1.7的 HashMap 的 hash 方法源码. 179 | 180 | ```java 181 | static int hash(int h) { 182 | // This function ensures that hashCodes that differ only by 183 | // constant multiples at each bit position have a bounded 184 | // number of collisions (approximately 8 at default load factor). 185 | 186 | h ^= (h >>> 20) ^ (h >>> 12); 187 | return h ^ (h >>> 7) ^ (h >>> 4); 188 | } 189 | ``` 190 | 191 | 相比于 JDK1.8 的 hash 方法 ,JDK 1.7 的 hash 方法的性能会稍差一点点,因为毕竟扰动了 4 次。 192 | 193 | 所谓 **“拉链法”** 就是:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。 194 | 195 | ![jdk1.8之前的内部结构-HashMap](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/jdk1.8之前的内部结构-HashMap.jpg) 196 | 197 | #### JDK1.8之后 198 | 199 | 相比于之前的版本, JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。 200 | 201 | ![jdk1.8之后的内部结构-HashMap](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/JDK1.8之后的HashMap底层数据结构.jpg) 202 | 203 | > TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。 204 | 205 | **推荐阅读:** 206 | 207 | - 《Java 8系列之重新认识HashMap》 : 208 | 209 | ### 2.2.9 HashMap 的长度为什么是2的幂次方 210 | 211 | 为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们上面也讲到了过了,Hash 值的范围值-2147483648到2147483647,前后加起来大概40亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个40亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是“ `(n - 1) & hash`”。(n代表数组长度)。这也就解释了 HashMap 的长度为什么是2的幂次方。 212 | 213 | **这个算法应该如何设计呢?** 214 | 215 | 我们首先可能会想到采用%取余的操作来实现。但是,重点来了:**“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。”** 并且 **采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。** 216 | 217 | ### 2.2.10 HashMap 多线程操作导致死循环问题 218 | 219 | 主要原因在于 并发下的Rehash 会造成元素之间会形成一个循环链表。不过,jdk 1.8 后解决了这个问题,但是还是不建议在多线程下使用 HashMap,因为多线程下使用 HashMap 还是会存在其他问题比如数据丢失。并发环境下推荐使用 ConcurrentHashMap 。 220 | 221 | 详情请查看: 222 | 223 | ### 2.2.11 ConcurrentHashMap 和 Hashtable 的区别 224 | 225 | ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。 226 | 227 | - **底层数据结构:** JDK1.7的 ConcurrentHashMap 底层采用 **分段的数组+链表** 实现,JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 **数组+链表** 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的; 228 | - **实现线程安全的方式(重要):** ① **在JDK1.7的时候,ConcurrentHashMap(分段锁)** 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。 **到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化)** 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;② **Hashtable(同一把锁)** :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。 229 | 230 | **两者的对比图:** 231 | 232 | 图片来源: 233 | 234 | **HashTable:** 235 | 236 | ![HashTable全表锁](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/HashTable全表锁.png) 237 | 238 | **JDK1.7的ConcurrentHashMap:** 239 | 240 | ![JDK1.7的ConcurrentHashMap](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/ConcurrentHashMap分段锁.jpg) 241 | 242 | **JDK1.8的ConcurrentHashMap(TreeBin: 红黑二叉树节点 Node: 链表节点):** 243 | 244 | ![JDK1.8的ConcurrentHashMap](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/JDK1.8-ConcurrentHashMap-Structure.jpg) 245 | 246 | ### 2.2.12 ConcurrentHashMap线程安全的具体实现方式/底层具体实现 247 | 248 | #### JDK1.7(上面有示意图) 249 | 250 | 首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。 251 | 252 | **ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成**。 253 | 254 | Segment 实现了 ReentrantLock,所以 Segment 是一种可重入锁,扮演锁的角色。HashEntry 用于存储键值对数据。 255 | 256 | ```java 257 | static class Segment extends ReentrantLock implements Serializable { 258 | } 259 | ``` 260 | 261 | 一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和HashMap类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个HashEntry数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment的锁。 262 | 263 | #### JDK1.8 (上面有示意图) 264 | 265 | ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构类似,数组+链表/红黑二叉树。Java 8在链表长度超过一定阈值(8)时将链表(寻址时间复杂度为O(N))转换为红黑树(寻址时间复杂度为O(log(N))) 266 | 267 | synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。 268 | 269 | ### 2.2.13 comparable 和 Comparator的区别 270 | 271 | - comparable接口实际上是出自java.lang包 它有一个 `compareTo(Object obj)`方法用来排序 272 | - comparator接口实际上是出自 java.util 包它有一个`compare(Object obj1, Object obj2)`方法用来排序 273 | 274 | 一般我们需要对一个集合使用自定义排序时,我们就要重写`compareTo()`方法或`compare()`方法,当我们需要对某一个集合实现两种排序方式,比如一个song对象中的歌名和歌手名分别采用一种排序方法的话,我们可以重写`compareTo()`方法和使用自制的Comparator方法或者以两个Comparator来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的 `Collections.sort()`. 275 | 276 | #### Comparator定制排序 277 | 278 | ```java 279 | ArrayList arrayList = new ArrayList(); 280 | arrayList.add(-1); 281 | arrayList.add(3); 282 | arrayList.add(3); 283 | arrayList.add(-5); 284 | arrayList.add(7); 285 | arrayList.add(4); 286 | arrayList.add(-9); 287 | arrayList.add(-7); 288 | System.out.println("原始数组:"); 289 | System.out.println(arrayList); 290 | // void reverse(List list):反转 291 | Collections.reverse(arrayList); 292 | System.out.println("Collections.reverse(arrayList):"); 293 | System.out.println(arrayList); 294 | 295 | // void sort(List list),按自然排序的升序排序 296 | Collections.sort(arrayList); 297 | System.out.println("Collections.sort(arrayList):"); 298 | System.out.println(arrayList); 299 | // 定制排序的用法 300 | Collections.sort(arrayList, new Comparator() { 301 | 302 | @Override 303 | public int compare(Integer o1, Integer o2) { 304 | return o2.compareTo(o1); 305 | } 306 | }); 307 | System.out.println("定制排序后:"); 308 | System.out.println(arrayList); 309 | ``` 310 | 311 | Output: 312 | 313 | ``` 314 | 原始数组: 315 | [-1, 3, 3, -5, 7, 4, -9, -7] 316 | Collections.reverse(arrayList): 317 | [-7, -9, 4, 7, -5, 3, 3, -1] 318 | Collections.sort(arrayList): 319 | [-9, -7, -5, -1, 3, 3, 4, 7] 320 | 定制排序后: 321 | [7, 4, 3, 3, -1, -5, -7, -9] 322 | ``` 323 | 324 | #### 重写compareTo方法实现按年龄来排序 325 | 326 | ```java 327 | // person对象没有实现Comparable接口,所以必须实现,这样才不会出错,才可以使treemap中的数据按顺序排列 328 | // 前面一个例子的String类已经默认实现了Comparable接口,详细可以查看String类的API文档,另外其他 329 | // 像Integer类等都已经实现了Comparable接口,所以不需要另外实现了 330 | 331 | public class Person implements Comparable { 332 | private String name; 333 | private int age; 334 | 335 | public Person(String name, int age) { 336 | super(); 337 | this.name = name; 338 | this.age = age; 339 | } 340 | 341 | public String getName() { 342 | return name; 343 | } 344 | 345 | public void setName(String name) { 346 | this.name = name; 347 | } 348 | 349 | public int getAge() { 350 | return age; 351 | } 352 | 353 | public void setAge(int age) { 354 | this.age = age; 355 | } 356 | 357 | /** 358 | * TODO重写compareTo方法实现按年龄来排序 359 | */ 360 | @Override 361 | public int compareTo(Person o) { 362 | // TODO Auto-generated method stub 363 | if (this.age > o.getAge()) { 364 | return 1; 365 | } else if (this.age < o.getAge()) { 366 | return -1; 367 | } 368 | return age; 369 | } 370 | } 371 | 372 | ``` 373 | 374 | ```java 375 | public static void main(String[] args) { 376 | TreeMap pdata = new TreeMap(); 377 | pdata.put(new Person("张三", 30), "zhangsan"); 378 | pdata.put(new Person("李四", 20), "lisi"); 379 | pdata.put(new Person("王五", 10), "wangwu"); 380 | pdata.put(new Person("小红", 5), "xiaohong"); 381 | // 得到key的值的同时得到key所对应的值 382 | Set keys = pdata.keySet(); 383 | for (Person key : keys) { 384 | System.out.println(key.getAge() + "-" + key.getName()); 385 | 386 | } 387 | } 388 | ``` 389 | 390 | Output: 391 | 392 | ``` 393 | 5-小红 394 | 10-王五 395 | 20-李四 396 | 30-张三 397 | ``` 398 | 399 | ### 2.2.14 集合框架底层数据结构总结 400 | 401 | #### Collection 402 | 403 | **1. List** 404 | 405 | - **Arraylist:** Object数组 406 | - **Vector:** Object数组 407 | - **LinkedList:** 双向链表(JDK1.6之前为循环链表,JDK1.7取消了循环) 408 | 409 | **2. Set** 410 | 411 | - **HashSet(无序,唯一):** 基于 HashMap 实现的,底层采用 HashMap 来保存元素 412 | - **LinkedHashSet:** LinkedHashSet 继承于 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 HashMap 实现一样,不过还是有一点点区别的 413 | - **TreeSet(有序,唯一):** 红黑树(自平衡的排序二叉树) 414 | 415 | #### Map 416 | 417 | - **HashMap:** JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间 418 | - **LinkedHashMap:** LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:[《LinkedHashMap 源码详细分析(JDK1.8)》](https://www.imooc.com/article/22931) 419 | - **Hashtable:** 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的 420 | - **TreeMap:** 红黑树(自平衡的排序二叉树) 421 | 422 | ### 2.2.15 如何选用集合? 423 | 424 | 主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用Map接口下的集合,需要排序时选择TreeMap,不需要排序时就选择HashMap,需要保证线程安全就选用ConcurrentHashMap.当我们只需要存放元素值时,就选择实现Collection接口的集合,需要保证元素唯一时选择实现Set接口的集合比如TreeSet或HashSet,不需要就选择实现List接口的比如ArrayList或LinkedList,然后再根据实现这些接口的集合的特点来选用。 425 | 426 | -------------------------------------------------------------------------------- /src/01-java-interview/01-java-base-interview/images/01Collection.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoderMerlin/coder-programming/75cd46b991771cbea0199c974eb7670acf248fc5/src/01-java-interview/01-java-base-interview/images/01Collection.jpg -------------------------------------------------------------------------------- /src/01-java-interview/02-java-data-base-interview/README.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,欢迎关注微信公众号:Coder编程。内容将持续更新! # 2 | # :mega: 本章主要介绍JAVA基础知识中的——数据库相关知识面试题! # 3 | -------------------------------------------------------------------------------- /src/01-java-interview/03-java-thread-interview/ProcessAndThread.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | > 欢迎关注公众号:**Coder编程** 3 | > 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 4 | 5 | 本章主要介绍进程与线程的区别与联系相关知识点,也是我们面试过程中,经常会问到的了一个问题。希望通过这篇文章,能让大家理解相关知识点~ 6 | 7 | 8 | 涉及面试题: 9 | - 1.进程与线程之间有什么区别? 10 | - 2.进程、线程都各有什么特点? 11 | - 3.进程之间的是怎么进行交互的呢? 12 | - 4.什么是缓冲区溢出? 13 | - 5.进程之间如何进行交互? 14 | - 6.线程之间如何进行交互? 15 | 16 | >上面的面试题可以看出,其实都是一回事,只是换了一种提问方式,只要我们能掌握核心要点,随便面试官怎么提问,我们都能轻松应对! 17 | 18 | ## 1. 小栗子: 19 | 我们生活中有许许多多关于进程与线程的小栗子,比如:1.我们使用打开一个微信软件,这个时候就开启了一个进程, 20 | 当我们在微信里面进行各种操作(查看朋友圈,扫一扫...),这么多的操作就是线程。 21 | 所以我们可以说“进程”是包含“线程”的,“线程”是“进程”的一个子集。 22 | 23 | > **来源百度百科:** 24 | 25 | **进程(Process)** 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。 26 | 27 | **线程(thread)**是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 28 | 29 | > 我们简单总结下: 30 | 31 | 进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程——资源分配的最小单位。 32 | 33 | 线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程——程序执行的最小单位。 34 | 35 | 36 | ## 2. 深入理解: 37 | 38 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190320210031603.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 39 | ### 2.1 进程(线程+内存+文件/网络句柄) 40 | 我们通过上面的图片进行进一步理解: 41 | 42 | **“内存”:** 43 | 我们通常所理解的内存是我们所见到的(2G/4G/8G/16G)物理内存,它为什么会在进程之中呢? 44 | 实际上,这里的内存是逻辑内存。指的是内存的寻址空间。每个进程的内存是相互独立的。 45 | 否则的话会出现一个问题:我们把指针的值改一改就指向其他进程的内存了,通过这样我们岂不是就可以看到其他进程中"微信"或者是"网上银行"的信息, 46 | 这样的话,那我们的微信聊天记录或者是银行账户的信息就都被别人找到了,这是一个很危险的信号!显然这样是不可能的。 47 | 48 | **“文件/网络句柄”:** 49 | 它们是所有的进程所共有的,例如打开同一个文件,去抢同一个网络的端口这样的操作是被允许的。 50 | 51 | **“线程”:** 52 | 接下来,我们就要介绍一下我们的“线程”有关知识 53 | 54 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190320210656574.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 55 | 56 | ### 2.2 线程(栈+PC+TLS) 57 | 58 | #### 2.2.1 栈: 59 | 60 | 我们通常都是说调用堆栈,其实这里的堆是没有含义的,调用堆栈就是调用栈的意思。 61 | 那么我们的栈里面有什么呢? 62 | 我们从主线程的入口main函数,会不断的进行函数调用, 63 | 每次调用的时候,会把所有的参数和返回地址压入到栈中。 64 | 65 | #### 2.2.2 PC: 66 | 67 | Program Counter 程序计数器,操作系统真正运行的是一个个的线程, 68 | 而我们的进程只是它的一个容器。PC就是指向当前的指令,而这个指令是放在内存中。 69 | 每个线程都有一串自己的指针,去指向自己当前所在内存的指针。 70 | 计算机绝大部分是存储程序性的,说的就是我们的数据和程序是存储在同一片内存里的 71 | 这个内存中既有我们的数据变量又有我们的程序。所以我们的PC指针就是指向我们的内存的。 72 | 73 | ##### 2.2.2.1 缓冲区溢出 74 | 例如我们经常听到一个漏洞:**缓冲区溢出** 75 | 这是什么意思呢? 76 | 例如:我们有个地方要输入用户名,本来是用来存数据的地方。 77 | 然后黑客把数据输入的特别长。这个长度超出了我们给数据存储的内存区,这时候跑到了 78 | 我们给程序分配的一部分内存中。黑客就可以通过这种办法将他所要运行的代码 79 | 写入到用户名框中,来植入进来。我们的解决方法就是,用用户名的长度来限制不要超过 80 | 用户名的缓冲区的大小来解决。 81 | 82 | #### 2.3 TLS: 83 | 84 | 全称:thread local storage 85 | 之前我们看到每个进程都有自己独立的内存,这时候我们想,我们的线程有没有一块独立的内存呢?答案是有的,就是TLS。 86 | 可以用来存储我们线程所独有的数据。 87 | 可以看到:线程才是我们操作系统所真正去运行的,而进程呢,则是像容器一样他把需要的一些东西放在了一起,而把不需要的东西做了一层隔离,进行隔离开来。 88 | 89 | ### 3. 进程之间的是怎么进行交互的呢? 90 | 通过TCP/IP的端口来实现 91 | ```在后续的文章中我们将一一详细介绍!``` 92 | 93 | ### 4. 线程之间又是怎样进行交互? 94 | 线程的通信就比较简单,有一大块共享的内存,只要大家的指针是同一个就可以看到各自的内存。 95 | ```在后续的文章中我们将一一详细介绍!``` 96 | 97 | ### 5.小结: 98 | 1.进程要分配一大部分的内存,而线程只需要分配一部分栈就可以了. 99 | 2.一个程序至少有一个进程,一个进程至少有一个线程. 100 | 3.进程是资源分配的最小单位,线程是程序执行的最小单位。 101 | 4.一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行. 102 | 103 | ## 文末 104 | 105 | >本章节介绍了进程与线程之间的区别与联系,以及其他方面的小知识点,也是面试过程中会出现的内容点。 106 | 里面涉及到了许多的小知识点,我们并没有扩展开来讲解,会放在今后的文章中做进一步的阐述。 107 | 欢迎关注公众号:**Coder编程** 108 | 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 109 | 110 | [Github个人主页目录](https://github.com/CoderMerlin/coder-programming) 111 | [Gitee个人主页目录](https://gitee.com/573059382/coder-programming) 112 | 113 | **欢迎大家关注并Star~** -------------------------------------------------------------------------------- /src/01-java-interview/03-java-thread-interview/README.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,欢迎关注微信公众号:Coder编程。内容将持续更新! # 2 | # :mega: 本章主要介绍JAVA基础知识中的——线程相关知识面试题! # 3 | 4 | ### 线程类知识点 ### 5 | 6 | - [1.进程与线程之间有什么区别?](ProcessAndThread.md) 7 | - [2.进程、线程都各有什么特点?](ProcessAndThread.md) 8 | - [3.进程之间的是怎么进行交互的呢?](ProcessAndThread.md) 9 | - [4.什么是缓冲区溢出?](ProcessAndThread.md) 10 | - [5.进程之间如何进行交互?](ProcessAndThread.md) 11 | - [6.线程之间如何进行交互?](ProcessAndThread.md) 12 | 13 | 14 | 15 | --- 16 | 17 | ### 2.3.1. 什么是线程和进程? 18 | 19 | #### 何为进程? 20 | 21 | 进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。 22 | 23 | 在 Java 中,当我们启动 main 函数时其实就是启动了一个 JVM 的进程,而 main 函数所在的线程就是这个进程中的一个线程,也称主线程。 24 | 25 | 如下图所示,在 windows 中通过查看任务管理器的方式,我们就可以清楚看到 window 当前运行的进程(.exe 文件的运行)。 26 | 27 | ![进程示例图片-Windows](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/进程示例图片-Windows.png) 28 | 29 | #### 何为线程? 30 | 31 | 线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享进程的**堆**和**方法区**资源,但每个线程有自己的**程序计数器**、**虚拟机栈**和**本地方法栈**,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。 32 | 33 | Java 程序天生就是多线程程序,我们可以通过 JMX 来看一下一个普通的 Java 程序有哪些线程,代码如下。 34 | 35 | ```java 36 | public class MultiThread { 37 | public static void main(String[] args) { 38 | // 获取 Java 线程管理 MXBean 39 | ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); 40 | // 不需要获取同步的 monitor 和 synchronizer 信息,仅获取线程和线程堆栈信息 41 | ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false); 42 | // 遍历线程信息,仅打印线程 ID 和线程名称信息 43 | for (ThreadInfo threadInfo : threadInfos) { 44 | System.out.println("[" + threadInfo.getThreadId() + "] " + threadInfo.getThreadName()); 45 | } 46 | } 47 | } 48 | ``` 49 | 50 | 上述程序输出如下(输出内容可能不同,不用太纠结下面每个线程的作用,只用知道 main 线程执行 main 方法即可): 51 | 52 | ``` 53 | [5] Attach Listener //添加事件 54 | [4] Signal Dispatcher // 分发处理给 JVM 信号的线程 55 | [3] Finalizer //调用对象 finalize 方法的线程 56 | [2] Reference Handler //清除 reference 线程 57 | [1] main //main 线程,程序入口 58 | ``` 59 | 60 | 从上面的输出内容可以看出:**一个 Java 程序的运行是 main 线程和多个其他线程同时运行**。 61 | 62 | ### 2.3.2. 请简要描述线程与进程的关系,区别及优缺点? 63 | 64 | **从 JVM 角度说进程和线程之间的关系** 65 | 66 | #### 图解进程和线程的关系 67 | 68 | 下图是 Java 内存区域,通过下图我们从 JVM 的角度来说一下线程和进程之间的关系。如果你对 Java 内存区域 (运行时数据区) 这部分知识不太了解的话可以阅读一下这篇文章:[《可能是把 Java 内存区域讲的最清楚的一篇文章》](https://github.com/Snailclimb/JavaGuide/blob/3965c02cc0f294b0bd3580df4868d5e396959e2e/Java%E7%9B%B8%E5%85%B3/%E5%8F%AF%E8%83%BD%E6%98%AF%E6%8A%8AJava%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F%E8%AE%B2%E7%9A%84%E6%9C%80%E6%B8%85%E6%A5%9A%E7%9A%84%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0.md "《可能是把 Java 内存区域讲的最清楚的一篇文章》") 69 | 70 |
71 | 72 |
73 | 74 | 从上图可以看出:一个进程中可以有多个线程,多个线程共享进程的**堆**和**方法区 (JDK1.8 之后的元空间)**资源,但是每个线程有自己的**程序计数器**、**虚拟机栈** 和 **本地方法栈**。 75 | 76 | **总结:** 线程 是 进程 划分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。线程执行开销小,但不利于资源的管理和保护;而进程正相反 77 | 78 | 下面是该知识点的扩展内容! 79 | 80 | 下面来思考这样一个问题:为什么**程序计数器**、**虚拟机栈**和**本地方法栈**是线程私有的呢?为什么堆和方法区是线程共享的呢? 81 | 82 | 83 | #### 程序计数器为什么是私有的? 84 | 85 | 程序计数器主要有下面两个作用: 86 | 87 | 1. 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。 88 | 2. 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。 89 | 90 | 需要注意的是,如果执行的是 native 方法,那么程序计数器记录的是 undefined 地址,只有执行的是 Java 代码时程序计数器记录的才是下一条指令的地址。 91 | 92 | 所以,程序计数器私有主要是为了**线程切换后能恢复到正确的执行位置**。 93 | 94 | 95 | #### 虚拟机栈和本地方法栈为什么是私有的? 96 | 97 | - **虚拟机栈:** 每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。 98 | - **本地方法栈:** 和虚拟机栈所发挥的作用非常相似,区别是: **虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。** 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。 99 | 100 | 所以,为了**保证线程中的局部变量不被别的线程访问到**,虚拟机栈和本地方法栈是线程私有的。 101 | 102 | 103 | #### 一句话简单了解堆和方法区 104 | 105 | 堆和方法区是所有线程共享的资源,其中堆是进程中最大的一块内存,主要用于存放新创建的对象 (所有对象都在这里分配内存),方法区主要用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 106 | 107 | ### 2.3.3. 说说并发与并行的区别? 108 | 109 | - **并发:** 同一时间段,多个任务都在执行 (单位时间内不一定同时执行); 110 | - **并行:** 单位时间内,多个任务同时执行。 111 | 112 | ### 2.3.4. 为什么要使用多线程呢? 113 | 114 | 先从总体上来说: 115 | 116 | - **从计算机底层来说:** 线程可以比作是轻量级的进程,是程序执行的最小单位,线程间的切换和调度的成本远远小于进程。另外,多核 CPU 时代意味着多个线程可以同时运行,这减少了线程上下文切换的开销。 117 | - **从当代互联网发展趋势来说:** 现在的系统动不动就要求百万级甚至千万级的并发量,而多线程并发编程正是开发高并发系统的基础,利用好多线程机制可以大大提高系统整体的并发能力以及性能。 118 | 119 | 再深入到计算机底层来探讨: 120 | 121 | - **单核时代:** 在单核时代多线程主要是为了提高 CPU 和 IO 设备的综合利用率。举个例子:当只有一个线程的时候会导致 CPU 计算时,IO 设备空闲;进行 IO 操作时,CPU 空闲。我们可以简单地说这两者的利用率目前都是 50%左右。但是当有两个线程的时候就不一样了,当一个线程执行 CPU 计算时,另外一个线程可以进行 IO 操作,这样两个的利用率就可以在理想情况下达到 100%了。 122 | - **多核时代:** 多核时代多线程主要是为了提高 CPU 利用率。举个例子:假如我们要计算一个复杂的任务,我们只用一个线程的话,CPU 只会一个 CPU 核心被利用到,而创建多个线程就可以让多个 CPU 核心被利用到,这样就提高了 CPU 的利用率。 123 | 124 | ### 2.3.5. 使用多线程可能带来什么问题? 125 | 126 | 并发编程的目的就是为了能提高程序的执行效率提高程序运行速度,但是并发编程并不总是能提高程序运行速度的,而且并发编程可能会遇到很多问题,比如:内存泄漏、上下文切换、死锁还有受限于硬件和软件的资源闲置问题。 127 | 128 | ### 2.3.6. 说说线程的生命周期和状态? 129 | 130 | Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态(图源《Java 并发编程艺术》4.1.4 节)。 131 | 132 | ![Java 线程的状态 ](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/19-1-29/Java%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%8A%B6%E6%80%81.png) 133 | 134 | 线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。Java 线程状态变迁如下图所示(图源《Java 并发编程艺术》4.1.4 节): 135 | 136 | ![Java 线程状态变迁 ](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/19-1-29/Java+%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81%E5%8F%98%E8%BF%81.png) 137 | 138 | 由上图可以看出:线程创建之后它将处于 **NEW(新建)** 状态,调用 `start()` 方法后开始运行,线程这时候处于 **READY(可运行)** 状态。可运行状态的线程获得了 CPU 时间片(timeslice)后就处于 **RUNNING(运行)** 状态。 139 | 140 | > 操作系统隐藏 Java 虚拟机(JVM)中的 RUNNABLE 和 RUNNING 状态,它只能看到 RUNNABLE 状态(图源:[HowToDoInJava](https://howtodoinjava.com/ "HowToDoInJava"):[Java Thread Life Cycle and Thread States](https://howtodoinjava.com/java/multi-threading/java-thread-life-cycle-and-thread-states/ "Java Thread Life Cycle and Thread States")),所以 Java 系统一般将这两个状态统称为 **RUNNABLE(运行中)** 状态 。 141 | 142 | ![RUNNABLE-VS-RUNNING](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3/RUNNABLE-VS-RUNNING.png) 143 | 144 | 当线程执行 `wait()`方法之后,线程进入 **WAITING(等待)** 状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 **TIME_WAITING(超时等待)** 状态相当于在等待状态的基础上增加了超时限制,比如通过 `sleep(long millis)`方法或 `wait(long millis)`方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 **BLOCKED(阻塞)** 状态。线程在执行 Runnable 的`run()`方法之后将会进入到 **TERMINATED(终止)** 状态。 145 | 146 | ### 2.3.7. 什么是上下文切换? 147 | 148 | 多线程编程中一般线程的个数都大于 CPU 核心的个数,而一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于一次上下文切换。 149 | 150 | 概括来说就是:当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。**任务从保存到再加载的过程就是一次上下文切换**。 151 | 152 | 上下文切换通常是计算密集型的。也就是说,它需要相当可观的处理器时间,在每秒几十上百次的切换中,每次切换都需要纳秒量级的时间。所以,上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。 153 | 154 | Linux 相比与其他操作系统(包括其他类 Unix 系统)有很多的优点,其中有一项就是,其上下文切换和模式切换的时间消耗非常少。 155 | 156 | ### 2.3.8. 什么是线程死锁?如何避免死锁? 157 | 158 | #### 认识线程死锁 159 | 160 | 线程死锁描述的是这样一种情况:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。 161 | 162 | 如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。 163 | 164 | ![线程死锁示意图 ](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-4/2019-4%E6%AD%BB%E9%94%811.png) 165 | 166 | 下面通过一个例子来说明线程死锁,代码模拟了上图的死锁的情况 (代码来源于《并发编程之美》): 167 | 168 | ```java 169 | public class DeadLockDemo { 170 | private static Object resource1 = new Object();//资源 1 171 | private static Object resource2 = new Object();//资源 2 172 | 173 | public static void main(String[] args) { 174 | new Thread(() -> { 175 | synchronized (resource1) { 176 | System.out.println(Thread.currentThread() + "get resource1"); 177 | try { 178 | Thread.sleep(1000); 179 | } catch (InterruptedException e) { 180 | e.printStackTrace(); 181 | } 182 | System.out.println(Thread.currentThread() + "waiting get resource2"); 183 | synchronized (resource2) { 184 | System.out.println(Thread.currentThread() + "get resource2"); 185 | } 186 | } 187 | }, "线程 1").start(); 188 | 189 | new Thread(() -> { 190 | synchronized (resource2) { 191 | System.out.println(Thread.currentThread() + "get resource2"); 192 | try { 193 | Thread.sleep(1000); 194 | } catch (InterruptedException e) { 195 | e.printStackTrace(); 196 | } 197 | System.out.println(Thread.currentThread() + "waiting get resource1"); 198 | synchronized (resource1) { 199 | System.out.println(Thread.currentThread() + "get resource1"); 200 | } 201 | } 202 | }, "线程 2").start(); 203 | } 204 | } 205 | ``` 206 | 207 | Output 208 | 209 | ``` 210 | Thread[线程 1,5,main]get resource1 211 | Thread[线程 2,5,main]get resource2 212 | Thread[线程 1,5,main]waiting get resource2 213 | Thread[线程 2,5,main]waiting get resource1 214 | ``` 215 | 216 | 线程 A 通过 synchronized (resource1) 获得 resource1 的监视器锁,然后通过`Thread.sleep(1000);`让线程 A 休眠 1s 为的是让线程 B 得到执行然后获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。上面的例子符合产生死锁的四个必要条件。 217 | 218 | 学过操作系统的朋友都知道产生死锁必须具备以下四个条件: 219 | 220 | 1. 互斥条件:该资源任意一个时刻只由一个线程占用。 221 | 2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 222 | 3. 不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。 223 | 4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 224 | 225 | #### 如何避免线程死锁? 226 | 227 | 我上面说了产生死锁的四个必要条件,为了避免死锁,我们只要破坏产生死锁的四个条件中的其中一个就可以了。现在我们来挨个分析一下: 228 | 229 | 1. **破坏互斥条件** :这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。 230 | 2. **破坏请求与保持条件** :一次性申请所有的资源。 231 | 3. **破坏不剥夺条件** :占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。 232 | 4. **破坏循环等待条件** :靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。 233 | 234 | 我们对线程 2 的代码修改成下面这样就不会产生死锁了。 235 | 236 | ```java 237 | new Thread(() -> { 238 | synchronized (resource1) { 239 | System.out.println(Thread.currentThread() + "get resource1"); 240 | try { 241 | Thread.sleep(1000); 242 | } catch (InterruptedException e) { 243 | e.printStackTrace(); 244 | } 245 | System.out.println(Thread.currentThread() + "waiting get resource2"); 246 | synchronized (resource2) { 247 | System.out.println(Thread.currentThread() + "get resource2"); 248 | } 249 | } 250 | }, "线程 2").start(); 251 | ``` 252 | 253 | Output 254 | 255 | ``` 256 | Thread[线程 1,5,main]get resource1 257 | Thread[线程 1,5,main]waiting get resource2 258 | Thread[线程 1,5,main]get resource2 259 | Thread[线程 2,5,main]get resource1 260 | Thread[线程 2,5,main]waiting get resource2 261 | Thread[线程 2,5,main]get resource2 262 | 263 | Process finished with exit code 0 264 | ``` 265 | 266 | **我们分析一下上面的代码为什么避免了死锁的发生?** 267 | 268 | 线程 1 首先获得到 resource1 的监视器锁,这时候线程 2 就获取不到了。然后线程 1 再去获取 resource2 的监视器锁,可以获取到。然后线程 1 释放了对 resource1、resource2 的监视器锁的占用,线程 2 获取到就可以执行了。这样就破坏了破坏循环等待条件,因此避免了死锁。 269 | 270 | ### 2.3.9. 说说 sleep() 方法和 wait() 方法区别和共同点? 271 | 272 | - 两者最主要的区别在于:**sleep 方法没有释放锁,而 wait 方法释放了锁** 。 273 | - 两者都可以暂停线程的执行。 274 | - Wait 通常被用于线程间交互/通信,sleep 通常被用于暂停执行。 275 | - wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。sleep() 方法执行完成后,线程会自动苏醒。或者可以使用 wait(long timeout)超时后线程会自动苏醒。 276 | 277 | ### 2.3.10. 为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法? 278 | 279 | 这是另一个非常经典的 java 多线程面试问题,而且在面试中会经常被问到。很简单,但是很多人都会答不上来! 280 | 281 | new 一个 Thread,线程进入了新建状态;调用 start() 方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。 而直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。 282 | 283 | **总结: 调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。** 284 | 285 | ### 2.3.11 synchronized 关键字 286 | 287 | #### 1.说一说自己对于 synchronized 关键字的了解 288 | 289 | synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。 290 | 291 | 另外,在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原生线程之上的。如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的 synchronized 效率低的原因。庆幸的是在 Java 6 之后 Java 官方对从 JVM 层面对synchronized 较大优化,所以现在的 synchronized 锁效率也优化得很不错了。JDK1.6对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。 292 | 293 | #### 2.说说自己是怎么使用 synchronized 关键字,在项目中用到了吗 294 | 295 | #### 3.synchronized关键字最主要的三种使用方式 296 | 297 | - **修饰实例方法:** 作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁 298 | - **修饰静态方法:** 也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员( static 表明这是该类的一个静态资源,不管new了多少个对象,只有一份)。所以如果一个线程A调用一个实例对象的非静态 synchronized 方法,而线程B需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,**因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁**。 299 | - **修饰代码块:** 指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。 300 | 301 | **总结:** synchronized 关键字加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁。synchronized 关键字加到实例方法上是给对象实例上锁。尽量不要使用 synchronized(String a) 因为JVM中,字符串常量池具有缓存功能! 302 | 303 | 下面我以一个常见的面试题为例讲解一下 synchronized 关键字的具体使用。 304 | 305 | 面试中面试官经常会说:“单例模式了解吗?来给我手写一下!给我解释一下双重检验锁方式实现单例模式的原理呗!” 306 | 307 | **双重校验锁实现对象单例(线程安全)** 308 | 309 | ```java 310 | public class Singleton { 311 | 312 | private volatile static Singleton uniqueInstance; 313 | 314 | private Singleton() { 315 | } 316 | 317 | public static Singleton getUniqueInstance() { 318 | //先判断对象是否已经实例过,没有实例化过才进入加锁代码 319 | if (uniqueInstance == null) { 320 | //类对象加锁 321 | synchronized (Singleton.class) { 322 | if (uniqueInstance == null) { 323 | uniqueInstance = new Singleton(); 324 | } 325 | } 326 | } 327 | return uniqueInstance; 328 | } 329 | } 330 | ``` 331 | 另外,需要注意 uniqueInstance 采用 volatile 关键字修饰也是很有必要。 332 | 333 | uniqueInstance 采用 volatile 关键字修饰也是很有必要的, uniqueInstance = new Singleton(); 这段代码其实是分为三步执行: 334 | 335 | 1. 为 uniqueInstance 分配内存空间 336 | 2. 初始化 uniqueInstance 337 | 3. 将 uniqueInstance 指向分配的内存地址 338 | 339 | 但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。 340 | 341 | 使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。 342 | 343 | #### 4.讲一下 synchronized 关键字的底层原理 344 | 345 | **synchronized 关键字底层原理属于 JVM 层面。** 346 | 347 | **① synchronized 同步语句块的情况** 348 | 349 | ```java 350 | public class SynchronizedDemo { 351 | public void method() { 352 | synchronized (this) { 353 | System.out.println("synchronized 代码块"); 354 | } 355 | } 356 | } 357 | 358 | ``` 359 | 360 | 通过 JDK 自带的 javap 命令查看 SynchronizedDemo 类的相关字节码信息:首先切换到类的对应目录执行 `javac SynchronizedDemo.java` 命令生成编译后的 .class 文件,然后执行`javap -c -s -v -l SynchronizedDemo.class`。 361 | 362 | ![synchronized关键字原理](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/synchronized关键字原理.png) 363 | 364 | 从上面我们可以看出: 365 | 366 | **synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。** 当执行 monitorenter 指令时,线程试图获取锁也就是获取 monitor(monitor对象存在于每个Java对象的对象头中,synchronized 锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因) 的持有权。当计数器为0则可以成功获取,获取后将锁计数器设为1也就是加1。相应的在执行 monitorexit 指令后,将锁计数器设为0,表明锁被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外一个线程释放为止。 367 | 368 | **② synchronized 修饰方法的的情况** 369 | 370 | ```java 371 | public class SynchronizedDemo2 { 372 | public synchronized void method() { 373 | System.out.println("synchronized 方法"); 374 | } 375 | } 376 | 377 | ``` 378 | 379 | ![synchronized关键字原理](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/synchronized关键字原理2.png) 380 | 381 | synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。 382 | 383 | #### 5.说说 JDK1.6 之后的synchronized 关键字底层做了哪些优化,可以详细介绍一下这些优化吗 384 | 385 | JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销。 386 | 387 | 锁主要存在四种状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。注意锁可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率。 388 | 389 | 关于这几种优化的详细信息可以查看笔主的这篇文章: 390 | 391 | #### 6.谈谈 synchronized和ReentrantLock 的区别 392 | 393 | 394 | **① 两者都是可重入锁** 395 | 396 | 两者都是可重入锁。“可重入锁”概念是:自己可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。同一个线程每次获取锁,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。 397 | 398 | **② synchronized 依赖于 JVM 而 ReentrantLock 依赖于 API** 399 | 400 | synchronized 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团队在 JDK1.6 为 synchronized 关键字进行了很多优化,但是这些优化都是在虚拟机层面实现的,并没有直接暴露给我们。ReentrantLock 是 JDK 层面实现的(也就是 API 层面,需要 lock() 和 unlock() 方法配合 try/finally 语句块来完成),所以我们可以通过查看它的源代码,来看它是如何实现的。 401 | 402 | **③ ReentrantLock 比 synchronized 增加了一些高级功能** 403 | 404 | 相比synchronized,ReentrantLock增加了一些高级功能。主要来说主要有三点:**①等待可中断;②可实现公平锁;③可实现选择性通知(锁可以绑定多个条件)** 405 | 406 | - **ReentrantLock提供了一种能够中断等待锁的线程的机制**,通过lock.lockInterruptibly()来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。 407 | - **ReentrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。** ReentrantLock默认情况是非公平的,可以通过 ReentrantLock类的`ReentrantLock(boolean fair)`构造方法来制定是否是公平的。 408 | - synchronized关键字与wait()和notify()/notifyAll()方法相结合可以实现等待/通知机制,ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition() 方法。Condition是JDK1.5之后才有的,它具有很好的灵活性,比如可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),**线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。 在使用notify()/notifyAll()方法进行通知时,被通知的线程是由 JVM 选择的,用ReentrantLock类结合Condition实例可以实现“选择性通知”** ,这个功能非常重要,而且是Condition接口默认提供的。而synchronized关键字就相当于整个Lock对象中只有一个Condition实例,所有的线程都注册在它一个身上。如果执行notifyAll()方法的话就会通知所有处于等待状态的线程这样会造成很大的效率问题,而Condition实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程。 409 | 410 | 如果你想使用上述功能,那么选择ReentrantLock是一个不错的选择。 411 | 412 | **④ 性能已不是选择标准** 413 | 414 | ### 2.3.12 volatile关键字 415 | 416 | #### 1. 讲一下Java内存模型 417 | 418 | 419 | 在 JDK1.2 之前,Java的内存模型实现总是从**主存**(即共享内存)读取变量,是不需要进行特别的注意的。而在当前的 Java 内存模型下,线程可以把变量保存**本地内存**(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成**数据的不一致**。 420 | 421 | ![数据不一致](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/数据不一致.png) 422 | 423 | 要解决这个问题,就需要把变量声明为**volatile**,这就指示 JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。 424 | 425 | 说白了, **volatile** 关键字的主要作用就是保证变量的可见性然后还有一个作用是防止指令重排序。 426 | 427 | ![volatile关键字的可见性](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/volatile关键字的可见性.png) 428 | 429 | #### 2 并发编程的三个重要特性 430 | 431 | 1. **原子性** : 一个的操作或者多次操作,要么所有的操作全部都得到执行并且不会收到任何因素的干扰而中断,要么所有的操作都执行,要么都不执行。`synchronized ` 可以保证代码片段的原子性。 432 | 2. **可见性** :当一个变量对共享变量进行了修改,那么另外的线程都是立即可以看到修改后的最新值。`volatile` 关键字可以保证共享变量的可见性。 433 | 3. **有序性** :代码在执行的过程中的先后顺序,Java 在编译器以及运行期间的优化,代码的执行顺序未必就是编写代码时候的顺序。`volatile` 关键字可以禁止指令进行重排序优化。 434 | 435 | #### 3. 说说 synchronized 关键字和 volatile 关键字的区别 436 | 437 | synchronized关键字和volatile关键字比较 438 | 439 | - **volatile关键字**是线程同步的**轻量级实现**,所以**volatile性能肯定比synchronized关键字要好**。但是**volatile关键字只能用于变量而synchronized关键字可以修饰方法以及代码块**。synchronized关键字在JavaSE1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁以及其它各种优化之后执行效率有了显著提升,**实际开发中使用 synchronized 关键字的场景还是更多一些**。 440 | - **多线程访问volatile关键字不会发生阻塞,而synchronized关键字可能会发生阻塞** 441 | - **volatile关键字能保证数据的可见性,但不能保证数据的原子性。synchronized关键字两者都能保证。** 442 | - **volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized关键字解决的是多个线程之间访问资源的同步性。** 443 | 444 | ### 2.3.13 ThreadLocal 445 | 446 | #### 1. ThreadLocal简介 447 | 448 | 通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。**如果想实现每一个线程都有自己的专属本地变量该如何解决呢?** JDK中提供的`ThreadLocal`类正是为了解决这样的问题。 **`ThreadLocal`类主要解决的就是让每个线程绑定自己的值,可以将`ThreadLocal`类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。** 449 | 450 | **如果你创建了一个`ThreadLocal`变量,那么访问这个变量的每个线程都会有这个变量的本地副本,这也是`ThreadLocal`变量名的由来。他们可以使用 `get()` 和 `set()` 方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题。** 451 | 452 | 再举个简单的例子: 453 | 454 | 比如有两个人去宝屋收集宝物,这两个共用一个袋子的话肯定会产生争执,但是给他们两个人每个人分配一个袋子的话就不会出现这样的问题。如果把这两个人比作线程的话,那么ThreadLocal就是用来避免这两个线程竞争的。 455 | 456 | #### 2. ThreadLocal示例 457 | 458 | 相信看了上面的解释,大家已经搞懂 ThreadLocal 类是个什么东西了。 459 | 460 | ```java 461 | import java.text.SimpleDateFormat; 462 | import java.util.Random; 463 | 464 | public class ThreadLocalExample implements Runnable{ 465 | 466 | // SimpleDateFormat 不是线程安全的,所以每个线程都要有自己独立的副本 467 | private static final ThreadLocal formatter = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd HHmm")); 468 | 469 | public static void main(String[] args) throws InterruptedException { 470 | ThreadLocalExample obj = new ThreadLocalExample(); 471 | for(int i=0 ; i<10; i++){ 472 | Thread t = new Thread(obj, ""+i); 473 | Thread.sleep(new Random().nextInt(1000)); 474 | t.start(); 475 | } 476 | } 477 | 478 | @Override 479 | public void run() { 480 | System.out.println("Thread Name= "+Thread.currentThread().getName()+" default Formatter = "+formatter.get().toPattern()); 481 | try { 482 | Thread.sleep(new Random().nextInt(1000)); 483 | } catch (InterruptedException e) { 484 | e.printStackTrace(); 485 | } 486 | //formatter pattern is changed here by thread, but it won't reflect to other threads 487 | formatter.set(new SimpleDateFormat()); 488 | 489 | System.out.println("Thread Name= "+Thread.currentThread().getName()+" formatter = "+formatter.get().toPattern()); 490 | } 491 | 492 | } 493 | 494 | ``` 495 | 496 | Output: 497 | 498 | ``` 499 | Thread Name= 0 default Formatter = yyyyMMdd HHmm 500 | Thread Name= 0 formatter = yy-M-d ah:mm 501 | Thread Name= 1 default Formatter = yyyyMMdd HHmm 502 | Thread Name= 2 default Formatter = yyyyMMdd HHmm 503 | Thread Name= 1 formatter = yy-M-d ah:mm 504 | Thread Name= 3 default Formatter = yyyyMMdd HHmm 505 | Thread Name= 2 formatter = yy-M-d ah:mm 506 | Thread Name= 4 default Formatter = yyyyMMdd HHmm 507 | Thread Name= 3 formatter = yy-M-d ah:mm 508 | Thread Name= 4 formatter = yy-M-d ah:mm 509 | Thread Name= 5 default Formatter = yyyyMMdd HHmm 510 | Thread Name= 5 formatter = yy-M-d ah:mm 511 | Thread Name= 6 default Formatter = yyyyMMdd HHmm 512 | Thread Name= 6 formatter = yy-M-d ah:mm 513 | Thread Name= 7 default Formatter = yyyyMMdd HHmm 514 | Thread Name= 7 formatter = yy-M-d ah:mm 515 | Thread Name= 8 default Formatter = yyyyMMdd HHmm 516 | Thread Name= 9 default Formatter = yyyyMMdd HHmm 517 | Thread Name= 8 formatter = yy-M-d ah:mm 518 | Thread Name= 9 formatter = yy-M-d ah:mm 519 | ``` 520 | 521 | 从输出中可以看出,Thread-0已经改变了formatter的值,但仍然是thread-2默认格式化程序与初始化值相同,其他线程也一样。 522 | 523 | 上面有一段代码用到了创建 `ThreadLocal` 变量的那段代码用到了 Java8 的知识,它等于下面这段代码,如果你写了下面这段代码的话,IDEA会提示你转换为Java8的格式(IDEA真的不错!)。因为ThreadLocal类在Java 8中扩展,使用一个新的方法`withInitial()`,将Supplier功能接口作为参数。 524 | 525 | ```java 526 | private static final ThreadLocal formatter = new ThreadLocal(){ 527 | @Override 528 | protected SimpleDateFormat initialValue() 529 | { 530 | return new SimpleDateFormat("yyyyMMdd HHmm"); 531 | } 532 | }; 533 | ``` 534 | 535 | #### 3. ThreadLocal原理 536 | 537 | 从 `Thread`类源代码入手。 538 | 539 | ```java 540 | public class Thread implements Runnable { 541 | ...... 542 | //与此线程有关的ThreadLocal值。由ThreadLocal类维护 543 | ThreadLocal.ThreadLocalMap threadLocals = null; 544 | 545 | //与此线程有关的InheritableThreadLocal值。由InheritableThreadLocal类维护 546 | ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; 547 | ...... 548 | } 549 | ``` 550 | 551 | 从上面`Thread`类 源代码可以看出`Thread` 类中有一个 `threadLocals` 和 一个 `inheritableThreadLocals` 变量,它们都是 `ThreadLocalMap` 类型的变量,我们可以把 `ThreadLocalMap` 理解为`ThreadLocal` 类实现的定制化的 `HashMap`。默认情况下这两个变量都是null,只有当前线程调用 `ThreadLocal` 类的 `set`或`get`方法时才创建它们,实际上调用这两个方法的时候,我们调用的是`ThreadLocalMap`类对应的 `get()`、`set() `方法。 552 | 553 | `ThreadLocal`类的`set()`方法 554 | 555 | ```java 556 | public void set(T value) { 557 | Thread t = Thread.currentThread(); 558 | ThreadLocalMap map = getMap(t); 559 | if (map != null) 560 | map.set(this, value); 561 | else 562 | createMap(t, value); 563 | } 564 | ThreadLocalMap getMap(Thread t) { 565 | return t.threadLocals; 566 | } 567 | ``` 568 | 569 | 通过上面这些内容,我们足以通过猜测得出结论:**最终的变量是放在了当前线程的 `ThreadLocalMap` 中,并不是存在 `ThreadLocal` 上,`ThreadLocal` 可以理解为只是`ThreadLocalMap`的封装,传递了变量值。** `ThrealLocal` 类中可以通过`Thread.currentThread()`获取到当前线程对象后,直接通过`getMap(Thread t)`可以访问到该线程的`ThreadLocalMap`对象。 570 | 571 | **`ThreadLocal` 内部维护的是一个类似 `Map` 的`ThreadLocalMap` 数据结构,`key` 为当前对象的 `Thread` 对象,值为 Object 对象。** 572 | 573 | ```java 574 | ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { 575 | ...... 576 | } 577 | ``` 578 | 579 | 比如我们在同一个线程中声明了两个 `ThreadLocal` 对象的话,会使用 `Thread`内部都是使用仅有那个`ThreadLocalMap` 存放数据的,`ThreadLocalMap`的 key 就是 `ThreadLocal`对象,value 就是 `ThreadLocal` 对象调用`set`方法设置的值。 580 | 581 | ![ThreadLocal数据结构](https://upload-images.jianshu.io/upload_images/7432604-ad2ff581127ba8cc.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/806) 582 | 583 | `ThreadLocalMap`是`ThreadLocal`的静态内部类。 584 | 585 | ![ThreadLocal内部类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/ThreadLocal内部类.png) 586 | 587 | #### 4. ThreadLocal 内存泄露问题 588 | 589 | `ThreadLocalMap` 中使用的 key 为 `ThreadLocal` 的弱引用,而 value 是强引用。所以,如果 `ThreadLocal` 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。这样一来,`ThreadLocalMap` 中就会出现key为null的Entry。假如我们不做任何措施的话,value 永远无法被GC 回收,这个时候就可能会产生内存泄露。ThreadLocalMap实现中已经考虑了这种情况,在调用 `set()`、`get()`、`remove()` 方法的时候,会清理掉 key 为 null 的记录。使用完 `ThreadLocal`方法后 最好手动调用`remove()`方法 590 | 591 | ```java 592 | static class Entry extends WeakReference> { 593 | /** The value associated with this ThreadLocal. */ 594 | Object value; 595 | 596 | Entry(ThreadLocal k, Object v) { 597 | super(k); 598 | value = v; 599 | } 600 | } 601 | ``` 602 | 603 | **弱引用介绍:** 604 | 605 | > 如果一个对象只具有弱引用,那就类似于**可有可无的生活用品**。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。 606 | > 607 | > 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。 608 | 609 | ### 2.3.14 线程池 610 | 611 | #### 1. 为什么要用线程池? 612 | 613 | > **池化技术相比大家已经屡见不鲜了,线程池、数据库连接池、Http 连接池等等都是对这个思想的应用。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。** 614 | 615 | **线程池**提供了一种限制和管理资源(包括执行一个任务)。 每个**线程池**还维护一些基本统计信息,例如已完成任务的数量。 616 | 617 | 这里借用《Java 并发编程的艺术》提到的来说一下**使用线程池的好处**: 618 | 619 | - **降低资源消耗**。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 620 | - **提高响应速度**。当任务到达时,任务可以不需要的等到线程创建就能立即执行。 621 | - **提高线程的可管理性**。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。 622 | 623 | #### 2. 实现Runnable接口和Callable接口的区别 624 | 625 | `Runnable`自Java 1.0以来一直存在,但`Callable`仅在Java 1.5中引入,目的就是为了来处理`Runnable`不支持的用例。**`Runnable` 接口**不会返回结果或抛出检查异常,但是**`Callable` 接口**可以。所以,如果任务不需要返回结果或抛出异常推荐使用 **`Runnable` 接口**,这样代码看起来会更加简洁。 626 | 627 | 工具类 `Executors` 可以实现 `Runnable` 对象和 `Callable` 对象之间的相互转换。(`Executors.callable(Runnable task`)或 `Executors.callable(Runnable task,Object resule)`)。 628 | 629 | `Runnable.java` 630 | 631 | ```java 632 | @FunctionalInterface 633 | public interface Runnable { 634 | /** 635 | * 被线程执行,没有返回值也无法抛出异常 636 | */ 637 | public abstract void run(); 638 | } 639 | ``` 640 | 641 | `Callable.java` 642 | 643 | ```java 644 | @FunctionalInterface 645 | public interface Callable { 646 | /** 647 | * 计算结果,或在无法这样做时抛出异常。 648 | * @return 计算得出的结果 649 | * @throws 如果无法计算结果,则抛出异常 650 | */ 651 | V call() throws Exception; 652 | } 653 | ``` 654 | 655 | #### 3. 执行execute()方法和submit()方法的区别是什么呢? 656 | 657 | 1. **`execute()`方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;** 658 | 2. **`submit()`方法用于提交需要返回值的任务。线程池会返回一个 `Future` 类型的对象,通过这个 `Future` 对象可以判断任务是否执行成功**,并且可以通过 `Future` 的 `get()`方法来获取返回值,`get()`方法会阻塞当前线程直到任务完成,而使用 `get(long timeout,TimeUnit unit)`方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。 659 | 660 | 我们以**`AbstractExecutorService`**接口中的一个 `submit` 方法为例子来看看源代码: 661 | 662 | ```java 663 | public Future submit(Runnable task) { 664 | if (task == null) throw new NullPointerException(); 665 | RunnableFuture ftask = newTaskFor(task, null); 666 | execute(ftask); 667 | return ftask; 668 | } 669 | ``` 670 | 671 | 上面方法调用的 `newTaskFor` 方法返回了一个 `FutureTask` 对象。 672 | 673 | ```java 674 | protected RunnableFuture newTaskFor(Runnable runnable, T value) { 675 | return new FutureTask(runnable, value); 676 | } 677 | ``` 678 | 679 | 我们再来看看`execute()`方法: 680 | 681 | ```java 682 | public void execute(Runnable command) { 683 | ... 684 | } 685 | ``` 686 | 687 | #### 4. 如何创建线程池 688 | 689 | 《阿里巴巴Java开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险 690 | 691 | > Executors 返回线程池对象的弊端如下: 692 | > 693 | > - **FixedThreadPool 和 SingleThreadExecutor** : 允许请求的队列长度为 Integer.MAX_VALUE ,可能堆积大量的请求,从而导致OOM。 694 | > - **CachedThreadPool 和 ScheduledThreadPool** : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。 695 | 696 | **方式一:通过构造方法实现** 697 | ![ThreadPoolExecutor构造方法](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/ThreadPoolExecutor构造方法.png) 698 | **方式二:通过Executor 框架的工具类Executors来实现** 699 | 我们可以创建三种类型的ThreadPoolExecutor: 700 | 701 | - **FixedThreadPool** : 该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。 702 | - **SingleThreadExecutor:** 方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。 703 | - **CachedThreadPool:** 该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。 704 | 705 | 对应Executors工具类中的方法如图所示: 706 | ![Executor框架的工具类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/Executor框架的工具类.png) 707 | 708 | #### 5. ThreadPoolExecutor 类分析 709 | 710 | `ThreadPoolExecutor` 类中提供的四个构造方法。我们来看最长的那个,其余三个都是在这个构造方法的基础上产生(其他几个构造方法说白点都是给定某些默认参数的构造方法比如默认制定拒绝策略是什么),这里就不贴代码讲了,比较简单。 711 | 712 | ```java 713 | /** 714 | * 用给定的初始参数创建一个新的ThreadPoolExecutor。 715 | */ 716 | public ThreadPoolExecutor(int corePoolSize, 717 | int maximumPoolSize, 718 | long keepAliveTime, 719 | TimeUnit unit, 720 | BlockingQueue workQueue, 721 | ThreadFactory threadFactory, 722 | RejectedExecutionHandler handler) { 723 | if (corePoolSize < 0 || 724 | maximumPoolSize <= 0 || 725 | maximumPoolSize < corePoolSize || 726 | keepAliveTime < 0) 727 | throw new IllegalArgumentException(); 728 | if (workQueue == null || threadFactory == null || handler == null) 729 | throw new NullPointerException(); 730 | this.corePoolSize = corePoolSize; 731 | this.maximumPoolSize = maximumPoolSize; 732 | this.workQueue = workQueue; 733 | this.keepAliveTime = unit.toNanos(keepAliveTime); 734 | this.threadFactory = threadFactory; 735 | this.handler = handler; 736 | } 737 | ``` 738 | 739 | **下面这些对创建 非常重要,在后面使用线程池的过程中你一定会用到!所以,务必拿着小本本记清楚。** 740 | 741 | ##### `ThreadPoolExecutor`构造函数重要参数分析 742 | 743 | **`ThreadPoolExecutor` 3 个最重要的参数:** 744 | 745 | - **`corePoolSize` :** 核心线程数线程数定义了最小可以同时运行的线程数量。 746 | - **`maximumPoolSize` :** 当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。 747 | - **`workQueue`:** 当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。 748 | 749 | `ThreadPoolExecutor`其他常见参数: 750 | 751 | 1. **`keepAliveTime`**:当线程池中的线程数量大于 `corePoolSize` 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 `keepAliveTime`才会被回收销毁; 752 | 2. **`unit`** : `keepAliveTime` 参数的时间单位。 753 | 3. **`threadFactory`** :executor 创建新线程的时候会用到。 754 | 4. **`handler`** :饱和策略。关于饱和策略下面单独介绍一下。 755 | 756 | ##### `ThreadPoolExecutor` 饱和策略 757 | 758 | **`ThreadPoolExecutor` 饱和策略定义:** 759 | 760 | 如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任时,`ThreadPoolTaskExecutor` 定义一些策略: 761 | 762 | - **`ThreadPoolExecutor.AbortPolicy`**:抛出 `RejectedExecutionException`来拒绝新任务的处理。 763 | - **`ThreadPoolExecutor.CallerRunsPolicy`**:调用执行自己的线程运行任务。您不会任务请求。但是这种策略会降低对于新任务提交速度,影响程序的整体性能。另外,这个策略喜欢增加队列容量。如果您的应用程序可以承受此延迟并且你不能任务丢弃任何一个任务请求的话,你可以选择这个策略。 764 | - **`ThreadPoolExecutor.DiscardPolicy`:** 不处理新任务,直接丢弃掉。 765 | - **`ThreadPoolExecutor.DiscardOldestPolicy`:** 此策略将丢弃最早的未处理的任务请求。 766 | 767 | 举个例子: Spring 通过 `ThreadPoolTaskExecutor` 或者我们直接通过 `ThreadPoolExecutor` 的构造函数创建线程池的时候,当我们不指定 `RejectedExecutionHandler` 饱和策略的话来配置线程池的时候默认使用的是 `ThreadPoolExecutor.AbortPolicy`。在默认情况下,`ThreadPoolExecutor` 将抛出 `RejectedExecutionException` 来拒绝新来的任务 ,这代表你将丢失对这个任务的处理。 对于可伸缩的应用程序,建议使用 `ThreadPoolExecutor.CallerRunsPolicy`。当最大池被填满时,此策略为我们提供可伸缩队列。(这个直接查看 `ThreadPoolExecutor` 的构造函数源码就可以看出,比较简单的原因,这里就不贴代码了) 768 | 769 | #### 6. 一个简单的线程池Demo:`Runnable`+`ThreadPoolExecutor` 770 | 771 | 为了让大家更清楚上面的面试题中的一些概念,我写了一个简单的线程池 Demo。 772 | 773 | 首先创建一个 `Runnable` 接口的实现类(当然也可以是 `Callable` 接口,我们上面也说了两者的区别。) 774 | 775 | `MyRunnable.java` 776 | 777 | ```java 778 | import java.util.Date; 779 | 780 | /** 781 | * 这是一个简单的Runnable类,需要大约5秒钟来执行其任务。 782 | * @author shuang.kou 783 | */ 784 | public class MyRunnable implements Runnable { 785 | 786 | private String command; 787 | 788 | public MyRunnable(String s) { 789 | this.command = s; 790 | } 791 | 792 | @Override 793 | public void run() { 794 | System.out.println(Thread.currentThread().getName() + " Start. Time = " + new Date()); 795 | processCommand(); 796 | System.out.println(Thread.currentThread().getName() + " End. Time = " + new Date()); 797 | } 798 | 799 | private void processCommand() { 800 | try { 801 | Thread.sleep(5000); 802 | } catch (InterruptedException e) { 803 | e.printStackTrace(); 804 | } 805 | } 806 | 807 | @Override 808 | public String toString() { 809 | return this.command; 810 | } 811 | } 812 | 813 | ``` 814 | 815 | 编写测试程序,我们这里以阿里巴巴推荐的使用 `ThreadPoolExecutor` 构造函数自定义参数的方式来创建线程池。 816 | 817 | `ThreadPoolExecutorDemo.java` 818 | 819 | ```java 820 | import java.util.concurrent.ArrayBlockingQueue; 821 | import java.util.concurrent.ThreadPoolExecutor; 822 | import java.util.concurrent.TimeUnit; 823 | 824 | public class ThreadPoolExecutorDemo { 825 | 826 | private static final int CORE_POOL_SIZE = 5; 827 | private static final int MAX_POOL_SIZE = 10; 828 | private static final int QUEUE_CAPACITY = 100; 829 | private static final Long KEEP_ALIVE_TIME = 1L; 830 | public static void main(String[] args) { 831 | 832 | //使用阿里巴巴推荐的创建线程池的方式 833 | //通过ThreadPoolExecutor构造函数自定义参数创建 834 | ThreadPoolExecutor executor = new ThreadPoolExecutor( 835 | CORE_POOL_SIZE, 836 | MAX_POOL_SIZE, 837 | KEEP_ALIVE_TIME, 838 | TimeUnit.SECONDS, 839 | new ArrayBlockingQueue<>(QUEUE_CAPACITY), 840 | new ThreadPoolExecutor.CallerRunsPolicy()); 841 | 842 | for (int i = 0; i < 10; i++) { 843 | //创建WorkerThread对象(WorkerThread类实现了Runnable 接口) 844 | Runnable worker = new MyRunnable("" + i); 845 | //执行Runnable 846 | executor.execute(worker); 847 | } 848 | //终止线程池 849 | executor.shutdown(); 850 | while (!executor.isTerminated()) { 851 | } 852 | System.out.println("Finished all threads"); 853 | } 854 | } 855 | 856 | ``` 857 | 858 | 可以看到我们上面的代码指定了: 859 | 860 | 1. `corePoolSize`: 核心线程数为 5。 861 | 2. `maximumPoolSize` :最大线程数 10 862 | 3. `keepAliveTime` : 等待时间为 1L。 863 | 4. `unit`: 等待时间的单位为 TimeUnit.SECONDS。 864 | 5. `workQueue`:任务队列为 `ArrayBlockingQueue`,并且容量为 100; 865 | 6. `handler`:饱和策略为 `CallerRunsPolicy`。 866 | 867 | **Output:** 868 | 869 | ``` 870 | pool-1-thread-2 Start. Time = Tue Nov 12 20:59:44 CST 2019 871 | pool-1-thread-5 Start. Time = Tue Nov 12 20:59:44 CST 2019 872 | pool-1-thread-4 Start. Time = Tue Nov 12 20:59:44 CST 2019 873 | pool-1-thread-1 Start. Time = Tue Nov 12 20:59:44 CST 2019 874 | pool-1-thread-3 Start. Time = Tue Nov 12 20:59:44 CST 2019 875 | pool-1-thread-5 End. Time = Tue Nov 12 20:59:49 CST 2019 876 | pool-1-thread-3 End. Time = Tue Nov 12 20:59:49 CST 2019 877 | pool-1-thread-2 End. Time = Tue Nov 12 20:59:49 CST 2019 878 | pool-1-thread-4 End. Time = Tue Nov 12 20:59:49 CST 2019 879 | pool-1-thread-1 End. Time = Tue Nov 12 20:59:49 CST 2019 880 | pool-1-thread-2 Start. Time = Tue Nov 12 20:59:49 CST 2019 881 | pool-1-thread-1 Start. Time = Tue Nov 12 20:59:49 CST 2019 882 | pool-1-thread-4 Start. Time = Tue Nov 12 20:59:49 CST 2019 883 | pool-1-thread-3 Start. Time = Tue Nov 12 20:59:49 CST 2019 884 | pool-1-thread-5 Start. Time = Tue Nov 12 20:59:49 CST 2019 885 | pool-1-thread-2 End. Time = Tue Nov 12 20:59:54 CST 2019 886 | pool-1-thread-3 End. Time = Tue Nov 12 20:59:54 CST 2019 887 | pool-1-thread-4 End. Time = Tue Nov 12 20:59:54 CST 2019 888 | pool-1-thread-5 End. Time = Tue Nov 12 20:59:54 CST 2019 889 | pool-1-thread-1 End. Time = Tue Nov 12 20:59:54 CST 2019 890 | 891 | ``` 892 | 893 | #### 7. 线程池原理分析 894 | 895 | 承接 4.6 节,我们通过代码输出结果可以看出:**线程池每次会同时执行 5 个任务,这 5 个任务执行完之后,剩余的 5 个任务才会被执行。** 大家可以先通过上面讲解的内容,分析一下到底是咋回事?(自己独立思考一会) 896 | 897 | 现在,我们就分析上面的输出内容来简单分析一下线程池原理。 898 | 899 | **为了搞懂线程池的原理,我们需要首先分析一下 `execute`方法。**在 4.6 节中的 Demo 中我们使用 `executor.execute(worker)`来提交一个任务到线程池中去,这个方法非常重要,下面我们来看看它的源码: 900 | 901 | ```java 902 | // 存放线程池的运行状态 (runState) 和线程池内有效线程的数量 (workerCount) 903 | private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 904 | 905 | private static int workerCountOf(int c) { 906 | return c & CAPACITY; 907 | } 908 | 909 | private final BlockingQueue workQueue; 910 | 911 | public void execute(Runnable command) { 912 | // 如果任务为null,则抛出异常。 913 | if (command == null) 914 | throw new NullPointerException(); 915 | // ctl 中保存的线程池当前的一些状态信息 916 | int c = ctl.get(); 917 | 918 | // 下面会涉及到 3 步 操作 919 | // 1.首先判断当前线程池中之行的任务数量是否小于 corePoolSize 920 | // 如果小于的话,通过addWorker(command, true)新建一个线程,并将任务(command)添加到该线程中;然后,启动该线程从而执行任务。 921 | if (workerCountOf(c) < corePoolSize) { 922 | if (addWorker(command, true)) 923 | return; 924 | c = ctl.get(); 925 | } 926 | // 2.如果当前之行的任务数量大于等于 corePoolSize 的时候就会走到这里 927 | // 通过 isRunning 方法判断线程池状态,线程池处于 RUNNING 状态才会被并且队列可以加入任务,该任务才会被加入进去 928 | if (isRunning(c) && workQueue.offer(command)) { 929 | int recheck = ctl.get(); 930 | // 再次获取线程池状态,如果线程池状态不是 RUNNING 状态就需要从任务队列中移除任务,并尝试判断线程是否全部执行完毕。同时执行拒绝策略。 931 | if (!isRunning(recheck) && remove(command)) 932 | reject(command); 933 | // 如果当前线程池为空就新创建一个线程并执行。 934 | else if (workerCountOf(recheck) == 0) 935 | addWorker(null, false); 936 | } 937 | //3. 通过addWorker(command, false)新建一个线程,并将任务(command)添加到该线程中;然后,启动该线程从而执行任务。 938 | //如果addWorker(command, false)执行失败,则通过reject()执行相应的拒绝策略的内容。 939 | else if (!addWorker(command, false)) 940 | reject(command); 941 | } 942 | ``` 943 | 944 | 通过下图可以更好的对上面这 3 步做一个展示,下图是我为了省事直接从网上找到,原地址不明。 945 | 946 | ![图解线程池实现原理](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-7/图解线程池实现原理.png) 947 | 948 | 现在,让我们在回到 4.6 节我们写的 Demo, 现在应该是不是很容易就可以搞懂它的原理了呢? 949 | 950 | 没搞懂的话,也没关系,可以看看我的分析: 951 | 952 | > 我们在代码中模拟了 10 个任务,我们配置的核心线程数为 5 、等待队列容量为 100 ,所以每次只可能存在 5 个任务同时执行,剩下的 5 个任务会被放到等待队列中去。当前的 5 个任务之行完成后,才会之行剩下的 5 个任务。 953 | 954 | ### 2.3.15 Atomic 原子类 955 | 956 | #### 1. 介绍一下Atomic 原子类 957 | 958 | Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的。在我们这里 Atomic 是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。 959 | 960 | 所以,所谓原子类说简单点就是具有原子/原子操作特征的类。 961 | 962 | 963 | 并发包 `java.util.concurrent` 的原子类都存放在`java.util.concurrent.atomic`下,如下图所示。 964 | 965 | ![JUC原子类概览](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/JUC原子类概览.png) 966 | 967 | #### 2. JUC 包中的原子类是哪4类? 968 | 969 | **基本类型** 970 | 971 | 使用原子的方式更新基本类型 972 | 973 | - AtomicInteger:整形原子类 974 | - AtomicLong:长整型原子类 975 | - AtomicBoolean:布尔型原子类 976 | 977 | **数组类型** 978 | 979 | 使用原子的方式更新数组里的某个元素 980 | 981 | 982 | - AtomicIntegerArray:整形数组原子类 983 | - AtomicLongArray:长整形数组原子类 984 | - AtomicReferenceArray:引用类型数组原子类 985 | 986 | **引用类型** 987 | 988 | - AtomicReference:引用类型原子类 989 | - AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。 990 | - AtomicMarkableReference :原子更新带有标记位的引用类型 991 | 992 | **对象的属性修改类型** 993 | 994 | - AtomicIntegerFieldUpdater:原子更新整形字段的更新器 995 | - AtomicLongFieldUpdater:原子更新长整形字段的更新器 996 | - 997 | 998 | 999 | #### 3. 讲讲 AtomicInteger 的使用 1000 | 1001 | **AtomicInteger 类常用方法** 1002 | 1003 | ```java 1004 | public final int get() //获取当前的值 1005 | public final int getAndSet(int newValue)//获取当前的值,并设置新的值 1006 | public final int getAndIncrement()//获取当前的值,并自增 1007 | public final int getAndDecrement() //获取当前的值,并自减 1008 | public final int getAndAdd(int delta) //获取当前的值,并加上预期的值 1009 | boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update) 1010 | public final void lazySet(int newValue)//最终设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。 1011 | ``` 1012 | 1013 | **AtomicInteger 类的使用示例** 1014 | 1015 | 使用 AtomicInteger 之后,不用对 increment() 方法加锁也可以保证线程安全。 1016 | ```java 1017 | class AtomicIntegerTest { 1018 | private AtomicInteger count = new AtomicInteger(); 1019 | //使用AtomicInteger之后,不需要对该方法加锁,也可以实现线程安全。 1020 | public void increment() { 1021 | count.incrementAndGet(); 1022 | } 1023 | 1024 | public int getCount() { 1025 | return count.get(); 1026 | } 1027 | } 1028 | 1029 | ``` 1030 | 1031 | ##### 4. 能不能给我简单介绍一下 AtomicInteger 类的原理 1032 | 1033 | AtomicInteger 线程安全原理简单分析 1034 | 1035 | AtomicInteger 类的部分源码: 1036 | 1037 | ```java 1038 | // setup to use Unsafe.compareAndSwapInt for updates(更新操作时提供“比较并替换”的作用) 1039 | private static final Unsafe unsafe = Unsafe.getUnsafe(); 1040 | private static final long valueOffset; 1041 | 1042 | static { 1043 | try { 1044 | valueOffset = unsafe.objectFieldOffset 1045 | (AtomicInteger.class.getDeclaredField("value")); 1046 | } catch (Exception ex) { throw new Error(ex); } 1047 | } 1048 | 1049 | private volatile int value; 1050 | ``` 1051 | 1052 | AtomicInteger 类主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。 1053 | 1054 | CAS的原理是拿期望的值和原本的一个值作比较,如果相同则更新成新的值。UnSafe 类的 objectFieldOffset() 方法是一个本地方法,这个方法是用来拿到“原来的值”的内存地址,返回值是 valueOffset。另外 value 是一个volatile变量,在内存中可见,因此 JVM 可以保证任何时刻任何线程总能拿到该变量的最新值。 1055 | 1056 | 关于 Atomic 原子类这部分更多内容可以查看我的这篇文章:并发编程面试必备:[JUC 中的 Atomic 原子类总结](https://mp.weixin.qq.com/s/joa-yOiTrYF67bElj8xqvg) 1057 | 1058 | ### 2.3.16 AQS 1059 | 1060 | #### 1. AQS 介绍 1061 | 1062 | AQS的全称为(AbstractQueuedSynchronizer),这个类在java.util.concurrent.locks包下面。 1063 | 1064 | ![AQS类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/AQS类.png) 1065 | 1066 | AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Semaphore,其他的诸如ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。当然,我们自己也能利用AQS非常轻松容易地构造出符合我们自己需求的同步器。 1067 | 1068 | #### 2. AQS 原理分析 1069 | 1070 | AQS 原理这部分参考了部分博客,在5.2节末尾放了链接。 1071 | 1072 | > 在面试中被问到并发知识的时候,大多都会被问到“请你说一下自己对于AQS原理的理解”。下面给大家一个示例供大家参加,面试不是背题,大家一定要加入自己的思想,即使加入不了自己的思想也要保证自己能够通俗的讲出来而不是背出来。 1073 | 1074 | 下面大部分内容其实在AQS类注释上已经给出了,不过是英语看着比较吃力一点,感兴趣的话可以看看源码。 1075 | 1076 | ##### AQS 原理概览 1077 | 1078 | **AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。** 1079 | 1080 | > CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。 1081 | 1082 | 看个AQS(AbstractQueuedSynchronizer)原理图: 1083 | 1084 | 1085 | ![AQS原理图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/AQS原理图.png) 1086 | 1087 | AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。 1088 | 1089 | ```java 1090 | private volatile int state;//共享变量,使用volatile修饰保证线程可见性 1091 | ``` 1092 | 1093 | 状态信息通过protected类型的getState,setState,compareAndSetState进行操作 1094 | 1095 | ```java 1096 | 1097 | //返回同步状态的当前值 1098 | protected final int getState() { 1099 | return state; 1100 | } 1101 | // 设置同步状态的值 1102 | protected final void setState(int newState) { 1103 | state = newState; 1104 | } 1105 | //原子地(CAS操作)将同步状态值设置为给定值update如果当前同步状态的值等于expect(期望值) 1106 | protected final boolean compareAndSetState(int expect, int update) { 1107 | return unsafe.compareAndSwapInt(this, stateOffset, expect, update); 1108 | } 1109 | ``` 1110 | 1111 | ##### AQS 对资源的共享方式 1112 | 1113 | **AQS定义两种资源共享方式** 1114 | 1115 | - **Exclusive**(独占):只有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁: 1116 | - 公平锁:按照线程在队列中的排队顺序,先到者先拿到锁 1117 | - 非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的 1118 | - **Share**(共享):多个线程可同时执行,如Semaphore/CountDownLatch。Semaphore、CountDownLatch、 CyclicBarrier、ReadWriteLock 我们都会在后面讲到。 1119 | 1120 | ReentrantReadWriteLock 可以看成是组合式,因为ReentrantReadWriteLock也就是读写锁允许多个线程同时对某一资源进行读。 1121 | 1122 | 不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源 state 的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。 1123 | 1124 | ##### AQS底层使用了模板方法模式 1125 | 1126 | 同步器的设计是基于模板方法模式的,如果需要自定义同步器一般的方式是这样(模板方法模式很经典的一个应用): 1127 | 1128 | 1. 使用者继承AbstractQueuedSynchronizer并重写指定的方法。(这些重写方法很简单,无非是对于共享资源state的获取和释放) 1129 | 2. 将AQS组合在自定义同步组件的实现中,并调用其模板方法,而这些模板方法会调用使用者重写的方法。 1130 | 1131 | 这和我们以往通过实现接口的方式有很大区别,这是模板方法模式很经典的一个运用。 1132 | 1133 | **AQS使用了模板方法模式,自定义同步器时需要重写下面几个AQS提供的模板方法:** 1134 | 1135 | ```java 1136 | isHeldExclusively()//该线程是否正在独占资源。只有用到condition才需要去实现它。 1137 | tryAcquire(int)//独占方式。尝试获取资源,成功则返回true,失败则返回false。 1138 | tryRelease(int)//独占方式。尝试释放资源,成功则返回true,失败则返回false。 1139 | tryAcquireShared(int)//共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。 1140 | tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true,失败则返回false。 1141 | 1142 | ``` 1143 | 1144 | 默认情况下,每个方法都抛出 `UnsupportedOperationException`。 这些方法的实现必须是内部线程安全的,并且通常应该简短而不是阻塞。AQS类中的其他方法都是final ,所以无法被其他类使用,只有这几个方法可以被其他类使用。 1145 | 1146 | 以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。 1147 | 1148 | 再以CountDownLatch以例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS(Compare and Swap)减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。 1149 | 1150 | 一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现`tryAcquire-tryRelease`、`tryAcquireShared-tryReleaseShared`中的一种即可。但AQS也支持自定义同步器同时实现独占和共享两种方式,如`ReentrantReadWriteLock`。 1151 | 1152 | 推荐两篇 AQS 原理和相关源码分析的文章: 1153 | 1154 | - http://www.cnblogs.com/waterystone/p/4920797.html 1155 | - https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html 1156 | 1157 | #### 3. AQS 组件总结 1158 | 1159 | - **Semaphore(信号量)-允许多个线程同时访问:** synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源,Semaphore(信号量)可以指定多个线程同时访问某个资源。 1160 | - **CountDownLatch (倒计时器):** CountDownLatch是一个同步工具类,用来协调多个线程之间的同步。这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计时结束,再开始执行。 1161 | - **CyclicBarrier(循环栅栏):** CyclicBarrier 和 CountDownLatch 非常类似,它也可以实现线程间的技术等待,但是它的功能比 CountDownLatch 更加复杂和强大。主要应用场景和 CountDownLatch 类似。CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是 CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await()方法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程被阻塞。 1162 | 1163 | ### Reference 1164 | 1165 | - 《深入理解 Java 虚拟机》 1166 | - 《实战 Java 高并发程序设计》 1167 | - 《Java并发编程的艺术》 1168 | - http://www.cnblogs.com/waterystone/p/4920797.html 1169 | - https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html 1170 | - 1171 | 1172 | -------------------------------------------------------------------------------- /src/01-java-interview/04-java-base-written/README.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,欢迎关注微信公众号:Coder编程。内容将持续更新! # 2 | # :mega: 本章主要介绍JAVA基础笔试题目! # 3 | 4 | ## :mega: 笔试题 ## 5 | 6 | > JAVA相关笔试题,祝各位找到好工作! 7 | 8 | * [Java基础笔试练习(一)](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483931&idx=1&sn=df30a4a906a04cb27c8acf9dc116a3c4&chksm=96e67308a191fa1ee191114dcc484dcc6cafa44d1fa75416cf8751c02eb2fe0b07349da8a09a&token=1232740665&lang=zh_CN#rd) 9 | 10 | * [Java基础笔试练习(二)](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483938&idx=1&sn=661d5eb1f4930f88d5d537ba5ba75d19&chksm=96e67331a191fa271eee96909aa5091011a08d98923c9787c7b9ecd6b8b95c3ff7f9043757d5&token=1232740665&lang=zh_CN#rd) 11 | 12 | * [Java基础笔试练习(三)](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483938&idx=2&sn=1bf423a3fcb8bd29bf9dbcd4f3d44c10&chksm=96e67331a191fa27862add855c2136b0693541d3f23eb44aff1457d9ea77418cf679e9161f74&token=1232740665&lang=zh_CN#rd) 13 | 14 | * [Java基础笔试练习(四)](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483943&idx=1&sn=48a002f734c7a124e003ff21051a9419&chksm=96e67334a191fa229ab158fdd8955fed4972bf19ef6e31b400bde80475331a6ce57347c7b843&token=1232740665&lang=zh_CN#rd) 15 | 16 | * [Java基础笔试练习(五)](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483943&idx=2&sn=e944acb61378185d1a21b0e4d25ee740&chksm=96e67334a191fa2257c54ed22bd57c8f8b0c08e47a63bcd81a3c5d14e9a27df26e697d9fc02a&token=1232740665&lang=zh_CN#rd) 17 | 18 | * [Java基础笔试练习(六) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483949&idx=1&sn=af7e3c91c43bae3dcdcf93dac12bc42d&chksm=96e6733ea191fa28a5fd29edcfce4428bb71c6bcce3fe0bf01f04ead0973c279bbd8e0f51cc1&token=11501131&lang=zh_CN#rd) 19 | 20 | * [Java基础笔试练习(七) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483949&idx=2&sn=245d9cc37a8a9facb8a84bb2d2e5f37f&chksm=96e6733ea191fa28abdbde92b01752e41c66df50216296b0a16672e5b757d5dcb4419e434ac9&token=11501131&lang=zh_CN#rd) 21 | 22 | * [Java基础笔试练习(八) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483953&idx=1&sn=427314f70bd0e6bd740388dff1a4f46c&chksm=96e67322a191fa342b13dc4e43abae4be5b0bb251c8fb229101717918c9d0636b53c9e09b831&token=11501131&lang=zh_CN#rd) 23 | 24 | 25 | * [Java基础笔试练习(九) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483957&idx=1&sn=46b2d55150d34b440b98f91c5c7ed978&chksm=96e67326a191fa302b44b5e611dc05749b34351e09c7025c0365a61638dcf2e75b8988813a85&token=11501131&lang=zh_CN#rd) 26 | 27 | * [Java基础笔试练习(十) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483961&idx=1&sn=8ddc986e932d5e08b7dee05cc77f9e2e&chksm=96e6732aa191fa3cf4a23b1f377fdc60df51c9b763bd6e598b34b33d1a9aa57d5602a0311535&token=11501131&lang=zh_CN#rd) 28 | 29 | 30 | * [Java基础笔试练习(十一) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483965&idx=1&sn=3f86e27eb94da212f976405ea81f449d&chksm=96e6732ea191fa3802df315aff371ff5ce44a4f82022f741722fd1c5a7724c830fa2101c55ce&token=11501131&lang=zh_CN#rd) 31 | 32 | * [Java基础笔试练习(十二) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483969&idx=1&sn=8e36cf13c0707d5a5a2a7eab0599fdf0&chksm=96e67352a191fa44a1d0f5b235dbe7439c6d9353e4bbb429a5b40d2b7b9bfdd49ce92338417d&token=11501131&lang=zh_CN#rd) 33 | 34 | * [Java基础笔试练习(十三) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483973&idx=1&sn=2bc139f0feb4a5798675452d46cdf231&chksm=96e67356a191fa401dab2daa8f6a86a17e5b7b9f0f1767144514f45da39e4cac1e3043faa307&token=11501131&lang=zh_CN#rd) 35 | 36 | 37 | * [Java基础笔试练习(十四) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483980&idx=2&sn=bd57b931f0916124d130cc7d8176d575&chksm=96e6735fa191fa4983e6afe570e75b33c5abaa7f8c584e14aba5039af91e42d78e7c513311b6&token=11501131&lang=zh_CN#rd) 38 | 39 | * [Java基础笔试练习(十四) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483980&idx=2&sn=bd57b931f0916124d130cc7d8176d575&chksm=96e6735fa191fa4983e6afe570e75b33c5abaa7f8c584e14aba5039af91e42d78e7c513311b6&token=11501131&lang=zh_CN#rd) 40 | 41 | * [Java基础笔试练习(十五) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483985&idx=2&sn=abe3b233ebb93a17c0b522bf8b19ddd2&chksm=96e67342a191fa5460cac0b3b25dfe9a3da2cd448c49ef502c31071af078902b44ddd8f6ef86&token=11501131&lang=zh_CN#rd) 42 | 43 | * [Java基础笔试练习(十六) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483990&idx=1&sn=e6e011dbff50b5e94d26c11d734e9f0a&chksm=96e67345a191fa53e997486bfc69f6dbb9cfa3980e94512946c0cd3eefb6ae342534f8ddcfe1&token=11501131&lang=zh_CN#rd) 44 | 45 | * [Java基础笔试练习(十七) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484005&idx=2&sn=a4dcb5b2047109967098dbeef6ee65f6&chksm=96e67376a191fa60b54cda1f369babcf86679e3bc5a04a4376d67cc52ac8d41bb323f0c8c2df&token=1208401862&lang=zh_CN#rd) 46 | 47 | * [Java基础笔试练习(十八) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484011&idx=2&sn=ea90f411c13dda58394e3980f24d093f&chksm=96e67378a191fa6ec5af3fa304b579c7e6e2b5789e9ed94f2040def0b84907a162dd5896f596&token=1208401862&lang=zh_CN#rd) 48 | 49 | * [Java基础笔试练习(十九) ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484040&idx=2&sn=1570b536fab0558e3136fa4c6766d9e7&chksm=96e6739ba191fa8da5cc46add6934cf308f24c5801fa375e076c9232cce86aef619a4f1e9523&token=793972075&lang=zh_CN#rd) 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/01-java-interview/README.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,欢迎关注微信公众号:Coder编程。内容将持续更新! # 2 | # :mega: 本章主要介绍JAVA面试相关的知识题目! # 3 | 4 | ## :mega: 面试题 ## 5 | 6 | > JAVA相关面试题,祝各位找到好工作! 7 | 8 | > notebook: JAVA基础面试题 9 | - [通过面试题,让我们来了解Collection](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483669&idx=1&sn=b726d81212af6a857d59cd488c0e0aa2&chksm=96e67006a191f9103b3c790e04d5ed4ddcbc76f3efbb52e6ce8349619f56a68e72aca156571e&token=948950272&lang=zh_CN#rd) 10 | 11 | - [一篇让你明白进程与线程之间的区别与联系](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483676&idx=1&sn=9033af3cb37754446779e1fcd89e3329&chksm=96e6700fa191f91919b4c2a46a8a99a7a7dda037181e97b5377835500e99a5f66cb1d3337898&token=948950272&lang=zh_CN#rd) 12 | 13 | - [聊一聊Iterable与Iterator的那些事!](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483690&idx=1&sn=2cebca2e43e329d428263b0b8a801047&chksm=96e67039a191f92fd5fe353f70d7ef4767eb52af752a9a53bc57db61f389a1f0a1b5f218a2f0&token=948950272&lang=zh_CN#rd) 14 | 15 | - [通过“表白”,让我们快速了解网络七层协议](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483701&idx=1&sn=b21d65f8ba4ae7f861a6e6175be2303c&chksm=96e67026a191f930c540a8c823c6ad5355dc4cb92824eadc9485aa195167768560dc506af358&token=948950272&lang=zh_CN#rd) 16 | 17 | - [一篇带你读懂TCP之“滑动窗口”协议 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483706&idx=1&sn=8eed9d160013bd8ed6203ad511711553&chksm=96e67029a191f93fdd1543af2bf06025397d9c3bd0f0692c7fe247ab9c139cd869d69ab05498&token=948950272&lang=zh_CN#rd) 18 | 19 | 20 | > notebook: JAVA网络安全面试题 21 | 22 | - [[Java网络系列面试题]常见web攻击有哪些?](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484207&idx=1&sn=72bc31c8b141013fdb3ba4cf77c20326&chksm=96e6723ca191fb2a5f17a9918b006b6b3d4341d166c4aaf497ad2d7bcff2d4ef03b4e40bd4dd&token=1394220820&lang=zh_CN#rd) 23 | 24 | - [[Java网络安全系列面试题] GET 和 POST 的区别在哪里?](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484212&idx=1&sn=ea30e28ab5ce9565cdf8b4ca7c273001&chksm=96e67227a191fb31e5886c71d1ec88315c0a526d648a998aabb5f1f4111c448f8d780270221c&token=1170466385&lang=zh_CN#rd) 25 | 26 | - [[Java网络安全系列面试题] HTTP和HTTPS协议区别和联系都有哪些?](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484216&idx=1&sn=09a1bad01e2286429a9518bd8f09c3cd&chksm=96e6722ba191fb3daec4925c5b71cc1e958109a626e3609d558dfa945735dcdecc373f8c0d3a&token=1458694075&lang=zh_CN#rd) 27 | 28 | --- 29 | 30 | > notebook: 数据库面试题 31 | 32 | - [带你了解数据库中JOIN的用法](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483713&idx=1&sn=d61ad0aed42dc36d64d17732db352288&chksm=96e67052a191f9445bbe3d5825ce547ad3171c3874b571a93b97977d0668413e37a164c3e0bc&token=948950272&lang=zh_CN#rd) 33 | 34 | - [带你了解数据库中group by的用法](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483717&idx=1&sn=157a8a021c29043a10480d0294b39ca0&chksm=96e67056a191f940668812ebb092fe9984b22eb619a18339cc052e1051c659a7e9d907c48814&token=948950272&lang=zh_CN#rd) 35 | 36 | - [带你了解数据库中事务的ACID特性](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483722&idx=1&sn=e8bc8bd82c559e0cfe7f35cf46100af3&chksm=96e67059a191f94fe8948e5b5e4ef177b77fa7707d86d945b153f67e7f2e76b83ed0c768ef27&token=948950272&lang=zh_CN#rd) 37 | 38 | - [5分钟带你读懂事务隔离性与隔离级别 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483728&idx=1&sn=cdc5dc4708e48051e56b8e7d2a9fe5a8&chksm=96e67043a191f955b93e7228b88572beb486e6fac3308a1b69f5ee83c9e9ced6957e30b12d58&token=948950272&lang=zh_CN#rd) 39 | 40 | - [面试必备的数据库悲观锁与乐观锁](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483733&idx=1&sn=1f066b1446a0a132af8648481063c021&chksm=96e67046a191f9508a133f6c37f2420140b6ca092eaf39012e6fbfa86874fbb57edef6d66b4e&token=948950272&lang=zh_CN#rd) 41 | 42 | > 数据库思维导图 43 | 44 | 45 | --- 46 | 47 | > notebook: Spring全家桶面试题 48 | 49 | - [Spring面试题集锦(精选)](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484099&idx=1&sn=8e7ad8e24c2ced9bc9a5bee16ffea66a&chksm=96e673d0a191fac60672313b7f8031d6b76daa1d5eec4b7c698505877fc2cc3a21c47cf2a922&token=1975823476&lang=zh_CN#rd) 50 | 51 | - [SpringMVC面试题集锦(精选)](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484113&idx=1&sn=e6c5c5942152ee1cd6bacbab571a766c&chksm=96e673c2a191fad42c54a718dbfcfad8f4a32448f63c832e081eea2a130968f5a12bb165c6bb&token=1975823476&lang=zh_CN#rd) 52 | 53 | - [MyBatis面试题集锦(精选)](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484117&idx=1&sn=bed12cc4ed5c27e2300ab92d77366fe6&chksm=96e673c6a191fad048b62da1eab00489a490da5185aa1b9566d41f6359d6871ada1c396d9891&token=550628603&lang=zh_CN#rd) 54 | 55 | 56 | > notebook: 其他中间件面试题 57 | 58 | - [Redis面试题集锦(精选)](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484121&idx=1&sn=78e12e075ba86a2ed3c5821a47419386&chksm=96e673caa191fadce4ca991c88f06d5b5628a8b8d591b0fe6d6e4c752ce7ec0dcbe612df88a2&token=550628603&lang=zh_CN#rd) 59 | 60 | 61 | - [RabbitMQ面试题集锦(精选)(另附思维导图)](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484127&idx=1&sn=78973c2ae28ed10cdfc87cb169f7a04c&chksm=96e673cca191fadae40218595844e6b288abf027245dc9763bc84e92d8afc88283b4722d18e2&token=550628603&lang=zh_CN#rd) -------------------------------------------------------------------------------- /src/02-data-structure-and-algorithm/README.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,内容正在努力加载中...... # -------------------------------------------------------------------------------- /src/02-data-structure-and-algorithm/Recursion.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ![关注我](https://img-blog.csdnimg.cn/20190426221838971.gif) 4 | ![从“数学归纳法”到理解“递归算法”!](https://img-blog.csdnimg.cn/20190429222831363.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 5 | 6 | >每章一点正能量:人的一生可能燃烧也可能腐朽。 7 | ## 前言 8 | 相信大家在面试或者工作中偶尔会遇到递归算法的提问或者编程,我们今天来聊一聊`从数学归纳法到理解递归算法`。如有错误还请大家及时指出~ 9 | 10 | >本文已同步至 [GitHub](https://github.com/CoderMerlin/coder-programming)/[Gitee](https://gitee.com/573059382/coder-programming)/公众号,感兴趣的同学帮忙点波关注~ 11 | 12 | ## 1. 数学归纳法 13 | 14 | ### 1.1 简介 15 | 16 | > **来源百度百科** 17 | 18 | **数学归纳法**(Mathematical Induction, MI)是一种数学证明方法,通常被用于证明某个给定命题在整个(或者局部)自然数范围内成立。除了自然数以外,广义上的数学归纳法也可以用于证明一般良基结构,例如:集合论中的树。这种广义的数学归纳法应用于数学逻辑和计算机科学领域,称作结构归纳法。在数论中,数学归纳法是以一种不同的方式来证明任意一个给定的情形都是正确的(第一个,第二个,第三个,一直下去概不例外)的数学定理。虽然数学归纳法名字中有“归纳”,但是数学归纳法并非不严谨的归纳推理法,它属于完全严谨的演绎推理法。事实上,所有数学证明都是演绎法。 19 | 20 | **自然数**是指表示物体个数的数,即由0开始,0,1,2,3,4,……一个接一个,组成一个无穷的集体,即指非负整数。 21 | 22 | ### 1.2 推演步骤 23 | 24 | 简单了解数学归纳法的概念后,我们来看看数学归纳法的推演步骤。 25 | 26 | 我们知道数学归纳法用来证明任意一个给定的情形都是正确的,也就是说,第一个,第二个,一直到所有情形,概不例外。 27 | 28 | 其证明步骤如下: 29 | 30 | 1. 证明基本情况(通常是N = 1 的时候)是否成立。 31 | 证明对于N=1成立。我们只需要先从最小的自然数开始证明。这一步通常非常简单。关键是证明第二步。 32 | 33 | 2. 证明N > 1 时,假设 N - 1 成立,那么对于N成立(N为任意大于1的自然数)。 34 | 这一步并不是直接证明的,而是假设N-1成立,利用这个结论推出N是成立的。如果能够推出的话,就可以说:对于所有的自然数都成立。因为证明了对1成立,那么对2成立,对3也成立。那么就证明了对所有自然数都成立。 35 | 我们会发现数学归纳法它很合适用来证明,例如常见的等差、等比、以及平方、立方数列的求和等等。 36 | 37 | ### 1.3 小栗子 38 | 39 | 我们来举一个小栗子,回顾下我们高中时期所学的数学归纳法是如何进行证明。 40 | 41 | 例子: 42 | 43 | ``` 44 | 45 | 证明: 1+2+3+...+n = n(n+1)/2 46 | 47 | ``` 48 | 49 | 我们来将上面 `1.2 推演步骤` 用起来。 50 | 51 | - 第一步: 证明基本情况(通常是N = 1 的时候)是否成立。 52 | 53 | 我们把N=1同时代入等号左边和右边,得 54 | 55 | ``` 56 | 57 | 1 = 1*(1+1)/2 58 | 59 | ``` 60 | 61 | 成立! 62 | 63 | - 第二步: 证明N > 1 时,假设 N - 1 成立,那么对于N成立(N为任意大于1的自然数)。 64 | 65 | 这里我们需要分两步。 66 | 67 | - ① 假设对于N-1的情况下成立 68 | 69 | 我们依然将N-1同时代入等号的左边和右边,得: 70 | 71 | ``` 72 | 73 | 1+2+3+...+(n-1) = (n-1)n/2 74 | 75 | ``` 76 | 77 | 78 | - ② 将假设结论代入,同时加N 79 | 80 | 81 | 我们假设N-1是成立的,那么我们在等号左边与右边同时加N,肯定也是成立的,得: 82 | 83 | ``` 84 | 85 | 1+2+3...+(n-1)+n = (n-1)n/2+n 86 | 87 | ``` 88 | 89 | 化简右边得:`n(n+1)/2`,那么我们最后证明的结果就是成立的! 90 | 91 | 即:`1+2+3+...+n = n(n+1)/2` 成立。通过以上步骤,我们可以证明这个公式是成立的。 92 | 93 | ### 1.4 小结 94 | 95 | 归纳法适用于想解决一个问题转化为解决他的子问题,而他的子问题又变成子问题的子问题,而且我们发现这些问题其实都是一个模型,也就是说存在相同的逻辑归纳处理项。 96 | 97 | 接下来我们来看看,我们写程序和数学归纳法的关联。 98 | 99 | ## 2. 递归 100 | 101 | 说起递归算法,其实我们每个开发人员都肯定听过或者写过。记得我最开始接触递归算法的时候,还是大一学习谭浩强老师写的那本C语言时,里面介绍了递归算法。给我的印象就是:自己调用自己。后来在工作中,用到的地方也不多,印象中只有一次写级联菜单的时候用到了递归算法。(是不是我写的代码太水,大家也可以说说哪里用到过递归算法)本章就来通过数学归纳法来回顾下我们曾经学过的递归算法。 102 | 103 | ### 2.1 理解递归 104 | 105 | 递归的基本思想:`以此类推` 106 | 107 | 具体来讲就是把规模大的问题转化为规模小的相似的子问题来解决。在函数实现时,因为解决大问题的方法和解决小问题的方法往往是同一个方法,所以就产生了函数调用它自身的情况。另外这个解决问题的函数必须有明显的结束条件,这样就不会产生无限递归的情况了。仔细观察递归,就会发现:`递归的数学模型其实就是归纳法`。 108 | 109 | ### 2.2 递归条件 110 | 111 | 我们在使用递归的时候需要满足一些基本条件,如果不满足的话,就有可能出现无限递归,最后会导致堆栈溢出了。 112 | 113 | 满足条件: 114 | 115 | 1. 严格定义递归函数作用,包括参数,返回值,其他变量。 116 | 2. 先一般情况,后特殊情况。 117 | 3. 有退出条件。在一般情况下,能让递归正常退出的条件。 118 | 4. 每次调用必须缩小问题规模,且新问题与原问题有着相同的形式,即规律。 119 | 120 | 上面的条件一环扣一环,也可以缩减成两个主要条件:`有规律`,`有退出条件`。我们以上面的条件,来结合案例进行理解。 121 | 122 | ### 2.3 小栗子 123 | 124 | #### 2.3.1 递归求和 125 | 126 | 例题: 127 | 128 | ``` 129 | 130 | 1+2+3+...+n=? 131 | 132 | ``` 133 | 134 | **第一步:** 严格定义递归函数作用,包括参数,返回值,其他变量。 135 | 136 | 我们初看题目,可以知道这是一个简单的求和,即从1开始:1+2+3+...一直加到n。所以我们可以定义一个入参为n,返回值类型为int的一个方法,既然是递归求和,我们的方法名就叫recursionSum。 137 | 138 | ``` 139 | 140 | public static int recursionSum(int n) { //为了方便调用,我用了static 141 | 142 | return 0; 143 | } 144 | 145 | System.out.println("公众号:Coder编程:" + recursionSum(0)); 146 | 147 | ``` 148 | 149 | 那么我们第一步就做完了。 150 | 151 | 152 | **第二步:** 先一般情况,后特殊情况。 153 | 154 | 我们先用一般的情况进行求和计算,例如代入1,2,3这样的一般情况。即: 155 | 156 | ``` 157 | 158 | public static int recursionSum(int n) { 159 | if(n == 1) { 160 | return 1; 161 | } 162 | 163 | if(n == 2) { 164 | return 1+2; 165 | } 166 | 167 | if(n == 3) { 168 | return 1+2+3; 169 | } 170 | return 0; 171 | } 172 | 173 | System.out.println("公众号:Coder编程:" + recursionSum(3)); 174 | 175 | ``` 176 | 177 | **第三步:** 有退出条件。在一般情况下,能让递归正常退出的条件。 178 | 179 | 其实,我们做完第二步,就会发现已经把第三步做完了。即有了让递归正常退出的条件! 180 | 181 | **第四步:** 每次调用必须缩小问题规模,且新问题与原问题有着相同的形式,即规律。 182 | 183 | 这一步是最关键的,也是最核心的!我们需要找到其规律,并且能缩小问题的规模。我们会发现,当我们需要求第N个数的和的时候,我们必须知道前N-1个数的和,即 sum(N-1)。前N个数的和就是sum(N-1)+N。找到这个规律后,我们就可以定义一个临时变量sum来接收前N个数的和了。 184 | 185 | ``` 186 | 187 | public static int recursionSum(int n) { 188 | 189 | if(n == 1) { 190 | return 1; 191 | } 192 | 193 | if(n == 2) { 194 | return 1+2; 195 | } 196 | 197 | if(n == 3) { 198 | return 1+2+3; 199 | } 200 | 201 | int sum = recursionSum(n-1)+n; 202 | return sum; 203 | } 204 | 205 | System.out.println("公众号:Coder编程:前5个数的和" + recursionSum(5)); 206 | 207 | ``` 208 | 209 | > 输出结果:15 210 | 211 | 212 | 我们优化一下: 213 | 214 | ``` 215 | 216 | public static int recursionSum(int n) { 217 | 218 | if (n < 0){ 219 | throw new Exception("参数不能为负!"); 220 | } 221 | if(n == 1) { 222 | return 1; 223 | } 224 | 225 | return recursionSum(n-1)+n; 226 | } 227 | 228 | System.out.println("公众号:Coder编程:前5个数的和" + recursionSum(5)); 229 | 230 | ``` 231 | 232 | 是不是突然发现递归其实也没想的那么难? 233 | 234 | #### 2.3.2 举一反三? 235 | 236 | 接下来我们难度进行升级!看大家能不能都理解了。我就不像上面求和那么啰嗦了! 237 | 238 | ##### 2.3.2.1 求阶乘 239 | 240 | 例题:求n的阶乘(n>1,n是正整数) 241 | 242 | 阶乘的递推公式为:factorial(n)=n*factorial(n-1),其中n为非负整数,且0!=1,1!=1 243 | 这里就不做过多说明,跟求后过程一致,可以模仿求和的过程,大家可以先自己尝试写下,下面我直接贴代码了: 244 | 245 | 246 | ``` 247 | 248 | public static int factorial(int n) throws Exception { 249 | if (n < 0){ 250 | throw new Exception("参数不能为负!"); 251 | }else if (n == 1 || n == 0) { 252 | return 1; 253 | }else { 254 | return n * factorial(n - 1); 255 | } 256 | } 257 | 258 | System.out.println("公众号:Coder编程:3的阶乘:" + factorial(3)); 259 | 260 | 261 | ``` 262 | 263 | > 输出结果: 公众号:Coder编程:3的阶乘:6 264 | 265 | ##### 2.3.2.2 斐波那契数列 266 | 267 | `斐波那契数列` 我想大家同样熟悉了解,下面我们继续回顾一下斐波那契数列到底是什么? 268 | 269 | ![斐波那契数列图](https://img-blog.csdnimg.cn/2019042922114129.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 270 | 271 | 斐波那契数列: 1、1、2、3、5、8、13、21..... 272 | 273 | 可以看出从第三位起:第三项等于前两项之和。总结递推公式::Fib(n)=Fib(n-1)+Fib(n-2)。所以我们可以将前两位作为退出递归的条件。即:`if(n==1) retrun 1 if(n==2) return 1` 274 | 275 | 因此我们可以直接用公式(规律)和退出条件,写出编程代码: 276 | 277 | ``` 278 | 279 | public static int fib(int n) throws Exception { 280 | if (n < 0) { 281 | throw new Exception("参数不能为负!"); 282 | }else if (n == 0 || n == 1){ 283 | return n; 284 | }else { 285 | return fib(n - 1) + fib(n - 2); 286 | } 287 | } 288 | 289 | System.out.println("公众号:Coder编程:斐波那契数列:" + fib(3)); 290 | 291 | ``` 292 | 293 | ##### 2.3.2.3 汉诺塔问题 294 | 295 | 相传在古印度圣庙中,有一种被称为`汉诺塔(Hanoi)`的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置不同个数的金盘(如下图)。 296 | 297 | 游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。 298 | 299 | 操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。 300 | 301 | ![汉诺塔图](https://img-blog.csdnimg.cn/20190429221124496.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 302 | 303 | 在总结规律和写代码之前,我们先来玩几把简单的(**先一般后特殊**): 304 | 305 | > 注:我们以数字的大小作为盘子的大小。 306 | 307 | 1. 一个盘子的情况: 308 | 309 | 1.1 将A柱子的1号盘子直接移动到C柱子中。 310 | 1.2 结束。 311 | 312 | 313 | 2. 两个盘子的情况: 314 | 315 | 2.1 将A柱子的1号盘子移动到B柱子。 316 | 2.2 将A柱子的2号盘子移动到C柱子。 317 | 2.3 将B柱子的1号盘子移动到C柱子。 318 | 2.4 结束。 319 | 320 | 3. 三个盘子的情况: 321 | 322 | 3.1 将A柱子的1号盘子移动到C柱子。 323 | 3.2 将A柱子的2号盘子移动到B柱子。 324 | 3.3 将C柱子的1号盘子移动到B柱子。 325 | 3.4 将A柱子的3号盘子移动到C柱子。 326 | 3.5 将B柱子的1号盘子移动到A柱子。 327 | 3.6 将B柱子的2号盘子移动到C柱子。 328 | 3.7 将A柱子的1号盘子移动到C柱子。 329 | 3.8 结束。 330 | 331 | --- 332 | 333 | 我们会发现,随着盘子数量的增加,盘子移动的难度也开始加大。 334 | 335 | 这时候不要害怕,我们回过头再来看这个问题:当盘子的数量是4个、5个...N个的时候,我们该如何解决呢?我们是不是可以用数学归纳法的思想或者递归的思想去解决呢?答案是:肯定的。这时候我们需要去找到他们的规律在哪? 336 | 337 | 我们再观察下上面在一般情况下移动盘子的规律在哪? 338 | 339 | - 1.当只有一个盘子的时候,可以将盘子直接移动到目标柱子C中。即`退出条件`。 340 | - 2.当只有两个盘子的时候,我们只需要将B柱子作为中介,将盘子1先放到中介柱子B上,然后将盘子2放到目标柱子C上,最后将中介柱子B上的盘子放到目标柱子C上即可。 341 | 342 | 第二点可以看成:当我们有N个盘子的时候,第N个盘子看成一个盘子,(N-1)个盘子看做成一个盘子。需要将(N-1)个盘子放在中介柱子B上,N个盘子放在目标柱子C即可。即`规律`。 343 | 344 | 当我们有三个盘子的时候,我们会发现一个问题: **角色变化** 345 | 346 | 1. 将A塔座的第(N-1)~1个盘子看成是一个盘子,放到中柱子B上,然后将第N个盘子放到目标柱子C上。这时候`柱子A空了!柱子A成为中介柱子,柱子B成为起始柱子`。 347 | 348 | 2. 柱子B这时候有N-1个盘子,将第(N-2)~1个盘子看成是一个盘子,放到中介柱子A上,然后将柱子B的第(N-1)号盘子放到目标柱子C上。这时候`柱子B空了!柱子B又成为了中介柱子,A成为了起始柱子`! 349 | 350 | 重复1、2步骤,直到所有盘子都放到目标塔座C上结束。 351 | 352 | 总结一下: 353 | 354 | 1. 从初始柱子A上移动包含n-1个盘子到中介柱子B上。 355 | 2. 将初始柱子A上剩余的一个盘子(最大的一个盘子)放到目标柱子C上。 356 | 3. 将中介柱子B上n-1个盘子移动到目标柱子C上。 357 | 358 | ``` 359 | 360 | move(3,"A","B","C"); 361 | 362 | /** 363 | * 汉诺塔问题 364 | * @param dish 盘子个数(也表示名称) 365 | * @param from 初始柱子 366 | * @param temp 中介柱子 367 | * @param to 目标柱子 368 | */ 369 | public static void move(int dish,String from,String temp,String to){ 370 | if(dish == 1){ 371 | System.out.println("将盘子"+dish+"从柱子"+from+"移动到目标柱子"+to); 372 | }else{ 373 | move(dish-1,from,to,temp);//A为初始柱子,B为目标柱子,C为中介柱子 374 | System.out.println("将盘子"+dish+"从柱子"+from+"移动到目标柱子"+to); 375 | move(dish-1,temp,from,to);//B为初始柱子,C为目标柱子,A为中介柱子 376 | } 377 | } 378 | 379 | ``` 380 | 381 | - move(dish-1,from,to,temp);//A为初始柱子,B为目标柱子,C为中介柱子 382 | 这里需要将n-1之前的盘子都放到B柱子上,最后第n个盘子放到C柱子。 383 | 384 | - move(dish-1,temp,from,to);//B为初始柱子,C为目标柱子,A为中介柱子 385 | 这时候B变为了初始柱子,A成为了目标柱子。将之前n-1个盘子放到C目标柱子中。 386 | 387 | 打印结果: 388 | 389 | ![打印结果](https://img-blog.csdnimg.cn/20190429221104489.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 390 | 391 | ## 文末 392 | 393 | 本章节主要简单介绍了数学归纳法与递归算法的一些思想。希望对大家有所帮助! 394 | 今后我会在每张文章开头增加 **每章一点正能量** ,文末增加5个编程相关的英语单词 **学点英语**。希望大家和我一样每天都能积极向上,一起学习一同进步! 395 | 396 | ### 学点英语 397 | 398 | - JRE Java Runtime Environment(Java运行环境),运行 JAVA程序所必须的环境的集合,包含JVM标准实现及Java核心类库。 399 | - JSDK Java Software Development Kit,和JDK以及J2SE 等同。 400 | - JDK Java Development Kit(Java开发工具包):包括运行环境 、编译工具及其它工具、源代码等,基本上和J2SE等同。 401 | - J2ME Java 2 Micro Edition(JAVA2精简版)API规格基 于J2SE ,但是被修改为可以适合某种产品的单一要求。J2ME使JAVA程序可以很方便的应用于电话卡、寻呼机等小型设备,它包括两种类型的组件,即配置 (configuration)和描述(profile)。 402 | 403 | 404 | >欢迎关注公众号:**Coder编程** 405 | 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 406 | 407 | 408 | 409 | 参考文章: 410 | 411 | https://www.cnblogs.com/ysocean/p/8005694.html 412 | 413 | http://www.nowamagic.net/librarys/veda/detail/2314 414 | 415 | ![微信公众号](https://img-blog.csdnimg.cn/20190426221806703.jpg) 416 | 417 | ## 推荐阅读 418 | 419 | 420 | [一篇带你读懂TCP之“滑动窗口”协议 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483706&idx=1&sn=8eed9d160013bd8ed6203ad511711553&chksm=96e67029a191f93fdd1543af2bf06025397d9c3bd0f0692c7fe247ab9c139cd869d69ab05498&token=1104592742&lang=zh_CN#rd) 421 | 422 | [带你了解数据库中JOIN的用法 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483713&idx=1&sn=d61ad0aed42dc36d64d17732db352288&chksm=96e67052a191f9445bbe3d5825ce547ad3171c3874b571a93b97977d0668413e37a164c3e0bc&token=1144933717&lang=zh_CN#rd) 423 | 424 | [ 深入浅出了解“装箱与拆箱” ](http://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=100000124&idx=1&sn=3e6cda3aef95f2dd609dd96963094da6&chksm=16e6706f2191f97972ca1b674034d0fb55026521d1418d1c069f1367b5ada38455ed2f4e2fcf#rd) 425 | 426 | ![求关注](https://img-blog.csdnimg.cn/20190429222933150.gif) -------------------------------------------------------------------------------- /src/03-java-base/Collection.md: -------------------------------------------------------------------------------- 1 | 2 | 前言 3 | -- 4 | 5 | 欢迎关注公众号:**Coder编程** 6 | 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 7 | 8 | >本章主要介绍Collection集合相关知识,结合面试中会提到的相关问题进行知识点的梳理。希望能帮到大家~ 9 | >基于JDK1.8,如有错误,还望大家能够指出! 10 | 11 | 涉及的Collection集合相关面试题 12 | 13 | - 1.什么是集合? 14 | - 2.JAVA中集合类型都有哪些?有什么特点? 15 | - 3.说一说集合的父类Collection? 16 | - 4.数组和集合都有哪些区别? 17 | - 5.说一说迭代器Iterator 18 | - 6.Collection接口中几种重要的类和接口简介 19 | 20 | 1.什么是集合? 21 | -------- 22 | 23 | 来自百度百科的回答: 24 | 25 | 1. 集合类存放于java.util包中。 26 | 2. 集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用(reference)。 27 | 3. 集合类型主要有3种:set(集)、list(列表)和map(映射)。 28 | 4. 集合接口分为:Collection和Map,list、set实现了Collection接口 29 | 30 | 31 | 32 | 2.JAVA中集合类型都有哪些?各有什么特点? 33 | -------- 34 | 35 | **Collection两大体系:链表List、集合Set** 36 | 37 | List特点:元素有序;元素可以重复;元素都有索引(角标) 38 | List里存放的对象是有序的,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。 39 | 因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。 40 | 41 | Set特点:元素无序;元素不可以重复; 42 | Set里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。 43 | 44 | 同时集合中还有另外一种类型:Map(映射)。 45 | 46 | Map特点:键值对;键不可以重复;值可以重复; 47 | Map集合中存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。 48 | 49 | 50 | 51 | 3.说一说集合的父类Collection? 52 | -------- 53 | 54 | ### 3.1 Collection 体系结构图 55 | 56 | 57 | ![](https://user-gold-cdn.xitu.io/2019/3/20/1699b162f7c55697?w=720&h=385&f=jpeg&s=27756) 58 | 59 | 60 | Collection 61 | ``` 62 | List (有序集合,允许相同元素和null) 63 | --LinkedList (非同步,允许相同元素和null,遍历效率低插入和删除效率高) 64 | --ArrayList (非同步,允许相同元素和null,实现了动态大小的数组,遍历效率高,用的多) 65 | --Vector(同步,允许相同元素和null,效率低) 66 | ---Stack(继承自Vector,实现一个后进先出的堆栈) 67 | 68 | Set (无序集合,不允许相同元素,最多有一个null元素) 69 | --HashSet(无序集合,不允许相同元素,最多有一个null元素) 70 | 71 | Map (没有实现collection接口,key不能重复,value可以重复,一个key映射一个value) 72 | --Hashtable (实现Map接口,同步,不允许null作为key和value,用自定义的类当作key的话要复写hashCode和eques方法,) 73 | --HashMap (实现Map接口,非同步,允许null作为key和value,用的多) 74 | --WeakHashMap(实现Map接口) 75 | ``` 76 | ### 3.2 Collection 中的主要方法 77 | 78 | (1)添加 79 | boolean add(E o); 80 | boolean add(Collection c); 81 | 82 | (2)删除 83 | boolean remove(Object o); 84 | boolean removeAll(Collection c) 85 | 86 | void clear(); 87 | 88 | (3)判断 89 | a.判断集合中是否有元素:boolean isEmpty(); 90 | b.判断集合中是否包含某个元素:boolean contains(Object o); 91 | c.判断集合中是否包含某些元素:boolean contains(Collection c); 92 | 93 | 94 | 95 | (4)获取 96 | a.获取集合中元素个数:int size(); 97 | b.遍历集合中所有元素:Iterator iterator(); 98 | c.判断两个集合中是否存在相同的元素并保留两个集合中相同的元素删除不同的元素:boolean retainAll(Collection c); 99 | 100 | (5)其他 101 | 将集合中元素转为数组: 102 | a. Ojbect[] toArray(); 103 | b. T[] toArray(); 泛型 104 | 105 | **Java8新增方法** 106 | 在 JDK 8 以后,Collection 接口还提供了从集合获取连续的或者并行流: 107 | Stream stream() 108 | Stream parallelStream() 109 | 于Collection接口相关还有一个抽象类AbstractCollection: 110 | AbstractCollection是一个抽象类,实现了Collection接口的部分功能,实现了一些最基本的通用操作,把复杂的和业务相关的延迟到子类实现。 111 | 在AbstractCollection中,主要实现了contains(), isEmpty(), toArray(), remove(), clear() 这几个操作。有兴趣的同学可以自行研究下,逻辑都比较简单。 112 | 113 | > 特别注意:List接口扩展了一个一些方法,其中最重要,也是用的最多的是: 114 | E get(int index) 返回指定索引的元素 115 | 116 | 4.数组和集合都有哪些区别? 117 | -------- 118 | 119 | **数组特点:** 120 | 1. 数组本质上就是一段连续的内存空间,用于记录多个类型相同的数据; 121 | 2. 数据一旦声明完毕,则内存空间固定不变; 122 | 3. 插入和删除操作不方便,可能会移动大量的元素并导致效率太低; 123 | 4. 支持下标访问,可以实现随机访问; 124 | 5. 数组中的元素可以是基本数据类型,也可以使用引用数据类型。 125 | 126 | **集合特点:** 127 | 1. 内存空间可以不连续,数据类型可以不相同; 128 | 2. 集合的内存空间可以动态的调整; 129 | 3. 集合的插入删除操作可以不移动大量元素; 130 | 4. 部分支持下标访问,部分不支持; 131 | 5. 集合中的元素必须是引用数据类型(你存储的是简单的int,它会自动装箱成Integer); 132 | 133 | > 可以看出数组和集合在数据的存储,访问,类型,长度等都有不同的地方。 134 | 135 | 5.说一说迭代器 Iterator? 136 | -------- 137 | 1. 通过集合对象获取其对应的Iterator对象 138 | 2. 判断是否存在下一个元素 139 | 3. 取出该元素并将迭代器对象指向下一个元素 140 | Iterator iterator():取出元素的方式:迭代器。该对象必须依赖于具体容器,因为每一个容器的数据结构都不同。 141 | 所以该迭代器对象是在容器中进行内部实现的。对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可,也就是iterator方法。 142 | 143 | > 扩展知识: 144 | ArrayList里面的iterator方法采用了设计模式中的——工厂方法模式! 145 | 有兴趣的同学可以后续看到我另外的文章“设计模式精讲”专栏。 146 | 147 | 6.Collection接口中几种重要的类和接口简介? 148 | -------- 149 | **该问题与第二个问题类似~ 后续文章会有更详细的介绍!** 150 | 151 | Collection两大体系:链表List、集合Set 另映射Map 152 | 153 | **List接口及子类介绍** 154 | 155 |   List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。 和下面要提到的Set不同,List允许有相同的元素。 除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。 156 | 157 |   实现List接口的常用类有: 158 | - LinkedList类 (底层数据结构是链表,线程不安全) 159 | - ArrayList类 (底层数据结构是数组,线程不安全) 160 | - Vector类  (底层数据结构是数组,线程安全) 161 | - Stack类 (顾名思义:栈,继承至Vector类并进行扩展) 162 | 163 | ```在后续的文章中我们将一一详细介绍这些类的相关特性!``` 164 | 165 | 166 | 167 | **Set接口及子类介绍** 168 | 169 |   Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。    170 | 注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。 171 | 172 |   实现Set接口的常用类有: 173 | - HashSet类 (底层数据结构是数组+单链表+红黑树,无序) 174 | - LinkedHashSet (底层数据结构是数组+单链表+红黑树+双向链表,无序) 175 | - TreeSet类 (底层数据结构红黑树,有序) 176 | 177 | ```在后续的文章中我们将一一详细介绍这些类的相关特性!``` 178 | 179 | **Map接口及子类介绍** 180 | 注意:Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。 181 | 182 | Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。 183 | - Hashtable类 184 | - HashMap类   185 | 186 | ```在后续的文章中我们将一一详细介绍这些类的相关特性!``` 187 | 188 | 文末 189 | -------- 190 | >本章节介绍了Collection接口中的大部分可能在面试过程中会出现的内容, 191 | 并没有详细去介绍其子类及其实现相关的原理。这方面的内容会放在后续的章节中去详细介绍。 192 | 欢迎关注公众号:**Coder编程** 193 | 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 194 | 195 | ![公众号](https://user-gold-cdn.xitu.io/2019/3/23/169a973aebe9c394?w=344&h=344&f=jpeg&s=9623) -------------------------------------------------------------------------------- /src/03-java-base/HashMap(One).md: -------------------------------------------------------------------------------- 1 | 现在是晚上11点了,学校屠猪馆的自习室因为太晚要关闭了。勤奋且疲惫的小鲁班也从屠猪馆出来了,正准备回宿舍洗洗睡,由 2 | 于自习室位置比较偏僻所以是接收不到手机网络信号的,因此小鲁班从兜里掏出手机的时候,信息可真是炸了呀。小鲁班心想, 3 | 微信群平时都没什么人聊天,今晚肯定是发生了什么大事。仔细一看,才发现原来是小鲁班的室友达摩(光头)拿到了阿里巴巴 4 | Java 开发实习生的 Offer,此时小鲁班真替他室友感到高兴的同时,心里也难免会产生一丝丝的失落感, 5 | 那是因为自己投了很多份简历,别说拿不拿得到 Offer,就连给面试邀的公司也都寥寥无几。 6 | 小鲁班这会可真是受到了一万点真实暴击。不过小鲁班还是很乐观的,很快调整了心态,带上耳机, 7 | 慢慢的走回了宿舍,正打算准备向他那神室友达摩取取经。 8 | 9 | 片刻后~ 10 | 11 | 12 | 首先当然是JAVA的基础知识:数据结构(Map / List / Set等)、设计模式、算法、线程相关、IO/NIO、序列化等等。 13 | 其次是高级特征:反射机制,并发与锁,JVM(GC策略,类加载机制,内存模型)等等。 14 | 15 | 16 | 就比如问你 HashMap 是不是有序的?你回答不是有序的。 17 | 那面试官就会可能继续问你,有没有有序的Map实现类呢?你如果这个时候说不知道的话,那这块问题就到此结束了。 18 | 如果你说有 TreeMap 和 LinkedHashMap。那么面试官接下来就可能会问你,TreeMap 和 LinkedHashMap 是如何保证它的顺序的? 19 | 如果你回答不上来,那么到此为止。如果你说 TreeMap 是通过实现 SortMap 接口,能够把它保存的键值对根据 key 排序,基于红黑树, 20 | 从而保证 TreeMap 中所有键值对处于有序状态。LinkedHashMap 则是通过插入排序(就是你 put 的时候的顺序是什么,取出来的时候就是什么样子) 21 | 和访问排序(改变排序把访问过的放到底部)让键值有序。那么面试官还会继续问你,你觉得它们两个哪个的有序实现比较好? 22 | 如果你依然可以回答的话,那么面试官会继续问你,你觉得还有没有比它更好或者更高效的实现方式? 23 | 无穷无尽深入,直到你回答不出来或者面试官认为问题到底了。 24 | 25 | 1、为什么用HashMap? 26 | HashMap 是一个散列桶(数组和链表),它存储的内容是键值对 key-value 映射 27 | HashMap 采用了数组和链表的数据结构,能在查询和修改方便继承了数组的线性查找和链表的寻址修改 28 | HashMap 是非 synchronized,所以 HashMap 很快 29 | HashMap 可以接受 null 键和值,而 Hashtable 则不能(原因就是 equlas() 方法需要对象,因为 HashMap 是后出的 API 经过处理才可以) 30 | 31 | 2、HashMap 的工作原理是什么? 32 | HashMap 是基于 hashing 的原理 33 | 我们使用 put(key, value) 存储对象到 HashMap 中,使用 get(key) 从 HashMap 中获取对象。 34 | 当我们给 put() 方法传递键和值时,我们先对键调用 hashCode() 方法,计算并返回的 hashCode 35 | 是用于找到 Map 数组的 bucket 位置来储存 Node 对象。 36 | 这里关键点在于指出,HashMap 是在 bucket 中储存键对象和值对象,作为Map.Node 。 37 | 38 | 39 | 40 | 以下是 HashMap 初始化 41 | 简化的模拟数据结构: 42 | 43 | 44 | Node[] table = new Node[16]; // 散列桶初始化,table 45 | class Node { 46 | hash; //hash值 47 | key; //键 48 | value; //值 49 | node next; //用于指向链表的下一层(产生冲突,用拉链法) 50 | } 51 | 以下是具体的 put 过程(JDK1.8) 52 | 对 Key 求 Hash 值,然后再计算下标 53 | 如果没有碰撞,直接放入桶中(碰撞的意思是计算得到的 Hash 值相同,需要放到同一个 bucket 中) 54 | 如果碰撞了,以链表的方式链接到后面 55 | 如果链表长度超过阀值(TREEIFY THRESHOLD==8),就把链表转成红黑树,链表长度低于6,就把红黑树转回链表 56 | 如果节点已经存在就替换旧值 57 | 如果桶满了(容量16*加载因子0.75),就需要 resize(扩容2倍后重排) 58 | 以下是具体 get 过程 59 | 考虑特殊情况:如果两个键的 hashcode 相同,你如何获取值对象? 60 | 61 | 当我们调用 get() 方法,HashMap 会使用键对象的 hashcode 找到 bucket 位置,找到 bucket 位置之后,会调用 keys.equals() 方法去找到链表中正确的节点,最终找到要找的值对象。 62 | 63 | 64 | 65 | 3、有什么方法可以减少碰撞? 66 | 扰动函数可以减少碰撞 67 | 原理是如果两个不相等的对象返回不同的 hashcode 的话,那么碰撞的几率就会小些。这就意味着存链表结构减小,这样取值的话就不会频繁调用 equal 方法,从而提高 HashMap 的性能(扰动即 Hash 方法内部的算法实现,目的是让不同对象返回不同hashcode)。 68 | 69 | 使用不可变的、声明作 final 对象,并且采用合适的 equals() 和 hashCode() 方法,将会减少碰撞的发生 70 | 不可变性使得能够缓存不同键的 hashcode,这将提高整个获取对象的速度,使用 String、Integer 这样的 wrapper 类作为键是非常好的选择。 71 | 72 | 为什么 String、Integer 这样的 wrapper 类适合作为键? 73 | 因为 String 是 final,而且已经重写了 equals() 和 hashCode() 方法了。不可变性是必要的,因为为了要计算 hashCode(),就要防止键值改变,如果键值在放入时和获取时返回不同的 hashcode 的话,那么就不能从 HashMap 中找到你想要的对象。 74 | 75 | 4、HashMap 中 hash 函数怎么是实现的? 76 | 我们可以看到,在 hashmap 中要找到某个元素,需要根据 key 的 hash 值来求得对应数组中的位置。如何计算这个位置就是 hash 算法。 77 | 78 | 前面说过,hashmap 的数据结构是数组和链表的结合,所以我们当然希望这个 hashmap 里面的元素位置尽量的分布均匀些,尽量使得每个位置上的元素数量只有一个。那么当我们用 hash 算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,而不用再去遍历链表。 所以,我们首先想到的就是把 hashcode 对数组长度取模运算。这样一来,元素的分布相对来说是比较均匀的。 79 | 80 | 但是“模”运算的消耗还是比较大的,能不能找一种更快速、消耗更小的方式?我们来看看 JDK1.8 源码是怎么做的(被楼主修饰了一下) 81 | 82 | 83 | static final int hash(Object key) { 84 | if (key == null){ 85 | return 0; 86 | } 87 | int h; 88 | h = key.hashCode();返回散列值也就是hashcode 89 | // ^ :按位异或 90 | // >>>:无符号右移,忽略符号位,空位都以0补齐 91 | //其中n是数组的长度,即Map的数组部分初始化长度 92 | return (n-1)&(h ^ (h >>> 16)); 93 | } 94 | 95 | 96 | 简单来说就是: 97 | 98 | 高16 bit 不变,低16 bit 和高16 bit 做了一个异或(得到的 hashcode 转化为32位二进制,前16位和后16位低16 bit和高16 bit做了一个异或) 99 | (n·1) & hash = -> 得到下标 100 | 5、拉链法导致的链表过深,为什么不用二叉查找树代替而选择红黑树?为什么不一直使用红黑树? 101 | 之所以选择红黑树是为了解决二叉查找树的缺陷:二叉查找树在特殊情况下会变成一条线性结构(这就跟原来使用链表结构一样了,造成层次很深的问题),遍历查找会非常慢。而红黑树在插入新数据后可能需要通过左旋、右旋、变色这些操作来保持平衡。引入红黑树就是为了查找数据快,解决链表查询深度的问题。我们知道红黑树属于平衡二叉树,为了保持“平衡”是需要付出代价的,但是该代价所损耗的资源要比遍历线性链表要少。所以当长度大于8的时候,会使用红黑树;如果链表长度很短的话,根本不需要引入红黑树,引入反而会慢。 102 | 103 | 6、说说你对红黑树的见解? 104 | 105 | 106 | 每个节点非红即黑 107 | 根节点总是黑色的 108 | 如果节点是红色的,则它的子节点必须是黑色的(反之不一定) 109 | 每个叶子节点都是黑色的空节点(NIL节点) 110 | 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度) 111 | 7、解决 hash 碰撞还有那些办法? 112 | 开放定址法 113 | 当冲突发生时,使用某种探查技术在散列表中形成一个探查(测)序列。沿此序列逐个单元地查找,直到找到给定的地址。按照形成探查序列的方法不同,可将开放定址法区分为线性探查法、二次探查法、双重散列法等。 114 | 115 | 下面给一个线性探查法的例子: 116 | 117 | 问题:已知一组关键字为 (26,36,41,38,44,15,68,12,06,51),用除余法构造散列函数,用线性探查法解决冲突构造这组关键字的散列表。 118 | 解答:为了减少冲突,通常令装填因子 α 由除余法因子是13的散列函数计算出的上述关键字序列的散列地址为 (0,10,2,12,5,2,3,12,6,12)。 119 | 前5个关键字插入时,其相应的地址均为开放地址,故将它们直接插入 T[0]、T[10)、T[2]、T[12] 和 T[5] 中。 120 | 当插入第6个关键字15时,其散列地址2(即 h(15)=15%13=2)已被关键字 41(15和41互为同义词)占用。故探查 h1=(2+1)%13=3,此地址开放,所以将 15 放入 T[3] 中。 121 | 当插入第7个关键字68时,其散列地址3已被非同义词15先占用,故将其插入到T[4]中。 122 | 当插入第8个关键字12时,散列地址12已被同义词38占用,故探查 hl=(12+1)%13=0,而 T[0] 亦被26占用,再探查 h2=(12+2)%13=1,此地址开放,可将12插入其中。 123 | 类似地,第9个关键字06直接插入 T[6] 中;而最后一个关键字51插人时,因探查的地址 12,0,1,…,6 均非空,故51插入 T[7] 中。 124 | 125 | 8、如果 HashMap 的大小超过了负载因子(load factor)定义的容量怎么办? 126 | HashMap 默认的负载因子大小为0.75。也就是说,当一个 Map 填满了75%的 bucket 时候,和其它集合类一样(如 ArrayList 等),将会创建原来 HashMap 大小的两倍的 bucket 数组来重新调整 Map 大小,并将原来的对象放入新的 bucket 数组中。这个过程叫作 rehashing。 127 | 128 | 因为它调用 hash 方法找到新的 bucket 位置。这个值只可能在两个地方,一个是原下标的位置,另一种是在下标为 <原下标+原容量> 的位置。 129 | 130 | 9、重新调整 HashMap 大小存在什么问题吗? 131 | 重新调整 HashMap 大小的时候,确实存在条件竞争。 132 | 133 | 因为如果两个线程都发现 HashMap 需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来。因为移动到新的 bucket 位置的时候,HashMap 并不会将元素放在链表的尾部,而是放在头部。这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了。多线程的环境下不使用 HashMap。 134 | 135 | 为什么多线程会导致死循环,它是怎么发生的? 136 | HashMap 的容量是有限的。当经过多次元素插入,使得 HashMap 达到一定饱和度时,Key 映射位置发生冲突的几率会逐渐提高。这时候, HashMap 需要扩展它的长度,也就是进行Resize。 137 | 138 | 扩容:创建一个新的 Entry 空数组,长度是原数组的2倍 139 | rehash:遍历原 Entry 数组,把所有的 Entry 重新 Hash 到新数组 140 | (这个过程比较烧脑,暂不作流程图演示,有兴趣去看看我的另一篇博文“HashMap扩容全过程”) 141 | 142 | 达摩:哎呦,小老弟不错嘛~~意料之外呀 143 | 小鲁班:嘿嘿,优秀吧,中场休息一波,我先喝口水 144 | 达摩:不仅仅是这些哦,面试官还会问你相关的集合类对比,比如: 145 | 146 | 10、HashTable 147 | 数组 + 链表方式存储 148 | 默认容量:11(质数为宜) 149 | put操作:首先进行索引计算 (key.hashCode() & 0x7FFFFFFF)% table.length;若在链表中找到了,则替换旧值,若未找到则继续;当总元素个数超过 容量 * 加载因子 时,扩容为原来 2 倍并重新散列;将新元素加到链表头部 150 | 对修改 Hashtable 内部共享数据的方法添加了 synchronized,保证线程安全 151 | 11、HashMap 与 HashTable 区别 152 | 默认容量不同,扩容不同 153 | 线程安全性:HashTable 安全 154 | 效率不同:HashTable 要慢,因为加锁 155 | 12、可以使用 CocurrentHashMap 来代替 Hashtable 吗? 156 | 我们知道 Hashtable 是 synchronized 的,但是 ConcurrentHashMap 同步性能更好,因为它仅仅根据同步级别对 map 的一部分进行上锁 157 | ConcurrentHashMap 当然可以代替 HashTable,但是 HashTable 提供更强的线程安全性 158 | 它们都可以用于多线程的环境,但是当 Hashtable 的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。由于 ConcurrentHashMap 引入了分割(segmentation),不论它变得多么大,仅仅需要锁定 Map 的某个部分,其它的线程不需要等到迭代完成才能访问 Map。简而言之,在迭代的过程中,ConcurrentHashMap 仅仅锁定 Map 的某个部分,而 Hashtable 则会锁定整个 Map 159 | 13、CocurrentHashMap(JDK 1.7) 160 | CocurrentHashMap 是由 Segment 数组和 HashEntry 数组和链表组成 161 | Segment 是基于重入锁(ReentrantLock):一个数据段竞争锁。每个 HashEntry 一个链表结构的元素,利用 Hash 算法得到索引确定归属的数据段,也就是对应到在修改时需要竞争获取的锁。ConcurrentHashMap 支持 CurrencyLevel(Segment 数组数量)的线程并发。每当一个线程占用锁访问一个 Segment 时,不会影响到其他的 Segment 162 | 核心数据如 value,以及链表都是 volatile 修饰的,保证了获取时的可见性 163 | 首先是通过 key 定位到 Segment,之后在对应的 Segment 中进行具体的 put 操作如下: 164 | 将当前 Segment 中的 table 通过 key 的 hashcode 定位到 HashEntry。 165 | 遍历该 HashEntry,如果不为空则判断传入的 key 和当前遍历的 key 是否相等,相等则覆盖旧的 value 166 | 不为空则需要新建一个 HashEntry 并加入到 Segment 中,同时会先判断是否需要扩容 167 | 最后会解除在 1 中所获取当前 Segment 的锁。 168 | 虽然 HashEntry 中的 value 是用 volatile 关键词修饰的,但是并不能保证并发的原子性,所以 put 操作时仍然需要加锁处理 169 | 首先第一步的时候会尝试获取锁,如果获取失败肯定就有其他线程存在竞争,则利用 scanAndLockForPut() 自旋获取锁。 170 | 171 | 尝试自旋获取锁 172 | 如果重试的次数达到了 MAX_SCAN_RETRIES 则改为阻塞锁获取,保证能获取成功。最后解除当前 Segment 的锁 173 | 14、CocurrentHashMap(JDK 1.8) 174 | CocurrentHashMap 抛弃了原有的 Segment 分段锁,采用了 CAS + synchronized 来保证并发安全性。 175 | 其中的 val next 都用了 volatile 修饰,保证了可见性。 176 | 177 | 最大特点是引入了 CAS 178 | 借助 Unsafe 来实现 native code。CAS有3个操作数,内存值 V、旧的预期值 A、要修改的新值 B。 179 | 当且仅当预期值 A 和内存值 V 相同时,将内存值V修改为 B,否则什么都不做。Unsafe 借助 CPU 指令 cmpxchg 来实现。 180 | 181 | CAS 使用实例 182 | 对 sizeCtl 的控制都是用 CAS 来实现的: 183 | 184 | -1 代表 table 正在初始化 185 | N 表示有 -N-1 个线程正在进行扩容操作 186 | 如果 table 未初始化,表示table需要初始化的大小 187 | 如果 table 初始化完成,表示table的容量,默认是table大小的0.75倍,用这个公式算 0.75(n – (n >>> 2)) 188 | CAS 会出现的问题:ABA 189 | 解决:对变量增加一个版本号,每次修改,版本号加 1,比较的时候比较版本号。 190 | 191 | put 过程 192 | 根据 key 计算出 hashcode 193 | 判断是否需要进行初始化 194 | 通过 key 定位出的 Node,如果为空表示当前位置可以写入数据,利用 CAS 尝试写入,失败则自旋保证成功 195 | 如果当前位置的 hashcode == MOVED == -1,则需要进行扩容 196 | 如果都不满足,则利用 synchronized 锁写入数据 197 | 如果数量大于 TREEIFY_THRESHOLD 则要转换为红黑树 198 | get 过程 199 | 根据计算出来的 hashcode 寻址,如果就在桶上那么直接返回值 200 | 如果是红黑树那就按照树的方式获取值 201 | 就不满足那就按照链表的方式遍历获取值 202 | 此时躺着床上的张飞哄了一声:睡觉了睡觉了~ 203 | 204 | 见此不太妙:小鲁班立马回到床上把被子盖过头,心里有一丝丝愉悦感。不对,好像还没洗澡…… 205 | 206 | by the way 207 | 208 | ConcurrentHashMap 在 Java 8 中存在一个 bug 会进入死循环,原因是递归创建 ConcurrentHashMap 对象, 209 | 但是在 JDK 1.9 已经修复了。场景重现如下: 210 | 211 | 212 | public class ConcurrentHashMapDemo{ 213 | private Map cache =new ConcurrentHashMap<>(15); 214 | 215 | public static void main(String[]args){ 216 | ConcurrentHashMapDemo ch = new ConcurrentHashMapDemo(); 217 | System.out.println(ch.fibonaacci(80)); 218 | } 219 | 220 | public int fibonaacci(Integer i){ 221 | if(i==0||i ==1) { 222 | return i; 223 | } 224 | 225 | return cache.computeIfAbsent(i,(key) -> { 226 | System.out.println("fibonaacci : "+key); 227 | return fibonaacci(key -1)+fibonaacci(key - 2); 228 | }); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/03-java-base/IterableAndIterator.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | > 欢迎关注公众号:**Coder编程** 3 | > 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 4 | 5 | 在上一篇文章[通过面试题,让我们来了解Collection](),我们会发现Collection接口之上还有一个接口**Iterable**, 6 | Iterable接口里面又有Iterator接口,那他们到底有什么区别呢?我们接下来就来了解下Iterable与Iterator相关内容,也就是本章的主要内容了,说不定 7 | 在我们面试过程中,也会遇到一些问题呢。那我们就开始吧~ 8 | 9 | 涉及面试题: 10 | - 1.说一说Iterable? 11 | - 2.Iterable结构? 12 | - 3.说一说Iterator? 13 | - 4.Iterator结构? 14 | - 5.forEachRemaining()与forEach()方法的区别? 15 | - 6.Iterator如何使用? 16 | - 7.Iterable与Iterator之间的区别与联系? 17 | 18 | 19 | >上面的面试题可以看出,其实都是一回事,只是换了一种提问方式,只要我们能掌握核心要点,随便面试官怎么提问,我们都能轻松应对! 20 | 21 | > **来源百度百科:** 22 | 23 | **Iterable:** 百度的时候,我却只看到了百度翻译:可迭代的; 可重复的; 迭代的; 因此我们可以知道,实现了这个接口的集合对象支持迭代,是可迭代的。 24 | **Iterator:** Iterator我们一般叫迭代器,它就是提供迭代机制的对象,具体如何迭代,都是Iterator接口规范的。通过UML图,我们也可以看出Iterable接口是Java集合框架的顶级接口, 25 | 实现此接口使集合对象可以通过迭代器遍历自身元素。同时在Java设计模式中也有一种模式——**迭代器模式**.(在后续的文章我们会介绍相关设计模式,敬请关注~) 26 | 可以看出Iterator是迭代器模式最好的一个应用例子! 27 | 28 | [CollectionUML图](images/Collection.png) 29 | 30 | ## 1.说一说Iterable? 31 | 32 | [Iterable方法图](images/Iterable方法.png) 33 | 34 | 由源码图可以看出,Iterable有三个方法,分别是 35 | - Iterator iterator(); 36 | - default void forEach(Consumer action){}; **JDK 1.8后新增** 37 | - default Spliterator spliterator(){}; **JDK 1.8后新增** 38 | 39 | 接下来我们简单介绍下这里面的方法。 40 | 41 | ### 1.1 Iterable接口中的iterator() 方法 42 | 43 | ``` 44 | Iterator iterator(); 45 | 46 | ``` 47 | 48 | 该接口主要是返回T类型的元素上的一个迭代器。下面再详细介绍Iterator。 49 | 50 | ### 1.2 Iterable接口中的forEach() 方法 51 | 52 | ``` 53 | default void forEach(Consumer action) { 54 | // 验证action是否为null,如果action为null,则抛出NullPointerException 55 | Objects.requireNonNull(action); 56 | for (T t : this) { 57 | action.accept(t); 58 | } 59 | } 60 | ``` 61 | 该方法是循环输出,对内部元素进行遍历,并对元素进行指定的操作。例如: 62 | 63 | ``` 64 | List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); 65 | numbers.forEach(integer -> System.out.println(integer)); 66 | ``` 67 | **对源码注释翻译:** 68 | 对这个Iterable的每一个元素执行给定的动作指导所有元素都被处理或者动作抛出一个异常 69 | 为止。除非被实现类指定,动作将以迭代的顺序执行(如果一个迭代的顺序被指定。)被动作 70 | 抛出的异常将被传递给调用者 71 | 72 | 73 | 74 | ### 1.3 Iterable接口中的spliterator() 方法 75 | 76 | 77 | ``` 78 | default Spliterator spliterator() { 79 | return Spliterators.spliteratorUnknownSize(iterator(), 0); 80 | } 81 | ``` 82 | 该方法提供了一个可以并行遍历元素的迭代器,以适应现在cpu多核时代并行遍历的需求。简单说:分割,增加并行处理能力 83 | **对源码注释翻译:** 84 | 创建一个被这个Iterable接口描述的元素上Spliterator。默认实现从iterable的Iterator中创建一个早期绑定的spliterator。这个spliterator 85 | 继承iterable的iterator的fail-fast性质。 86 | 默认实现应该总是被重写。被默认实现返回的spliterator拥有不好分解能力,是不依据指定 87 | 大小定制的,而且不报告任何spliterator的性质。实现类差不多总是能提供更好的实现。 88 | 89 | 90 | 91 | ## 2.说一说Iterator? 92 | 93 | ### 2.1 Iterator是什么? 94 | Iterator被称之为顺序遍历迭代器,jdk中默认对集合框架中数据结构做了实现。 95 | Iterator在实际应用中有一个比较好的点就是,可以一边遍历一遍删除元素。后面我会通过ArrayList中的Iterator()方法进行说明。 96 | 97 | ### 2.2 Iterator结构? 98 | [Iterable方法图](images/Iterator方法.png) 99 | 100 | 由源码图Iterator接口中定义了四个方法,分别是 101 | 102 | - boolean hasNext():如果被迭代遍历的集合还没有被遍历完,返回True 103 | - Object next():返回集合里面的下一个元素 104 | - remove():删除集合里面上一次next()方法返回的元素 105 | - void forEachRemaining(Consumer action):**JDK 1.8后新增默认方法** 使用Lambda表达式来遍历集合元素 106 | 107 | ### 2.3 Iterator接口中的forEachRemaining() 方法 108 | 109 | 在JDK1.8之后Iterator中增加的一个默认方法 110 | 111 | ``` 112 | //使用方法 113 | List arr=new ArrayList<>(); 114 | arr.add("hello"); 115 | arr.add(("world")); 116 | arr.iterator().forEachRemaining(str-> System.out.println(str)); 117 | ``` 118 | 119 | #### 2.3.1 forEachRemaining()与forEach()方法之间的区别? 120 | 121 | **forEachRemaining()源码:** 122 | 123 | ``` 124 | default void forEachRemaining(Consumer action) { 125 | Objects.requireNonNull(action); 126 | while (hasNext()) 127 | action.accept(next()); 128 | } 129 | ``` 130 | 131 | **forEach()源码:** 132 | 133 | ``` 134 | default void forEach(Consumer action) { 135 | Objects.requireNonNull(action); 136 | for (T t : this) { 137 | action.accept(t); 138 | } 139 | } 140 | ``` 141 | 142 | 通过源码,我们可以看出他们之间的区别与联系。 143 | **相同点:** 144 | - 都可以遍历集合 145 | - 都是接口的默认方法 146 | - 都是1.8版本引入的 147 | 148 | **区别:** 149 | - forEachRemaining()方法内部是通过使用迭代器Iterator的所有元素,forEach()方法内部使用的是增强for循环。 150 | - forEach()方法可以多次调用,forEachRemaining()方法第二次调用不会做任何操作,因为不会有下一个元素。 151 | 152 | ## 3.Iterator如何使用? 153 | ### 简单举个小栗子 154 | 155 | ``` 156 | List list = new ArrayList(); 157 | list.add("公众号"); 158 | list.add("Coder编程"); 159 | for (Iterator iter = l.iterator(); iter.hasNext();) { 160 | String str = (String)iter.next(); 161 | System.out.println(str); 162 | } 163 | /*迭代器用于while循环 164 | Iterator iter = l.iterator(); 165 | while(iter.hasNext()){ 166 | String str = (String) iter.next(); 167 | System.out.println(str); 168 | } 169 | */ 170 | ``` 171 | 172 | ## 推荐阅读 173 | [带你了解Collection相关知识!](03-java-base/Collection.md) 174 | [一篇让你理解进程与线程的区别与联系!](03-java-base/ProcessAndThread.md) 175 | 176 | ## 文末 177 | 178 | >本章节主要介绍了Iterable与Iterator之间的区别与联系,以及其他方面的小知识点,也是面试过程中会出现的内容点。 179 | 欢迎关注公众号:**Coder编程** 180 | 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 181 | 182 | [Github个人主页目录](https://github.com/CoderMerlin/coder-programming) 183 | [Gitee个人主页目录](https://gitee.com/573059382/coder-programming) 184 | 185 | **欢迎大家关注并Star~** 186 | -------------------------------------------------------------------------------- /src/03-java-base/List-ArrayList.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoderMerlin/coder-programming/75cd46b991771cbea0199c974eb7670acf248fc5/src/03-java-base/List-ArrayList.md -------------------------------------------------------------------------------- /src/03-java-base/List.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoderMerlin/coder-programming/75cd46b991771cbea0199c974eb7670acf248fc5/src/03-java-base/List.md -------------------------------------------------------------------------------- /src/03-java-base/ProcessAndThread.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | > 欢迎关注公众号:**Coder编程** 3 | > 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 4 | 5 | 本章主要介绍进程与线程的区别与联系相关知识点,也是我们面试过程中,经常会问到的了一个问题。希望通过这篇文章,能让大家理解相关知识点~ 6 | 7 | 8 | 涉及面试题: 9 | - 1.进程与线程之间有什么区别? 10 | - 2.进程、线程都各有什么特点? 11 | - 3.进程之间的是怎么进行交互的呢? 12 | - 4.什么是缓冲区溢出? 13 | - 5.进程之间如何进行交互? 14 | - 6.线程之间如何进行交互? 15 | 16 | >上面的面试题可以看出,其实都是一回事,只是换了一种提问方式,只要我们能掌握核心要点,随便面试官怎么提问,我们都能轻松应对! 17 | 18 | ## 1. 小栗子: 19 | 我们生活中有许许多多关于进程与线程的小栗子,比如:1.我们使用打开一个微信软件,这个时候就开启了一个进程, 20 | 当我们在微信里面进行各种操作(查看朋友圈,扫一扫...),这么多的操作就是线程。 21 | 所以我们可以说“进程”是包含“线程”的,“线程”是“进程”的一个子集。 22 | 23 | > **来源百度百科:** 24 | 25 | **进程(Process)** 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。 26 | 27 | **线程(thread)** 是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 28 | 29 | > 我们简单总结下: 30 | 31 | 进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程——资源分配的最小单位。 32 | 33 | 线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程——程序执行的最小单位。 34 | 35 | 36 | ## 2. 深入理解: 37 | 38 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190320210031603.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 39 | ### 2.1 进程(线程+内存+文件/网络句柄) 40 | 我们通过上面的图片进行进一步理解: 41 | 42 | **“内存”:** 43 | 我们通常所理解的内存是我们所见到的(2G/4G/8G/16G)物理内存,它为什么会在进程之中呢? 44 | 实际上,这里的内存是逻辑内存。指的是内存的寻址空间。每个进程的内存是相互独立的。 45 | 否则的话会出现一个问题:我们把指针的值改一改就指向其他进程的内存了,通过这样我们岂不是就可以看到其他进程中"微信"或者是"网上银行"的信息, 46 | 这样的话,那我们的微信聊天记录或者是银行账户的信息就都被别人找到了,这是一个很危险的信号!显然这样是不可能的。 47 | 48 | **“文件/网络句柄”:** 49 | 它们是所有的进程所共有的,例如打开同一个文件,去抢同一个网络的端口这样的操作是被允许的。 50 | 51 | **“线程”:** 52 | 接下来,我们就要介绍一下我们的“线程”有关知识 53 | 54 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190320210656574.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 55 | 56 | ### 2.2 线程(栈+PC+TLS) 57 | 58 | #### 2.2.1 栈: 59 | 60 | 我们通常都是说调用堆栈,其实这里的堆是没有含义的,调用堆栈就是调用栈的意思。 61 | 那么我们的栈里面有什么呢? 62 | 我们从主线程的入口main函数,会不断的进行函数调用, 63 | 每次调用的时候,会把所有的参数和返回地址压入到栈中。 64 | 65 | #### 2.2.2 PC: 66 | 67 | Program Counter 程序计数器,操作系统真正运行的是一个个的线程, 68 | 而我们的进程只是它的一个容器。PC就是指向当前的指令,而这个指令是放在内存中。 69 | 每个线程都有一串自己的指针,去指向自己当前所在内存的指针。 70 | 计算机绝大部分是存储程序性的,说的就是我们的数据和程序是存储在同一片内存里的 71 | 这个内存中既有我们的数据变量又有我们的程序。所以我们的PC指针就是指向我们的内存的。 72 | 73 | ##### 2.2.2.1 缓冲区溢出 74 | 例如我们经常听到一个漏洞:**缓冲区溢出** 75 | 这是什么意思呢? 76 | 例如:我们有个地方要输入用户名,本来是用来存数据的地方。 77 | 然后黑客把数据输入的特别长。这个长度超出了我们给数据存储的内存区,这时候跑到了 78 | 我们给程序分配的一部分内存中。黑客就可以通过这种办法将他所要运行的代码 79 | 写入到用户名框中,来植入进来。我们的解决方法就是,用用户名的长度来限制不要超过 80 | 用户名的缓冲区的大小来解决。 81 | 82 | #### 2.3 TLS: 83 | 84 | 全称:thread local storage 85 | 之前我们看到每个进程都有自己独立的内存,这时候我们想,我们的线程有没有一块独立的内存呢?答案是有的,就是TLS。 86 | 可以用来存储我们线程所独有的数据。 87 | 可以看到:线程才是我们操作系统所真正去运行的,而进程呢,则是像容器一样他把需要的一些东西放在了一起,而把不需要的东西做了一层隔离,进行隔离开来。 88 | 89 | ### 3. 进程之间的是怎么进行交互的呢? 90 | 通过TCP/IP的端口来实现 91 | ```在后续的文章中我们将一一详细介绍!``` 92 | 93 | ### 4. 线程之间又是怎样进行交互? 94 | 线程的通信就比较简单,有一大块共享的内存,只要大家的指针是同一个就可以看到各自的内存。 95 | ```在后续的文章中我们将一一详细介绍!``` 96 | 97 | ### 5.小结: 98 | 1.进程要分配一大部分的内存,而线程只需要分配一部分栈就可以了. 99 | 2.一个程序至少有一个进程,一个进程至少有一个线程. 100 | 3.进程是资源分配的最小单位,线程是程序执行的最小单位。 101 | 4.一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行. 102 | 103 | ## 文末 104 | 105 | >本章节介绍了进程与线程之间的区别与联系,以及其他方面的小知识点,也是面试过程中会出现的内容点。 106 | 里面涉及到了许多的小知识点,我们并没有扩展开来讲解,会放在今后的文章中做进一步的阐述。 107 | 欢迎关注公众号:**Coder编程** 108 | 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 109 | 110 | [Github个人主页目录](https://github.com/CoderMerlin/coder-programming) 111 | [Gitee个人主页目录](https://gitee.com/573059382/coder-programming) 112 | 113 | **欢迎大家关注并Star~** -------------------------------------------------------------------------------- /src/03-java-base/README.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,欢迎关注微信公众号:Coder编程。内容将持续更新! # 2 | # :mega: 本章导航内容主要——JAVA中的基础知识! # 3 | 4 | ### 集合类知识点 ### 5 | 6 | [通过面试题,让我们来了解Collection](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483669&idx=1&sn=b726d81212af6a857d59cd488c0e0aa2&chksm=96e67006a191f9103b3c790e04d5ed4ddcbc76f3efbb52e6ce8349619f56a68e72aca156571e&token=424171447&lang=zh_CN#rd) 7 | [聊一聊Iterable与Iterator的那些事!](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483690&idx=1&sn=2cebca2e43e329d428263b0b8a801047&chksm=96e67039a191f92fd5fe353f70d7ef4767eb52af752a9a53bc57db61f389a1f0a1b5f218a2f0&token=424171447&lang=zh_CN#rd) 8 | 9 | 10 | ### 线程类知识点 ### 11 | 12 | [一篇让你理解进程与线程的区别与联系](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483676&idx=1&sn=9033af3cb37754446779e1fcd89e3329&chksm=96e6700fa191f91919b4c2a46a8a99a7a7dda037181e97b5377835500e99a5f66cb1d3337898&token=424171447&lang=zh_CN#rd) 13 | 14 | 15 | ### String知识点 ### 16 | 17 | [通过面试题,让我们细聊String的那些知识点(上)](ProcessAndThread.md) 18 | [通过面试题,让我们细聊String的那些知识点(中)](ProcessAndThread.md) 19 | [通过面试题,让我们细聊String的那些知识点(下)](ProcessAndThread.md) -------------------------------------------------------------------------------- /src/03-java-base/String(one).md: -------------------------------------------------------------------------------- 1 | http://www.tiantianbianma.com/ 2 | 3 | ## 前言 4 | >你现在的努力,是为了以后有更多的选择。 5 | 6 | ## 什么是事务? 7 | 8 | 事务(Transaction)是关系型数据库中,它是我们数据库中非常重要的一个概念。是由一组SQL语句组成的一个程序执行单元(unit); 9 | 该执行单元要么成功commit,要么失败rollback。 10 | 11 | **执行单元** 12 | - insert into 13 | - delete 14 | - update 15 | - select 16 | 17 | 18 | ## 事务的ACID特性 19 | 20 | 事务应该具有四个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。 21 | - Atomicity(原子性) 22 | - Consistency(一致性) 23 | - Isolation(隔离性) 24 | - Durability(持久性) 25 | 26 | ### Atomicity(原子性) 27 | **原子性** 是指事务包含的所有操作要么全部成功,要么全部失败回滚。 28 | 比如:我们的`银行转账`,我把钱转给你,银行会把我的钱扣掉,然后把你的钱加上去。不能只做一半,只把我的钱扣掉,你的钱没有加上去。 29 | 30 | ### Consistency(一致性) 31 | **一致性** 是指逻辑上的一致性,即所有操作是符合现实当中的期望的。 32 | 比如:我们的`银行转账`,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。 33 | **ps:** `我们通常说的数据库事务的一致性和分布式系统的一致性并不是一个概念。` 34 | 35 | 36 | ### Isolation(隔离性) 37 | **隔离性** 是指多个并发独立事务,相互独立,相互隔离,互不影响 38 | 我做一个事务,你做一个事务,这中间是相互独立的,不能相互影响。 39 | 40 | ### Durability(持久性) 41 | 42 | 我将事务做完之后,这个结果是能持久下去的。能一直存下去。不管断电还是其他情况。 43 | 我们的关系型数据库都实现了ACID这样的一些事务特性。其中最关键的一点是Isolation。互相不影响。 44 | 45 | 46 | 47 | ## 事务的隔离级别 48 | 49 | 50 | - Read uncommitted 51 | - Read Committed 52 | - Repeatable Reads 53 | 54 | 55 | 56 | Read uncommitted:是隔离级别最低的一种事务级别。在这种隔离级别下,一个事务会读到另一个事务更新后但未提交的数据,如果另一个事务回滚,那么当前事务读到的数据就是脏数据,这就是脏读(Dirty Read)。简单点说:别人事务做到一半还没提交,这时候被我读到的值。 57 | 58 | Read Committed:读到的都是别人提交后的值。这种隔离级别下,一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。不可重复读是指,在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。 59 | 60 | Repeatable Reads:这种隔离级别下,一个事务可能会遇到幻读(Phantom Read)的问题。 61 | 62 | 幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。 63 | 64 | Serializable:是最严格的隔离级别。在Serializable隔离级别下,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。 65 | 66 | 67 | 68 | 以下演示: 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/03-java-base/images/Collection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoderMerlin/coder-programming/75cd46b991771cbea0199c974eb7670acf248fc5/src/03-java-base/images/Collection.png -------------------------------------------------------------------------------- /src/03-java-base/images/Iterable方法.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoderMerlin/coder-programming/75cd46b991771cbea0199c974eb7670acf248fc5/src/03-java-base/images/Iterable方法.png -------------------------------------------------------------------------------- /src/03-java-base/images/Iterator方法.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoderMerlin/coder-programming/75cd46b991771cbea0199c974eb7670acf248fc5/src/03-java-base/images/Iterator方法.png -------------------------------------------------------------------------------- /src/04-java-advanced/AutoboxingAndUnboxing.md: -------------------------------------------------------------------------------- 1 | ![图片](xxxx.png) 2 | 3 | > 每章一点正能量:每当你想要放弃的时候,就想想是为了什么才一路坚持到现在。 4 | 5 | 6 | ## 前言 7 | 8 | 最近在回顾复习Java基础中的一些知识点,发现了一些以前见过但是没有留意却特别有意思的知识特性,比如这次想分享的Java中一个常见的特性:`自动装箱与拆箱`。这个知识点和特性其实在我们开发过程中经常会遇到。同时我们也会去使用一些基本数据类型或者是封装数据类型,但是对于他们之间的一些转换等特性可能不是特别清楚。也可能出现在我们的面试中。本章部分内容从源码中解读一些自动装箱与拆箱的原理,以及会出现的一些陷阱已经性能等。如有错误还请大家及时指出~ 9 | 10 | >本文已同步至 [GitHub](https://github.com/CoderMerlin/coder-programming)/[Gitee](https://gitee.com/573059382/coder-programming)/公众号,感兴趣的同学帮忙点波关注~ 11 | 12 | 问题: 13 | 14 | - 基本数据类型与封装数据类型有哪些区别? 15 | - 什么是装箱?什么是拆箱? 16 | - 装箱和拆箱都是如何实现的? 17 | - 使用时需要注意哪些问题? 18 | 19 | ## 1.基础知识回顾 20 | 21 | 1. Java把内存划分成两种:一种是栈内存,另一种是堆内存。 22 | 23 | 2. int是基本类型,直接存数值;而 Integer是类,产生对象时用一个引用指向这个对象。 24 | 25 | 3. 封装类位于java.lang包中。 26 | 27 | 4. 封装类是引用传递而基本类型是值传递 28 | 29 | 5. 基本类型的变量和对象的引用变量都是在函数的栈内存中分配 ,而实际的对象是在存储堆内存中 30 | 31 | 32 | ### 1.1 基本数据类型和封装类型的区别 33 | 34 | 我们来看下他们之间有哪些区别: 35 | 36 | [图片1]() 37 | 38 | 八种基本数据类型分别是:byte、char、boolean、int、short、float、double、long; 39 | 对应的封装类型分别是:Byte、Character、Boolean、Integer、Short、Float、Double、Long。 40 | 41 | | 基本类型| 封装类型| 字节长度 | 默认值 | 42 | |---|---|---|---| 43 | |boolean|Boolean|1|false| 44 | |byte|Byte|1|0| 45 | |char|Character|2|u0000| 46 | |short|Short|2|0| 47 | |int|integer|4|0| 48 | |long|Long|8|0l或0L| 49 | |float|Float|4|0.0f或0.0F| 50 | |double|Double|8|0.0| 51 | 52 | 53 | --- 54 | 55 | ## 2. "==" 和 "equal()" 方法 56 | 57 | 在巩固了上面的基础知识点之后,我们再来看下另外的一个知识点 `"=="和"equal()" ` 这两个判断符在比较基本数据类型和封装类型的时候会做的一些事情。 58 | 59 | " == ":比较的是基本数据类型,比较的是它们的值 60 | 61 | "equals()": 比较的是引用数据类型,根据不同的数据类型调用不同的equals方法。在特殊情况下可以重写equals方法。 62 | 63 | a==b并不能判断a等于b,而是判断是否为同一个Object。如果我们要判断他们的值怎么做呢?用equal或者Objects.equals()(JDK1.7之后新加 的语法) 64 | 65 | **Objects.equals有什么好处呢?** 66 | 67 | 如果用a.equals(b) 如果a是null 的话,还会抛出空指针异常。但是用Objects.equals就没有问题。因此我们在使用引用类型的时候需要注意,当我们在赋值的时候,两个变量都是引用同一个Object。 68 | 69 | 70 | 71 | 我们以 `int与Integer` 作为例子,看下"=="和"equal()"方法: 72 | 73 | 1)基本型和封装类型进行"=="运算符的比较,封装类型将会自动拆箱变为基本型后再进行比较,因此Integer(0)会自动拆箱为int类型再进行比较。 74 | 75 | 2)两个Integer类型进行"=="比较,如果其值在-128至127,那么返回true,否则返回false, 这跟Integer.valueOf()的缓冲对象有关,后面会说。 76 | 77 | 3)两个封装类型进行equals()比较,首先equals()会比较类型,如果类型相同,则继续比较值,如果值也相同,返回true。 78 | 79 | 4)基本型封装类型调用equals(),但是参数是基本类型,这时候,先会进行自动装箱,基本型转换为其封装类型,再进行3中的比较。 80 | 81 | 82 | 83 | ## 3.什么是装箱和拆箱 84 | 85 | 基本数据(Primitive)类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。Java语言规范中说道:在许多情况下包装与解包装是由编译器自行完成的(在这种情况下包装称为装箱,解包装称为拆箱)。 86 | 通俗的理解:装箱:基本类型转换成封装类型, 拆箱:封装类型转换成基本类型 这么一个过程。在上面有介绍八种基本类型和对应的封装类型。下面以Integer与int之间的转换作为理解: 87 | 88 | ### 3.1 自动装箱(Autoboxing) 89 | 90 | ``` 91 | 92 | Integer a = 2; //Boxing 93 | 94 | ``` 95 | 96 | 简单的理解:将2装在一个箱子里,这个箱子的类型是Integer 。箱子这里面装的数值就是2,我们就完成了一次装箱操作。并把a指向2这个箱子。 97 | 98 | ``` 99 | 100 | Integer b = new Integer(2);//Boxing 101 | 102 | ``` 103 | 104 | 显示装箱。生成一个新的箱子 new Integer(); 并且这个箱子的值为2.而且让b指向这个箱子。 105 | 106 | 107 | ### 3.2 拆箱(Unboxing) 108 | 109 | 故名思议就是将对象重新转化为基本数据类型 110 | 111 | ``` 112 | 113 | int v = a.intValue(); //Unboxing 114 | 115 | ``` 116 | 117 | 简单的理解:将里面int的值取出来。拆箱有个很典型的用法就是在进行运算的时候:因为对象时不能直接进行运算的,而是要转化为基本数据类型后才能进行加减乘除。 118 | 119 | 例如: 120 | 121 | ``` 122 | 123 | Integer c = 5; 124 | System.out.print(c--);//进行计算时隐含的有自动拆箱 125 | 126 | ``` 127 | 128 | 129 | ## 4. 装箱拆箱结合源码分析 130 | 131 | 通过第四点我们知道装箱拆箱的基本概念知识,下面我们同样以Integer 为例,进入源码里面看看里面的乾坤。 132 | 133 | 我们首先看下Integer的大小。 134 | 135 | ### 4.1 Integer 大小 136 | 137 | 138 | 可以看出,其定义了Integer的最大值为2^31-1,最小值为-2^31。Integer的基本数据类型为int。 139 | 140 | ![Integer大小]() 141 | 142 | ### 4.2 Integer中的valueOf()方法 143 | 144 | 再来看看Integer中的valueOf()方法。 145 | 146 | ![Integer中的valueOf()]() 147 | 148 | 可以看出valueOf()方法是个静态方法。当传进来的变量值在一个区间之内,直接用IntegerCache.cache[]数组里面的数返回,否则new一个新对象。 149 | 150 | 接着我们来看看IntegerCache类。其实也是会出现坑的一个地方。 151 | 152 | ### 4.3 其中存在的陷阱 153 | 154 | 接着来说下Integer这儿的一个坑,也是比较有意思的地方。 155 | 156 | 157 | ![IntegerCache]() 158 | 159 | 初始化Integer后,IntegerCache会缓存[-128,127]之间的数据,这个区间的上限可以配置,取决于java.lang.Integer.IntegerCache.high这个属性,这个属性在VM参数里为-XX:AutoBoxCacheMax=2000进行设置调整或者VM里设置-Djava.lang.Integer.IntegerCache.high=2000。所以Integer在初始化完成后会缓存[-128,max]之间的数据。cache属于常量,存放在java的方法区中。 160 | 161 | 同样,在Long,Byte,Short,我们也可以看到缓存,其缓存数据长度均是-128到127。这里不做展开。 162 | 163 | **另外其他陷阱:** 164 | 如: 165 | 166 | ``` 167 | 168 | System.out.println(Integer.valueOf(null)); 169 | 170 | ``` 171 | 172 | Integer对象的值可以为null,所以编译器检查时不会出现检查时异常,但是在转换成int的时候就会抛出空指针异常。 173 | 174 | ## 4. 例题分析 175 | 176 | 我们通过几个经典的问题,来看看大家到底理解了`装箱与拆箱`的知识点没。 177 | 178 | - new Integer(5) == 5? 179 | - new Integer(5) == new Integer(5) ? 180 | - Integer.valueOf(5) == Integer.valueOf(5)? 181 | - Integer.valueOf(5).intValue() == 5? 182 | - new Integer(5).equals(new Integer(5))? 183 | 184 | ### 4.1 问题一:new Integer(5) == 5? 185 | 186 | **答案:true。** 等号的左边是一个Object右边是一个数值,Object和数值怎么会相等的呢?Java的编译器很聪明,它会自己去做装箱和拆箱的操作。这边它将new Integer(5)做的是Unboxing,它会里面的value取出来,这时候发现取出来的5等于右边,所以就为true。 187 | 188 | 189 | ### 4.2 问题二:new Integer(5) == new Integer(5) ? 190 | 191 | **答案:false。** new Integer(5) 就是新建一个箱子,这个箱子的值就是5。 == 是判断这两个箱子是不是同一个箱子,不是说里面的值是不是一样.所以是false。因为他们不是同一个箱子。 192 | 193 | 194 | 195 | ### 4.3 问题三:Integer.valueOf(5) == Integer.valueOf(5)? 196 | 197 | 198 | **答案: true。** Integer.valueOf(5)它会返回一个箱子给我们,箱子里面的值是5。但是在返回这个箱子给我们的时候,可能会新建一个新的箱子给我们,也可能会使用现有的一个箱子给我们。所以Integer.valueOf(5) == Integer.valueOf(5)。什么情况下才会相等呢?只有当系统已经将2这个箱子建立好了,并且缓存起来的情况下。会把箱子的引用同时发给等号的左边与右边。这样的情况,他们才会互相相等。Integer.valueOf() 是系统给我们分配的一个箱子,我们发现,每次调我们的箱子时候,系统都给了同一个箱子。这个我们的 Integer.valueOf(5) == Integer.valueOf(5) 199 | 200 | **但是:** 可能为false。我们在上面介绍过,在low和high之间,它会返回一个系统已经生产的cache,否则它会生产一个新的出来。看源码可以看到low = -128 high = 127。所以当它的值超过了区间后,它就会返回新的箱子,所以就会为false。 201 | 202 | 我们不用5改用200试一试。 203 | 204 | ``` 205 | Integer.valueOf(200) == Integer.valueOf(200) 206 | 207 | ``` 208 | 209 | **答案:false。** 说明系统对小的数字会使用系统分配的箱子,对于大的数字,系统会重新new一个箱子。面试的时候,可以回答,他们可能相等,也可能不相等。是有系统决定的。 210 | 211 | ### 4.4 问题四:Integer.valueOf(5).intValue() == 5? 212 | 213 | **答案: true。** intValue()做了一个拆箱的操作,将里面的值5取出来,值5等于5,所以是true。 214 | 215 | 216 | ### 4.5 问题五:new Integer(5).equals(new Integer(5))? 217 | 218 | **答案:true。** 这里我们没有用==而是用equals,equals判断相等是判断里面的值是不是相等,而不是判断这个箱子是不是同一个,所以我们的答案是true。我们来看看equals的源码。判断里面的值是不是相等。 219 | 220 | ![equals]() 221 | 222 | 223 | ### 打印结果: 224 | 225 | ![打印结果]() 226 | 227 | ## 文末 228 | 229 | 本章节主要简单介绍了`自动装箱与拆箱`的相关知识,希望对大家有所帮助~ 230 | 今后我会在每张文章开头增加 **每章一点正能量** ,文末增加5个编程相关的**英语单词** 学点英语。希望大家和我一样每天都能积极向上,一起学习一同进步! 231 | 232 | ### 学点英语 233 | 234 | - AWT(Abstract Window Toolkit)抽象窗口工具 235 | - API(Application Programming Interface)应用程序接口 236 | - AOP Aspect Oriented Programming(面向切面编程),可以 通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一 添加功能的一种技术。 237 | - BMP Bean-Managed Persistent(Bean管理的持久性),EJB中由 Bean自己负责持久性管理的方法,Bean的内容的同步(保存)需要自己编写代码 实现。 238 | - I18N internationalization(国际化),这个单词的长度是20,然后取 其首尾字母,中间省略的字母刚好18个。 239 | 240 | 241 | >欢迎关注公众号:**Coder编程** 242 | 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 243 | 244 | 参考文章: 245 | 246 | https://blog.csdn.net/u013309870/article/details/70229983 247 | 248 | https://blog.csdn.net/jairuschan/article/details/7513045 249 | 250 | https://www.cnblogs.com/dolphin0520/p/3780005.html 251 | 252 | 253 | 254 | 255 | ![微信公众号](https://user-gold-cdn.xitu.io/2019/4/16/16a26835c75c12fc?w=300&h=390&f=png&s=18217) 256 | 257 | ## 推荐阅读 258 | 259 | [一篇带你读懂TCP之“滑动窗口”协议 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483706&idx=1&sn=8eed9d160013bd8ed6203ad511711553&chksm=96e67029a191f93fdd1543af2bf06025397d9c3bd0f0692c7fe247ab9c139cd869d69ab05498&token=1104592742&lang=zh_CN#rd) 260 | 261 | [带你了解数据库中JOIN的用法 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483713&idx=1&sn=d61ad0aed42dc36d64d17732db352288&chksm=96e67052a191f9445bbe3d5825ce547ad3171c3874b571a93b97977d0668413e37a164c3e0bc&token=1144933717&lang=zh_CN#rd) 262 | 263 | [ 带你了解数据库中group by的用法 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483717&idx=1&sn=157a8a021c29043a10480d0294b39ca0&chksm=96e67056a191f940668812ebb092fe9984b22eb619a18339cc052e1051c659a7e9d907c48814&token=1144933717&lang=zh_CN#rd) -------------------------------------------------------------------------------- /src/04-java-advanced/README.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,欢迎关注微信公众号:Coder编程。内容将持续更新! # 2 | # :mega: 本章导航内容主要——JAVA中的进阶知识! # 3 | 4 | -------------------------------------------------------------------------------- /src/05-java-becoming-god/README.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,内容正在努力加载中...... # 2 | 3 | 4 | ## JVM教程与性能优化 5 | 6 | 1.[[JVM教程与调优] 为什么要学习JVM虚拟机?](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484247&idx=1&sn=e41732e54d5b57534d312dc9f15f47f0&chksm=96e67244a191fb5287c35c278cff4810939bd70a76cd4190fe7636d06b3ccb07f6aca974ed62&token=89408735&lang=zh_CN#rd) 7 | 8 | 9 | 2.[[JVM教程与调优] JVM都有哪些参数类型?](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484247&idx=2&sn=a1f732611bab89f0db84d3ab162e8763&chksm=96e67244a191fb52a4d6b292112cc94c412d3c90a688bc6da533dec8cfe9cfa693af65b16dd5&token=89408735&lang=zh_CN#rd) 10 | 11 | 12 | 3.[[JVM教程与调优] 什么是JVM运行时参数?](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484251&idx=1&sn=e174ffdc8730676ca729386d4f35757d&chksm=96e67248a191fb5e663339d8aadf8daa312ff06a6c473007b24ff456734247d3740ea56186a2&token=1619516654&lang=zh_CN#rd) 13 | -------------------------------------------------------------------------------- /src/06-data-base/PessimisticLockAndOptimisticLock.md: -------------------------------------------------------------------------------- 1 | 2 | ![悲观锁与乐观锁](https://user-gold-cdn.xitu.io/2019/4/16/16a26835c73ea038?w=900&h=383&f=png&s=178029) 3 | ## 前言 4 | 在上一个章节[5分钟带你读懂事务隔离性与隔离级别 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483728&idx=1&sn=cdc5dc4708e48051e56b8e7d2a9fe5a8&chksm=96e67043a191f955b93e7228b88572beb486e6fac3308a1b69f5ee83c9e9ced6957e30b12d58&token=1787261466&lang=zh_CN#rd)的最后,其实我们已经提到了锁的概念。本章节接下来将主要介绍以下数据库`悲观锁与乐观锁`的相关知识。如有错误还请大家及时指出~ 5 | 6 | >本文已同步至 [GitHub](https://github.com/CoderMerlin/coder-programming)/[Gitee](https://gitee.com/573059382/coder-programming)/公众号,感兴趣的同学帮忙点波关注~ 7 | 8 | 问题: 9 | 10 | - 为什么需要锁? 11 | - 什么是悲观锁? 12 | - 什么是乐观锁? 13 | - 悲观锁与乐观锁区别与联系? 14 | - 悲观锁与乐观锁的使用场景? 15 | 16 | 17 | ## 为什么需要锁? 18 | 在并发环境下,如果多个客户端访问同一条数据,此时就会产生数据不一致的问题,如何解决,通过加锁的机制,常见的有两种锁,乐观锁和悲观锁,可以在一定程度上解决并发访问。 19 | 20 | 21 | 22 | ## 1. 悲观锁(Pessimistic Lock) 23 | 24 | ### 1.1 定义 25 | 26 | **百度百科:** 27 | 28 | 悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。 29 | 30 | **其他知识点** 31 | 32 | 悲观锁主要是`共享锁`或`排他锁` 33 | `共享锁`又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。 34 | `排他锁`又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。 35 | 36 | 37 | ### 1.2 案例分析 38 | 39 | >使用场景举例:以MySQL InnoDB为例 40 | 41 | 作为演示,我们继续使用之前的数据库表:product表 42 | 43 | |productId | productName| productPrice| productCount| 44 | |---|---|---|---| 45 | |1 | 小米 |1999|100| 46 | |2| 魅族 |1999|100| 47 | 48 | 首先我们需要`set autocommit=0`,即不允许自动提交 49 | 50 | 有看过上一篇文章[5分钟带你读懂事务隔离性与隔离级别 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483728&idx=1&sn=cdc5dc4708e48051e56b8e7d2a9fe5a8&chksm=96e67043a191f955b93e7228b88572beb486e6fac3308a1b69f5ee83c9e9ced6957e30b12d58&token=1787261466&lang=zh_CN#rd)的同学,可以看到最后我们使用事务隔离级别时,所引申出来的根本问题就是可以通过锁机制解决。 51 | 52 | #### 问题 53 | 在并发情况下回导致数据一致性的问题: 54 | 如果有A、B两个用户需要抢productId =1的小米手机,A、B用户都查询小米手机数量是100,A购买后修改商品的数量为99,B购买后修改数量为99。 55 | 56 | #### 用法 57 | 每次获取小米手机时,对该商品加排他锁。也就是在用户A获取获取 id=1 的小米手机信息时对该行记录加锁,期间其他用户阻塞等待访问该记录。代码如下: 58 | 59 | ``` 60 | start transaction; 61 | 62 | select p.productCount from product p where p.productId = 1 for update; 63 | 64 | update product p set p.productCount=p.productCount-1 where p.productId=1 ; 65 | 66 | commit; 67 | 68 | ``` 69 | 70 | 71 | **操作** 72 | 73 | 下面同时打开两个窗口模拟2个用户并发访问数据库 74 | 75 | |时间轴| 事务A| 事务B| 76 | |---|---|---| 77 | |T1 | start transaction;| | 78 | |T2| select p.productCount from product p where p.productId = 1 for update;| | 79 | |T3| | start transaction; | 80 | |T4| | select p.productCount from product p where p.productId = 1 for update;(等待中...) | 81 | 82 | **流程说明** 83 | 84 | 1. 用户A start transaction开启一个事物。前一步我们关闭了mysql的autocommit,所以需要手动控制事务的提交。 85 | 2. 在获得小米手机信息(productId = 1 )时,进行数据加锁操作(for update)。与普通查询方式不同,我们使用了`select…for update`的方式,这样就通过数据库实现了悲观锁。在这个update事务提交之前其他外界是不能修改这条数据的,但是这种处理方式效率比较低,一般不推荐使用。 86 | 3. 用户B start transaction开启一个事物。 87 | 4. 用户B 也进行查询操作,此时处于等待中(阻塞状态)。ps:需要等待用户A事务提交后,才会执行。 88 | 89 | 90 | > 注意:在事务中,只有select…for update(排他锁) 或lock in share mode(共享锁) 操作同一个数据时才会等待其它事务结束后才执行,一般select... 则不受此影响。例如在 T3中执行select p.productCount from product p where p.productId = 1;则能正常查询出数据,不会受第一个事务的影响。 91 | 92 | 93 | ## 2. 乐观锁(Optimistic Lock) 94 | 95 | ### 2.1 定义 96 | 97 | **百度百科:** 98 | 99 | 乐观锁机制采取了更加宽松的加锁机制。乐观锁是相对悲观锁而言,也是为了避免数据库幻读、业务处理时间过长等原因引起数据处理错误的一种机制,但乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性。 100 | 101 | 102 | **其他知识点** 103 | 104 | 实现乐观锁一般来说有以下2种方式: 105 | 106 | - 使用版本号 107 | 使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。 108 | 109 | - 使用时间戳 110 | 乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。 111 | 112 | 113 | ### 2.2 案例分析 114 | 115 | >使用场景举例:以MySQL InnoDB为例 116 | 117 | 作为演示,我们继续使用之前的数据库表:product表 118 | 119 | |productId | productName| productPrice| productCount| version| 120 | |---|---|---|---|---| 121 | |1 | 小米 |1999|100|1| 122 | |2| 魅族 |1999|100|2| 123 | 124 | 我们以`版本号`实现的方式进行说明。 125 | 126 | **操作** 127 | 128 | 查询当前事务隔离级别: 129 | 130 | ``` 131 | 132 | SELECT @@tx_isolation; 133 | 134 | 135 | 结果: 136 | REPEATABLE-READ 137 | 138 | ``` 139 | 140 | 141 | 下面同时打开两个窗口模拟2个用户并发访问数据库 142 | 143 | **第一种测试** 144 | 145 | |时间轴| 用户A| 用户B| 146 | |---|---|---| 147 | |T1 | start transaction;| | 148 | |T2| select * from product p where p.productId = 1;(productCount=100)| | 149 | |T3|update product p set p.productCount = 99,version=version+1 where p.productId = 1 and version = 1;(受影响的行: 1) | | 150 | |T4| | start transaction; | 151 | |T5| | select * from product p where p.productId = 1;(productCount=100) | 152 | |T6| | update product p set p.productCount = 99,version=version+1 where p.productId = 1 and version = 1;(等待中...) | 153 | |T7|commit; | | 154 | |T8| | T6执行(受影响的行: 0) | 155 | |T9| |commit; | 156 | 157 | 158 | **流程说明** 159 | 1. 事务A开启事务。 160 | 2. 事务A查询当前小米手机数量为100。 161 | 3. 事务A购买小米手机,小米手机数量更新为99。(此时并未提交事务)。 162 | 4. 事务B开启事务。 163 | 5. 事务B查询当前小米手机数量为100。 164 | 6. 事务B购买小米手机,小米手机数量更新为99。注意:此时处于阻塞状态。 165 | 7. 事务A提交事务。 166 | 8. 此时第六步执行完毕,但并未成功(受影响的行: 0)。 167 | 9. 事务B提交事务。 168 | 169 | **第二种测试** 170 | 171 | |时间轴| 用户A| 用户B| 172 | |---|---|---| 173 | |T1 | select * from product p where p.productId = 1;(productCount=100)| | 174 | |T2| update product p set p.productCount = 99,version=version+1 where p.productId = 1 and version = 1;(受影响的行: 1)| | 175 | |T3| | select * from product p where p.productId = 1;(productCount=100) | 176 | |T4| | update product p set p.productCount = 99,version=version+1 where p.productId = 1 and version = 1;(受影响的行: 0) | 177 | 178 | **乐观锁小结** 179 | 180 | - 用户B修改数据的时候,受影响行数为0,对业务来说,及更新失败。这时候我们只需要告诉用户购买失败,重新查询一遍即可。 181 | - 对比第一种和第二种测试,我们会发现第一种测试,将update语句放入事务中会出现阻塞的情况,而第二种测试不会出现阻塞情况。这是为什么呢?update其实在不在事务中都无所谓,在内部是这样的:update是单线程的,及如果一个线程对一条数据进行update操作,会获得锁,其他线程如果要对同一条数据操作会阻塞,直到这个线程update成功后释放锁。 182 | 183 | > 乐观锁不需要数据库底层的支持! 184 | 185 | 186 | 187 | ## 3. 适用场景 188 | 189 | **悲观锁** 190 | 191 | 比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量。 192 | 193 | **乐观锁** 194 | 195 | 比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。 196 | 197 | 198 | 199 | ## 文末 200 | 201 | 本章节主要简单介绍了数据库中`乐观锁与悲观锁`的相关知识,后续我们将会继续介绍数据库中的其他锁以及相关知识。例如行锁、表锁、死锁、 202 | 203 | >欢迎关注公众号:**Coder编程** 204 | 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 205 | 206 | 参考文章: 207 | 208 | https://chenzhou123520.iteye.com/blog/1860954 209 | 210 | https://chenzhou123520.iteye.com/blog/1863407 211 | 212 | 213 | ![微信公众号](https://user-gold-cdn.xitu.io/2019/4/16/16a26835c75c12fc?w=300&h=390&f=png&s=18217) 214 | 215 | ## 推荐阅读 216 | 217 | [带你了解数据库中JOIN的用法 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483713&idx=1&sn=d61ad0aed42dc36d64d17732db352288&chksm=96e67052a191f9445bbe3d5825ce547ad3171c3874b571a93b97977d0668413e37a164c3e0bc&token=1144933717&lang=zh_CN#rd) 218 | 219 | [ 带你了解数据库中事务的ACID特性 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483722&idx=1&sn=e8bc8bd82c559e0cfe7f35cf46100af3&chksm=96e67059a191f94fe8948e5b5e4ef177b77fa7707d86d945b153f67e7f2e76b83ed0c768ef27&token=128531458&lang=zh_CN#rd) 220 | 221 | [5分钟带你读懂事务隔离性与隔离级别 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483728&idx=1&sn=cdc5dc4708e48051e56b8e7d2a9fe5a8&chksm=96e67043a191f955b93e7228b88572beb486e6fac3308a1b69f5ee83c9e9ced6957e30b12d58&token=1787261466&lang=zh_CN#rd) -------------------------------------------------------------------------------- /src/06-data-base/README.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,欢迎关注微信公众号:Coder编程。内容将持续更新! 2 | # :mega: 本章导航内容: 关系型数据库 3 | 4 | 5 | [带你了解数据库中JOIN的用法](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483713&idx=1&sn=d61ad0aed42dc36d64d17732db352288&chksm=96e67052a191f9445bbe3d5825ce547ad3171c3874b571a93b97977d0668413e37a164c3e0bc&token=1144933717&lang=zh_CN#rd) 6 | 7 | [带你了解数据库中group by的用法](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483717&idx=1&sn=157a8a021c29043a10480d0294b39ca0&chksm=96e67056a191f940668812ebb092fe9984b22eb619a18339cc052e1051c659a7e9d907c48814&token=1144933717&lang=zh_CN#rd) 8 | 9 | [带你了解数据库中事务的ACID特性 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483722&idx=1&sn=e8bc8bd82c559e0cfe7f35cf46100af3&chksm=96e67059a191f94fe8948e5b5e4ef177b77fa7707d86d945b153f67e7f2e76b83ed0c768ef27&token=1701970188&lang=zh_CN#rd) 10 | 11 | 12 | ## MySQL教程 13 | 14 | * [[Mysql教程系列]介绍一下MySQL命名规范](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484230&idx=1&sn=84c2a178df29f5a0a9f7c1934ec01348&chksm=96e67255a191fb43060b0703e7669c7163904181dda73cba234e22a748814465e6f95c8759a0&token=1118603774&lang=zh_CN#rd) 15 | 16 | * [[Mysql教程系列]介绍一下MySQL表设计规范](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484234&idx=1&sn=423c48aa548debfa3b498d19f6ac2e73&chksm=96e67259a191fb4fcd13704a98d2909a178b1eaab7ef9a9aa050dfce9ae9e5b41041d91358ae&token=1053177998&lang=zh_CN#rd) 17 | 18 | * [[Mysql教程系列]介绍一下MySQL索引设计规范](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484238&idx=1&sn=65b83b89437596c4767c57fb660b817a&chksm=96e6725da191fb4bed9b832847ec91f46785440d305d0c2e2af682b4172b0bb3ad816883f893&token=1882537801&lang=zh_CN#rd) -------------------------------------------------------------------------------- /src/06-data-base/acid.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | 前面我们介绍过数据库中 [带你了解数据库中JOIN的用法 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483713&idx=1&sn=d61ad0aed42dc36d64d17732db352288&chksm=96e67052a191f9445bbe3d5825ce547ad3171c3874b571a93b97977d0668413e37a164c3e0bc&token=1144933717&lang=zh_CN#rd) 与 [ 带你了解数据库中group by的用法 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483717&idx=1&sn=157a8a021c29043a10480d0294b39ca0&chksm=96e67056a191f940668812ebb092fe9984b22eb619a18339cc052e1051c659a7e9d907c48814&token=1144933717&lang=zh_CN#rd)的相关用法。本章节主要来介绍下数据库中一个非常重要的知识点`事务`,也是我们项目中或面试中经常会遇到的知识点。如有错误还请大家及时指出~ 4 | 5 | 涉及面试题: 6 | - 请讲下你对事务的理解? 7 | - 讲下事务有那几大特性? 8 | 9 | 10 | >以下都是采用mysql数据库 11 | 12 | ## 事务 13 | 14 | 事务(Transaction),是我们关系型数据库中非常重要的一个概念,它要符合ACID特性。是由一组SQL语句组成的一个程序执行单元(Unit),该执行单元要么成功Commit,要么失败Rollback。 15 | 16 | ![事务](https://img-blog.csdnimg.cn/20190409223141756.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 17 | ## ACID特性 18 | 19 | - Atomicity 20 | - Consistency 21 | - Isolation 22 | - Durability 23 | 24 | 25 | ### Atomicity(原子性) 26 | `原子性`:指事务是一个不可再分割的工作单元,事务中的操作要么都发生,要么都不发生。 27 | 通俗的说:我们有一堆的事情,它要么全做,要么全都不做,不能只做一半。比如我们的银行转账。我把钱转给你,把我的钱扣掉,然后把你的钱加上去。不能只做一半,只把我的钱扣掉,你的钱没有加上去。 28 | 29 | ![原子性](https://img-blog.csdnimg.cn/20190409223612544.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 30 | ### Consistency(一致性) 31 | 32 | `一致性`指事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。 33 | 通俗的说:我和你的钱加起来一共是2000,那么不管我和你之间如何转账,转几次账,事务结束后我们的钱相加起来应该还得是2000,这就是事务的一致性。 34 | ![一致性](https://img-blog.csdnimg.cn/20190409223903482.png) 35 | 36 | 37 | ### Isolation(隔离性) 38 | 39 | `隔离性`指多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。 40 | 通俗的说:多个用户并发访问操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。 41 | 42 | 数据库定义了4个隔离的级别: 43 | 44 | - READ_UNCOMMITTED 45 | - READ_COMMITTED 46 | - REPEATABLE_READ 47 | - SERIALIZABLE 48 | 49 | >下篇文章我们将专门介绍事务的隔离性 50 | 51 | 52 | ### Durability(持久性) 53 | 54 | `持久性`指事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。 55 | 通俗的说:比如我将事务做完之后,这个结果是能持久下去的并能一直存下去。不管断电还是其他情况。 56 | ![持久性](https://img-blog.csdnimg.cn/20190409224425827.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 57 | 58 | >关系型数据库都实现了ACID这样的一些事务特性。其中最关键的一点是Isolation(隔离性),互相不影响。 59 | 60 | 61 | ## 文末 62 | 63 | >本章节主要简单介绍了数据库中事务的ADID特性,下一章我们将详细介绍事务`隔离`。 64 | 欢迎关注公众号:**Coder编程** 65 | 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 66 | 67 | ![微信公众号](https://img-blog.csdnimg.cn/20190407231851378.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 68 | ## 推荐阅读 69 | [带你了解数据库中JOIN的用法 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483713&idx=1&sn=d61ad0aed42dc36d64d17732db352288&chksm=96e67052a191f9445bbe3d5825ce547ad3171c3874b571a93b97977d0668413e37a164c3e0bc&token=1144933717&lang=zh_CN#rd) 70 | 71 | 72 | [ 带你了解数据库中group by的用法 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483717&idx=1&sn=157a8a021c29043a10480d0294b39ca0&chksm=96e67056a191f940668812ebb092fe9984b22eb619a18339cc052e1051c659a7e9d907c48814&token=1144933717&lang=zh_CN#rd) 73 | 74 | 75 | 76 | [一篇带你读懂TCP之“滑动窗口”协议 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483706&idx=1&sn=8eed9d160013bd8ed6203ad511711553&chksm=96e67029a191f93fdd1543af2bf06025397d9c3bd0f0692c7fe247ab9c139cd869d69ab05498&token=1104592742&lang=zh_CN#rd) 77 | 78 | 79 | 80 | 81 | [Github个人主页目录](https://github.com/CoderMerlin/coder-programming) 82 | 83 | [Gitee个人主页目录](https://gitee.com/573059382/coder-programming) 84 | 85 | **欢迎大家关注并Star~** 86 | -------------------------------------------------------------------------------- /src/06-data-base/groupby.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 前言 4 | 5 | 本章主要介绍数据库中`group by`的用法,也是我们在使用数据库时非常基础的一个知识点。并且也会涉及Join的使用,关于Join的用法,可以看我写的上一篇文章:[带你了解数据库中JOIN的用法 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483713&idx=1&sn=d61ad0aed42dc36d64d17732db352288&chksm=96e67052a191f9445bbe3d5825ce547ad3171c3874b571a93b97977d0668413e37a164c3e0bc&token=1144933717&lang=zh_CN#rd)如有错误还请大家及时指出~ 6 | 7 | >以下都是采用mysql数据库 8 | 9 | ## Group By 10 | ### 概念 11 | Group By语句从英文的字面意义上理解就是“根据(by)一定的规则进行分组(Group)”。 12 | 作用:通过一定的规则将一个数据集划分成若干个小的区域,然后针对若干个小区域进行数据处理。 13 | >注意:group by 是先排序后分组! 14 | 15 | ### 语法 16 | ``` 17 | SELECT expression1, expression2, ... expression_n, 18 | aggregate_function (aggregate_expression) 19 | FROM tables 20 | [WHERE conditions] 21 | GROUP BY expression1, expression2, ... expression_n 22 | [ORDER BY expression [ ASC | DESC ]]; 23 | ``` 24 | #### 语法说明 25 | `expression1,expression2,... expression_n` 26 | 表达式未封装在聚合函数中,必须包含在SQL语句末尾的GROUP BY子句中。 27 | 28 | `aggregate_function` 29 | 这是一个聚合函数,例如SUM,COUNT,MIN,MAX或AVG函数。 30 | 31 | `aggregate_expression` 32 | 这是将使用aggregate_function的列或表达式。 33 | 34 | `tables` 35 | 您希望从中检索记录的表。FROM子句中必须至少列出一个表。 36 | 37 | `where` 38 | 可选的。这些是要选择的记录必须满足的条件。 39 | 40 | `ORDER BY`表达式 41 | 可选的。用于对结果集中的记录进行排序的表达式。如果提供了多个表达式,则值应以逗号分隔。 42 | 43 | `ASC` 44 | 可选的。ASC按表达式按升序对结果集进行排序。如果没有修饰符是提供者,则这是默认行为。 45 | 46 | `DESC` 47 | 可选的。DESC按表达式按降序对结果集进行排序。 48 | 49 | ### 常用聚合函数 50 | - count() 计数 51 | - sum() 求和 52 | - avg() 平均数 53 | - max() 最大值 54 | - min() 最小值 55 | 56 | 57 | 58 | ### 举例 59 | 我们以下面两张表举例: 60 | 61 | **学生表(Student)** 62 | |ID | StudentName | StudentClassID| 63 | |---|---|---| 64 | |1 | 小明 |1| 65 | |2 | 小红|2| 66 | |3 | 小兰 | 3| 67 | |4 | 小吕|2| 68 | |5 | 小梓|1| 69 | 70 | **班级表(Class)** 71 | |ClassID| ClassName| 72 | |---|---| 73 | |1 | 软件一班| 74 | |2 | 软件二班| 75 | |3 | 软件三班| 76 | 77 | 78 | - 计算每个班都有多少学生? 79 | 80 | > SELECT cl.ClassName,COUNT(stu.StudentClassID) AS studentNum 81 | FROM student stu LEFT JOIN class cl ON stu.StudentClassID = cl.ClassID 82 | GROUP BY cl.ClassName; 83 | 84 | --- 85 | 86 | **计算结果:** 87 | |ClassName| StudentName | 88 | |---|---| 89 | |软件一班| 2| 90 | |软件二班| 2| 91 | |软件三班| 1| 92 | 93 | 94 | 95 | 96 | ## HAVING 97 | - HAVING语句通常与GROUP BY语句联合使用,用来过滤由GROUP BY语句返回的记录集。 98 | - HAVING语句的存在弥补了WHERE关键字不能与聚合函数联合使用的不足。 99 | 通俗的说:WHERE过滤行,HAVING过滤组 100 | 101 | ### 语法 102 | ``` 103 | SELECT expression1, expression2, ... expression_n, 104 | aggregate_function (aggregate_expression) 105 | FROM tables 106 | [WHERE conditions] 107 | GROUP BY expression1, expression2, ... expression_n 108 | ``` 109 | ### 语法说明 110 | `HAVING condition` 111 | 这是另一个条件,仅应用于聚合结果以限制返回行的组。只有那些条件评估为TRUE的组才会包含在结果集中。 112 | 113 | ### 举例 114 | `采用上面两张数据表` 115 | 116 | - 查询学生人数大于2人的班级? 117 | 118 | >SELECT cl.ClassName,COUNT(stu.StudentClassID) AS studentNum 119 | FROM student stu LEFT JOIN class cl ON stu.StudentClassID = cl.ClassID 120 | GROUP BY cl.ClassName 121 | HAVING COUNT(stu.StudentClassID)>=2; 122 | 123 | --- 124 | 125 | **计算结果:** 126 | |ClassName| StudentName | 127 | |---|---| 128 | |软件一班| 2| 129 | |软件二班| 2| 130 | ## 小结 131 | - 当group by 与聚合函数配合使用时,功能为分组后计算 132 | - 当group by 与 having配合使用时,功能为分组后过滤,获得满足条件的分组的返回结果。 133 | - having与where区别:where过滤行,having过滤组 134 | 135 | --- 136 | ## 文末 137 | 138 | >本章节主要简单介绍了数据库中group by的用法,并没有详细的展开讲解,相信大家只要把基础打扎实,再复杂的查询也可以通过`分而治之`的思想来解决。 139 | 欢迎关注公众号:**Coder编程** 140 | 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 141 | 142 | ![微信公众号](https://img-blog.csdnimg.cn/20190407231851378.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 143 | ## 推荐阅读 144 | [带你了解数据库中JOIN的用法 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483713&idx=1&sn=d61ad0aed42dc36d64d17732db352288&chksm=96e67052a191f9445bbe3d5825ce547ad3171c3874b571a93b97977d0668413e37a164c3e0bc&token=1144933717&lang=zh_CN#rd) 145 | 146 | 147 | [一篇让你理解进程与线程的区别与联系](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483676&idx=1&sn=9033af3cb37754446779e1fcd89e3329&chksm=96e6700fa191f91919b4c2a46a8a99a7a7dda037181e97b5377835500e99a5f66cb1d3337898&token=424171447&lang=zh_CN#rd) 148 | 149 | 150 | [一篇带你读懂TCP之“滑动窗口”协议 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483706&idx=1&sn=8eed9d160013bd8ed6203ad511711553&chksm=96e67029a191f93fdd1543af2bf06025397d9c3bd0f0692c7fe247ab9c139cd869d69ab05498&token=1104592742&lang=zh_CN#rd) 151 | 152 | 153 | 参考文章: 154 | 155 | https://www.techonthenet.com/sql/having.php 156 | 157 | https://www.techonthenet.com/sql/group_by.php 158 | 159 | https://www.cnblogs.com/8335IT/p/5850531.html 160 | 161 | 162 | [Github个人主页目录](https://github.com/CoderMerlin/coder-programming) 163 | 164 | [Gitee个人主页目录](https://gitee.com/573059382/coder-programming) 165 | 166 | **欢迎大家关注并Star~** 167 | -------------------------------------------------------------------------------- /src/06-data-base/isolation.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | 我们在上一章节中介绍过数据库的[带你了解数据库中事务的ACID特性 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483722&idx=1&sn=e8bc8bd82c559e0cfe7f35cf46100af3&chksm=96e67059a191f94fe8948e5b5e4ef177b77fa7707d86d945b153f67e7f2e76b83ed0c768ef27&token=1701970188&lang=zh_CN#rd)的相关用法。本章节主要来介绍下数据库中一个非常重要的知识点`事务的隔离级别`。如有错误还请大家及时指出~ 4 | 5 | 问题: 6 | - 事务的隔离级别有哪些? 7 | - 如果并发事务没有进行隔离,会出现什么问题? 8 | 9 | >以下都是采用mysql数据库 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 | ### 2.1 事务的隔离级别分为: 39 | 40 | - Read uncommitted(读未提交) 41 | - Read Committed(读已提交) 42 | - Repeatable Reads(可重复读) 43 | - Serializable(串行化) 44 | 45 | #### Read uncommitted 46 | 47 | `读未提交`:隔离级别最低的一种事务级别。在这种隔离级别下,会引发脏读、不可重复读和幻读。 48 | 49 | #### Read Committed 50 | 51 | `读已提交`读到的都是别人提交后的值。这种隔离级别下,会引发不可重复读和幻读,但避免了脏读。 52 | 53 | #### Repeatable Reads 54 | `可重复读`这种隔离级别下,会引发幻读,但避免了脏读、不可重复读。 55 | 56 | #### Serializable 57 | 58 | `串行化`是最严格的隔离级别。在Serializable隔离级别下,所有事务按照次序依次执行。脏读、不可重复读、幻读都不会出现。 59 | 60 | ![隔离级别](https://img-blog.csdnimg.cn/20190411212953426.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 61 | 62 | ## 三、操作 63 | 64 | ### 3.1 查看事务隔离级别 65 | 66 | `SHOW VARIABLES LIKE 'tx_isolation';` 67 | 68 | 查看全局的事务隔离级别 69 | 70 | `SHOW GLOBAL VARIABLES LIKE 'tx_isolation';` 71 | 72 | 使用系统变量查询 73 | 74 | `SELECT @@global.tx_isolation;` 75 | `SELECT @@session.tx_isolation;` 76 | `SELECT @@tx_isolation;` 77 | 78 | ### 3.2 设置MysQL的事务隔离级别 79 | 80 | #### 语法 81 | ``` 82 | SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL 83 | { 84 | REPEATABLE READ 85 | | READ COMMITTED 86 | | READ UNCOMMITTED 87 | | SERIALIZABLE 88 | } 89 | ``` 90 | `GLOBAL`:设置全局的事务隔离级别 91 | `SESSION`:设置当前session的事务隔离级别,如果语句没有指定GLOBAL或SESSION,默认值为SESSION 92 | 93 | #### 使用系统变量设置事务隔离级别 94 | 95 | `SET GLOBAL tx_isolation='REPEATABLE-READ';` 96 | `SET SESSION tx_isolation='SERIALIZABLE';` 97 | 98 | ## 四、案例分析 99 | 100 | 下面实际操作中使用到的一些并发控制语句,可看上面的**操作**介绍 101 | 102 | 103 | 作为演示:product表 104 | 105 | |productId | productName| productPrice| productCount| 106 | |---|---|---|---| 107 | |1 | xiaomi|1999|100| 108 | 109 | 110 | 带着上面的我们来看一下,事务在没有隔离性的情况下,会引发哪些问题? 111 | 112 | 同时打开两个窗口模拟2个用户并发访问数据库 113 | 114 | ### 4.1 事务隔离级别设置为read uncommitted 115 | 查询事务隔离级别 116 | 117 | ``` 118 | SELECT @@tx_isolation; 119 | ``` 120 | 121 | 设置隔离级别为**未提交读**: 122 | 123 | ``` 124 | SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 125 | ``` 126 | 127 | > 注意:需要同时修改两个窗口的事务隔离级别 128 | 129 | 以下我们以两位用户抢小米手机为例 130 | 131 | |时间轴| 事务A| 事务B| 132 | |---|---|---| 133 | |T1 | start transaction;| | 134 | |T2| select p.productName,p.productCount from product p where p.productId=1;(productCount =100)| | 135 | |T3 | | start transaction; | 136 | |T4 | | select p.productName,p.productCount from product p where p.productId=1;(productCount =100)| 137 | |T5 | |update product set productCount = 99 where productId = 1; | 138 | |T6 | select p.productName,p.productCount from product p where p.productId=1;(productCount =99)|| 139 | |T7 | |ROLLBACK;| 140 | |T8 | select p.productName,p.productCount from product p where p.productId=1;(productCount =100)|| 141 | 142 | --- 143 | T1—— A用户开启事务,start transaction; 144 | T2—— A用户查询当前小米手机剩余数量,select p.productName,p.productCount from product p where p.productId=1;此时数量显示为100。 145 | T3——B用户开启事务,start transaction; 146 | T4——B用户查询当前小米手机剩余数量,select p.productName,p.productCount from product p where p.productId=1;此时数量显示为100。 147 | T5—— B用户购买了一台小米手机,update product set productCount = 99 where productId = 1; 此时只修改数据并未提交事务。 148 | T6—— A用户刷新页面,select p.productName,p.productCount from product p where p.productId=1;此时数量显示为99。 149 | T7—— B用户购买失败,回滚事务。 150 | T8—— A用户查询当前小米手机剩余数量,select p.productName,p.productCount from product p where p.productId=1;此时数量显示为100。 151 | 152 | --- 153 | #### 小结: 154 | 事务A读取了未提交的数据,事务B的回滚,导致了事务A的数据不一致,导致了事务A的`脏读` ! 155 | 156 | ### 4.2 事务隔离级别设置为Read Committed 157 | 查询事务隔离级别 158 | 159 | ``` 160 | SELECT @@tx_isolation; 161 | ``` 162 | 163 | 更改数据库隔离级别,设置隔离级别为**提交读**: 164 | 165 | ``` 166 | SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; 167 | ``` 168 | > 注意:需要同时修改两个窗口的事务隔离级别 169 | 170 | |时间轴| 事务A| 事务B| 171 | |---|---|---| 172 | |T1 | start transaction;| | 173 | |T2| select p.productName,p.productCount from product p where p.productId=1;(productCount =100)| | 174 | |T3 | | start transaction; | 175 | |T4 | | select p.productName,p.productCount from product p where p.productId=1;(productCount =100)| 176 | |T5 | |update product set productCount = 99 where productId = 1; | 177 | |T7 | select p.productName,p.productCount from product p where p.productId=1;(productCount =100)|| 178 | |T6 | |commit;| 179 | |T8 | select p.productName,p.productCount from product p where p.productId=1;(productCount =99)|| 180 | 181 | --- 182 | 183 | 这里就不再对流程做过多赘述。 184 | ##### 小结: 185 | 186 | 可以看到避免了`脏读`现象,但是却出现了,一个事务还没有结束,就发生了不可重复读问题,即事务A来说 productCount从 100->100->99。但这个过程中事务并未提交结束。 187 | 188 | ### 4.3 事务隔离级别设置为Repeatable Read(mysql默认级别) 189 | 查询事务隔离级别 190 | 191 | ``` 192 | SELECT @@tx_isolation; 193 | ``` 194 | 195 | 更改数据库隔离级别,设置隔离级别为**可重复读**: 196 | 197 | ``` 198 | SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; 199 | ``` 200 | 201 | > 注意:需要同时修改两个窗口的事务隔离级别 202 | 203 | |时间轴| 事务A| 事务B| 204 | |---|---|---| 205 | |T1 | start transaction;| | 206 | |T2| select p.productName,p.productCount from product p where p.productId=1;(productCount =100)| | 207 | |T3 | | start transaction; | 208 | |T4 | | select p.productName,p.productCount from product p where p.productId=1;(productCount =100)| 209 | |T5 | |update product set productCount = 99 where productId = 1; | 210 | |T7 | select p.productName,p.productCount from product p where p.productId=1;(productCount =100)|| 211 | |T6 | |commit;| 212 | |T8 | select p.productName,p.productCount from product p where p.productId=1;(productCount =100)|| 213 | 214 | --- 215 | 216 | 这里就不再对流程做过多赘述。 217 | ##### 小结: 218 | 可以看到`可重复读`隔离级别避免了`脏读`,`不可重复读`的问题,但是出现了`幻读`现象。事务A查询到的小米数量等于100,但是事务B修改了数量为99,但是事务A读取到的值还是100。当事务A去减1等于99时,是错误的,此时应该是99-1=98才对。接下来我们再提高一个事务隔离级别。 219 | 220 | ### 4.4 事务隔离级别设置为Serializable 221 | 查询事务隔离级别 222 | 223 | ``` 224 | SELECT @@tx_isolation; 225 | ``` 226 | 227 | 更改数据库隔离级别,设置隔离级别为**串行化**: 228 | 229 | ``` 230 | SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; 231 | ``` 232 | 233 | |时间轴| 事务A| 事务B| 234 | |---|---|---| 235 | |---|---|---| 236 | |T1 | start transaction;| | 237 | |T2 | | start transaction; | 238 | |T2| select p.productName,p.productCount from product p where p.productId=1;(productCount =100);| | 239 | |T4 | | update product set productCount = 99 where productId = 1;(等待中..)| 240 | 241 | --- 242 | 243 | 这里就不再对流程做过多赘述。 244 | ##### 小结: 245 | 在我们Serializable隔离级别中,我们可以看到事务B去做修改动作时卡主了,不能向下执行。这是因为:给事务A的select操作上了锁,所以事务B去修改值的话,就会被卡主。只有当事务A操作执行完毕,才会执行事务B的操作。这样就避免了上述三个问题了。 246 | 247 | ## 问题本身 248 | - 回到问题的本身,其实我们并不需要将事务提到这么高。 249 | 250 | 251 | - 问题的本身就是,当我们读完了的时候,就要在上面加锁。我们不希望别人能够去读它。因为别人读到了count,就会修改count的值,并写进去。所以我们在select 操作的时候,加上for update。这时候就会把这行操作给锁掉了。那么另外一个人也进行相同的操作,也表示select 出来的count需要进行update,需要锁住。 252 | 253 | ``` 254 | select p.productName,p.productCount from product p where p.productId=1 for update; 255 | ``` 256 | 257 | >PS: 在实际开发过程中,这样的加锁行为,是非常的耗系统性能的。下一章节我们将来介绍`悲观锁与乐观锁` 258 | 259 | ## 文末 260 | 261 | >本章节主要介绍了数据库中事务的ADID特性中的`隔离性`,在没有隔离的情况下会发生什么问题,相信大家通过本章,对数据库事务中的`隔离性`有了一定的了解,下篇文章我们将介绍数据库中的`悲观锁与乐观锁`。 262 | 欢迎关注公众号:**Coder编程** 263 | 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 264 | 265 | ![微信公众号](https://img-blog.csdnimg.cn/20190411234814936.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 266 | 267 | 268 | ## 推荐阅读 269 | 270 | [带你了解数据库中JOIN的用法 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483713&idx=1&sn=d61ad0aed42dc36d64d17732db352288&chksm=96e67052a191f9445bbe3d5825ce547ad3171c3874b571a93b97977d0668413e37a164c3e0bc&token=1144933717&lang=zh_CN#rd) 271 | 272 | [ 带你了解数据库中group by的用法 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483717&idx=1&sn=157a8a021c29043a10480d0294b39ca0&chksm=96e67056a191f940668812ebb092fe9984b22eb619a18339cc052e1051c659a7e9d907c48814&token=1144933717&lang=zh_CN#rd) 273 | 274 | [ 带你了解数据库中事务的ACID特性 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483722&idx=1&sn=e8bc8bd82c559e0cfe7f35cf46100af3&chksm=96e67059a191f94fe8948e5b5e4ef177b77fa7707d86d945b153f67e7f2e76b83ed0c768ef27&token=1701970188&lang=zh_CN#rd) 275 | 276 | 277 | 278 | 279 | [Github个人主页目录](https://github.com/CoderMerlin/coder-programming) 280 | 281 | [Gitee个人主页目录](https://gitee.com/573059382/coder-programming) 282 | 283 | **欢迎大家关注并Star~** 284 | -------------------------------------------------------------------------------- /src/06-data-base/join.md: -------------------------------------------------------------------------------- 1 | 2 | ## 前言 3 | > 欢迎关注公众号:**Coder编程** 4 | > 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 5 | 6 | 本章主要介绍数据库中Join的的用法,也是我们在使用数据库时非常基础的一个知识点。本次会介绍数据库中的`inner join`、`left join`、`right join` 的用法以及它们之间的区别。 文章如有错误还请大家及时指出~ 7 | 8 | >以下都是采用mysql数据库 9 | 10 | ## Join 11 | 相信大家在学习数据库的使用时,都有使用过Join,对数据库中的两张或两张以上表进行连接操作。 12 | **Join** 分为: 13 | - 内连接(inner join) 14 | - 外连接(outer join) 15 | 16 | 其中**外连接**分为: 17 | - 左外连接(left outer join) 18 | - 右外连接(right outer join) 19 | - 全外连接(full outer join) 20 | 21 | >**说明:** 22 | >>1.其中外连接的“OUTER”关键字可以省略不写。 23 | >>2.内连接用于返回满足连接条件的记录;而外连接则是内连接的扩展,它不仅会满足连接条件的记录,而且还会返回不满足连接条件的记录。 24 | 25 | 26 | 27 | ## 笛卡尔积 28 | >在我们进行多表联合查询的时候会出现的一种情况——**笛卡尔积现象** 29 | 30 | 我们以下面两张表举例: 31 | 32 | **学生表(Student)** 33 | |ID | StudentName | StudentClassID| 34 | |---|---|---| 35 | |1 | 小明| 1| 36 | |2 | 小红| 2| 37 | |3 | 小兰 | 3| 38 | |4 | 小吕 | 2| 39 | |5 | 小梓 | 1| 40 | 41 | **班级表(Class)** 42 | |ClassID| ClassName| 43 | |---|---| 44 | |1 | 软件一班| 45 | |2 | 软件二班| 46 | |3 | 软件三班| 47 | 48 | 当我们进行查询操作的时候: 49 | > select * from Student,Class; 50 | 51 | --- 52 | ![查询](https://img-blog.csdnimg.cn/20190407222148953.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 53 | 就会出现上面的情况,也就是笛卡尔现象,表Student中有5条记录,表Class中有3条记录,那么对于表Student而言有5种选择,对于表Class来说有3种选择。所以一共有 5 * 3 = 15种选择了,也就是**笛卡尔积**。 54 | 55 | --- 56 | 57 | ## 内连接——inner join 58 | 内连接查询返回满足条件的所有记录,默认情况下没有指定任何连接则为内连接。 59 | 例如:查询xx学生在xx班级 ` 沿用上面的数据表` 60 | 61 | >select stu.StudentName,cl.ClassName from Student stu `inner join` Class cl on stu.StudentClassID=cl.ClassID; 62 | 63 | --- 64 | **查询结果** 65 | ![inner join](https://img-blog.csdnimg.cn/20190407224142330.png) 66 | 67 | 68 | ## 左外连接——left join 69 | 左外连接查询不仅返回满足条件的所有记录,而且还会返回不满足连接条件的连接操作符左边表的其他行。 70 | 我们在原Student 表中新增学生:小美 71 | ![新增学生-小美](https://img-blog.csdnimg.cn/20190407225114143.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 72 | 例如: 查询xx学生在xx班级 ` 沿用上面的数据表` 73 | >select stu.StudentName,cl.ClassName from Student stu `left join` Class cl on stu.StudentClassID=cl.ClassID; 74 | 75 | --- 76 | **查询结果** 77 | ![左外连接](https://img-blog.csdnimg.cn/20190407225326466.png) 78 | 79 | 80 | ## 右外连接——right join 81 | 右外连接查询不仅返回满足条件的所有记录,而且还会返回不满足连接条件的连接操作符右边表的其他行。 82 | 我们在原Class表中新增班级:软件四班 83 | 84 | ![新增班级_软件四班](https://img-blog.csdnimg.cn/20190407225710404.png) 85 | 例如: 查询xx学生在xx班级 ` 沿用上面的数据表` 86 | >select stu.StudentName,cl.ClassName from Student stu right join Class cl on stu.StudentClassID=cl.ClassID; 87 | 88 | --- 89 | **查询结果** 90 | ![查询结果](https://img-blog.csdnimg.cn/20190407225819462.png) 91 | 92 | ## 全连接——full join 93 | 全连接查询不仅返回满足条件的所有记录,而且还会返回不满足连接条件的其他行。 94 | >注:mysql默认不支持full join。 95 | 96 | 这里我们就不做介绍了。 97 | 98 | --- 99 | ## 文末 100 | 101 | >本章节主要介绍了数据库中JOIN的用法,以及其他方面的小知识点。 102 | 欢迎关注公众号:**Coder编程** 103 | 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 104 | 105 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190407231851378.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2hhZWxfSE0=,size_16,color_FFFFFF,t_70) 106 | ## 推荐阅读 107 | 108 | [一篇让你理解进程与线程的区别与联系](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483676&idx=1&sn=9033af3cb37754446779e1fcd89e3329&chksm=96e6700fa191f91919b4c2a46a8a99a7a7dda037181e97b5377835500e99a5f66cb1d3337898&token=424171447&lang=zh_CN#rd) 109 | 110 | [通过“表白”的方式,让我们快速了解网络协议](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483701&idx=1&sn=b21d65f8ba4ae7f861a6e6175be2303c&chksm=96e67026a191f930c540a8c823c6ad5355dc4cb92824eadc9485aa195167768560dc506af358&token=1104592742&lang=zh_CN&scene=21#wechat_redirect) 111 | 112 | [一篇带你读懂TCP之“滑动窗口”协议 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483706&idx=1&sn=8eed9d160013bd8ed6203ad511711553&chksm=96e67029a191f93fdd1543af2bf06025397d9c3bd0f0692c7fe247ab9c139cd869d69ab05498&token=1104592742&lang=zh_CN#rd) 113 | 114 | 115 | 参考文章: 116 | 117 | https://www.cnblogs.com/jackson-zhangjiang/p/7819634.html 118 | 119 | http://www.360doc.com/content/14/1229/21/7635_436727229.shtml 120 | 121 | 122 | [Github个人主页目录](https://github.com/CoderMerlin/coder-programming) 123 | [Gitee个人主页目录](https://gitee.com/573059382/coder-programming) 124 | 125 | **欢迎大家关注并Star~** 126 | -------------------------------------------------------------------------------- /src/06-data-base/rowlock_tablelock.md: -------------------------------------------------------------------------------- 1 | 2 | ![图片](xxxx.png) 3 | ## 前言 4 | 在上一个章节xxx,本章节接下来将主要介绍xxxx的相关知识。如有错误还请大家及时指出~ 5 | 6 | >本文已同步至 [GitHub](https://github.com/CoderMerlin/coder-programming)/[Gitee](https://gitee.com/573059382/coder-programming)/公众号,感兴趣的同学帮忙点波关注~ 7 | 8 | 问题: 9 | 10 | - xxxxx? 11 | - xxxxx? 12 | 13 | 14 | ## 正文 15 | 16 | 17 | ## 文末 18 | 19 | 本章节主要简单介绍了xxx 的相关知识,后续我们将会继续xxx。 20 | 21 | >欢迎关注公众号:**Coder编程** 22 | 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 23 | 24 | 参考文章: 25 | https://blog.csdn.net/why15732625998/article/details/80439315 26 | 27 | https://blog.csdn.net/zgcr654321/article/details/82345087 28 | 29 | https://blog.csdn.net/qq_36071795/article/details/83869042 30 | 31 | https://blog.csdn.net/xts5701046/article/details/81395958 32 | 33 | 34 | 35 | ![微信公众号](https://user-gold-cdn.xitu.io/2019/4/16/16a26835c75c12fc?w=300&h=390&f=png&s=18217) 36 | 37 | ## 推荐阅读 38 | 39 | xxxxx 40 | 41 | xxxxx 42 | 43 | xxxxx 44 | 45 | 46 | #### 行锁(Row Lock)与表锁(Table Lock) 47 | 48 | 使用select…for update会把数据给锁住,这是通过数据库实现的悲观锁。接下来我们来介绍下行锁与表锁。 49 | 50 | ##### 行锁 51 | 52 | 作为演示,我们继续使用之前的数据库表:product表 53 | 54 | |productId | productName| productPrice| productCount| 55 | |---|---|---|---| 56 | |1 | 小米 |1999|100| 57 | |2| 魅族 |1999|100| 58 | 59 | 1. 只根据主键进行查询,并且查询到数据,主键字段产生行锁。 60 | 61 | **操作** 62 | 63 | 下面同时打开两个窗口模拟2个用户并发访问数据库 64 | 65 | |时间轴| 事务A| 事务B| 66 | |---|---|---| 67 | |T1 | start transaction;| | 68 | |T2| select p.productCount from product p where p.productId = 1 for update;(productName = 小米)| | 69 | |T3 | | start transaction; | 70 | |T4 | | select p.productCount from product p where p.productId = 2 for update;(productName = 魅族) | 71 | |T5 | | select p.productCount from product p where p.productId = 1 for update;(等待中...)| 72 | 73 | **说明** 74 | productId是主键,当在事务A上查询productId=1的数据时候,在事务B上查询productId=2的数据没问题,但在事务B上查询productId=1的数据时阻塞,说明此时的锁时行锁。当事务A执行commit时,事务B查询的productId=1的命令立即返回数据。 75 | 76 | 2. 根据非主键含索引(productName)进行查询,并且查询到数据,productName字段产生行锁,将整行锁住,其他条件查询该数据阻塞。 77 | 78 | **操作** 79 | 80 | 下面同时打开两个窗口模拟2个用户并发访问数据库 81 | 82 | |时间轴| 事务A| 事务B| 83 | |---|---|---| 84 | |T1 | start transaction;| | 85 | |T2| select p.productCount from product p where p.productId = 1 for update;(productName = 小米)| | 86 | |T3 | | start transaction; | 87 | |T4 | | select p.productCount from product p where p.productId = 2 for update;(productName = 魅族) | 88 | |T5 | | select p.productCount from product p where p.productId = 1 for update;(等待中...)| 89 | 90 | 91 | 92 | 93 | ##### 表锁 -------------------------------------------------------------------------------- /src/07-front-end/other.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,内容正在努力加载中...... # -------------------------------------------------------------------------------- /src/08-frame/other.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,内容正在努力加载中...... # -------------------------------------------------------------------------------- /src/09-framework/README.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,内容正在努力加载中...... # -------------------------------------------------------------------------------- /src/10-micro-service-and-devops/other.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,内容正在努力加载中...... # -------------------------------------------------------------------------------- /src/11-test/other.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,内容正在努力加载中...... # -------------------------------------------------------------------------------- /src/12-project/other.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,内容正在努力加载中...... # -------------------------------------------------------------------------------- /src/13-operation/other.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,内容正在努力加载中...... # -------------------------------------------------------------------------------- /src/14-tools/other.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,内容正在努力加载中...... # -------------------------------------------------------------------------------- /src/15-agreement/NetworkSevenLayerProtocol.md: -------------------------------------------------------------------------------- 1 | 本章主要介绍网络协议中的网络七层协议,因为在我们谈到任何联网的协议时,我们就会谈到`OSI(网络七层协议模型)`,必须遵循这个协议模型, 2 | 我们的手机和电脑才可以联网通信,接下来我们来一起看一下`OSI(网络七层协议模型)` 3 | 4 | 我们来看下通常情况下的七层协议 5 | -------------------------------------------------------------------------------- /src/15-agreement/README.md: -------------------------------------------------------------------------------- 1 | ## :mega: 谢谢关注,欢迎关注微信公众号:Coder编程。内容将持续更新! 2 | ## :mega: 本章导航内容主要——JAVA中的基础知识! 3 | 4 | [通过“表白”的方式,让我们快速了解网络协议](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483701&idx=1&sn=b21d65f8ba4ae7f861a6e6175be2303c&chksm=96e67026a191f930c540a8c823c6ad5355dc4cb92824eadc9485aa195167768560dc506af358&token=1104592742&lang=zh_CN&scene=21#wechat_redirect) 5 | [一篇带你读懂TCP之“滑动窗口”协议 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483706&idx=1&sn=8eed9d160013bd8ed6203ad511711553&chksm=96e67029a191f93fdd1543af2bf06025397d9c3bd0f0692c7fe247ab9c139cd869d69ab05498&token=1104592742&lang=zh_CN#rd) -------------------------------------------------------------------------------- /src/16-learning-materials/other.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,内容正在努力加载中...... # 2 | 首页 3 | 博客 4 | 学院 5 | 下载 6 | 图文课 7 | 论坛 8 | APP 9 | 问答 10 | 商城 11 | VIP会员 12 | 活动 13 | 招聘 14 | ITeye 15 | GitChat 16 | 搜博主文章 17 | 写博客 小程序 消息 18 | 19 | 20 | 关闭 21 | 两年Java开发工作经验面试总结 05-08 22 | 2年工作经验的Java程序员面试经历 06-11 23 | 一位10年Java工作经验的架构师聊Java和工作经验 10-29 24 | java面试题:2年工作经验java简历包装,面试为什么公司不通过... 01-25 25 | 面试感悟:3年工作经验java程序员应有的技能 11-24 26 | 3年工作经验java程序员应有的技能 01-09 27 | 三年工作经验——感悟 12-11 28 | 29 | 30 | Java开发工作经验面试总结 12-20 31 | JAVA面试题(1年工作经验!) 11-06 32 | 我的JAVA面试经验(3年左右工作经验) 09-14 33 | java笔试、面试、找工作经验 09-07 34 | 「女程序员」JAVA面试经验(3年左右工作经验) 12-21 35 | 2年java开发工作经验 10-12 36 | 找工作:java开发三年工作经验 03-05 37 | 两年工作经验java面试题精炼汇总 12-30 38 | 如何面试有2年java工作经验的应聘人员 06-24 39 | java面试题(一年工作经验)的心得 07-16 40 | 2年Java开发工作经验,跳槽之后面试20余家公司的总结 02-25 41 | 我的JAVA面试经验(5年左右工作经验) 10-10 42 | 一年多Java开发工作经验面试总结 12-22 43 | 一位10年Java工作经验的架构师聊Java和工作经验_一位10年Jav... 11-17 44 | 3年工作经验的Java程序员面试经过 12-22 45 | java工作经验总结 01-01 46 | java两年开发工作经验面试总结 12-06 47 | Java三年工作经验面试准备(一) 01-25 48 | 某公司Java面试题(1年左右工作经验) 02-17 49 | 15 个必须知道的 Java 面试问题(2年工作经验) 07-17 50 | android2-3年工作经验面试及心得 03-14 51 | 52 | 53 | 一年工作经验 03-09 54 | 英文面试:工作经验篇 08-03 55 | 北漂面试经历(一(两)年工作经验)——Java基础部分 05-12 56 | 十个iOS面试问题【2年工作经验】 11-05 57 | 工作经验 11-08 58 | 3年Java开发工作经验的你需要具备什么技能? 01-11 59 | 面试了10个2年MySQL_DBA工作经验 11-08 60 | 转 61 | java面试:2年java开发工作经验 62 | 2018年04月12日 22:04:26 zhanzhan0816 阅读数:100更多 63 | 个人分类: java面试 64 | 转载:https://blog.csdn.net/xp_lx1/article/details/78217680 65 | 66 | 最近换了个公司,从三月底开始面,面到四月底,面了有快二十家公司。我是一个喜欢总结经验的人,每经过一场面试,我在回来的路上都会仔细回想今天哪些问题可以答的更好,或者哪些问题是自己之前没遇到过的,或者是哪个知识点今天又问了等等。四月中旬的时候,我就在构思要写一篇面经,主要是想着可能对那些跟我相同处境的人有点帮助,再者就是稍微记录下这为期一个月的面试过程。 67 | 68 | 个人介绍: 69 | 首先介绍下我面试时的自身条件情况,我把自己的情况分为优势和劣势来说可能更有利于你们比较自身情况。 70 | 71 | 劣势: 72 | 73 | 1.15年7月毕业后开始到上海工作,面试的时候是17年3月到4月,一年多的经验,勉强算两年经验。分析:一年多经验我认为是比较尴尬的,处于一个不上不下的位置,很多公司比较喜欢招三年经验的,或者直接招应届生来培养。 74 | 75 | 2.毕业于一个非985/211,勉强上一本的高校。分析:这个相对影响较小,因为有工作经验后,公司对学校的要求就没那么高了,只要是本科就基本没问题,但是还是有个别叼毛公司只要985/211。 76 | 77 | 3.前一家公司是传统电信行业,加入项目组时,项目已经上线有段时间了,我们的任务就是有需求就开发,有bug就优化,其他时间就聊骚,各干各的,工作一年多跟在养老一样,用一句话说就是编程5分钟,扯淡2小时,项目经验严重不足,没开发过很难的需求。分析:这一点是最伤的,公司招有经验的就想看你都干了些什么牛批的东西,结果你告诉面试官我写的需求都是垃圾。 78 | 79 | 优势: 80 | 81 | 1.大学时拿过比较多的奖,每年都是校级优秀学生,毕业时是市级优秀毕业生,拿过省级ACM二等奖等。分析:大学的荣誉对一个有工作经验的人来说,公司不一定会看重,但是可能会对面试官产生微妙的影响,特别是ACM奖,我碰到过有的面试官也是搞过ACM的,有共同的话题聊起来总是比较容易的,但是也要注意不能把这一栏篇幅写的过于多,只能当作点缀用,我当时是放在简历最后一栏,简要的写了最主要的几个奖。 82 | 83 | 2.良好的沟通交流能力。分析:这个能力不会是关键性的,但是可以加分。 84 | 85 | 3.较强的学习能力和逻辑思维能力。分析:有些公司和面试官还是比较看重一个人的学习能力的,经验代表着你现在在什么级别,而学习能力则代表着你将来能到达什么级别。 86 | 87 | 学习过程: 88 | 看了我的优劣势介绍,你会发现我的优势相对于我的劣势来说,简直不值一提。我自己对此也有清晰的认识,因此从过完年之后,我就开始抓紧空闲时间学习。学习的过程如下: 89 | 90 | 1.看面试题 91 | 92 | 正常人第一步肯定都会看面试题,我也不例外,在看的过程中,我发现有些文章写的不错,对我帮助不小值得推荐,如下: 93 | Java面试题全集(上)很多基础的东西,建议先看。 94 | 各大公司Java后端开发面试题总结 95 | 面试心得与总结—BAT、网易、蘑菇街 96 | 关于Java面试,你应该准备这些知识点 97 | 98 | 2.深入学习 99 | 100 | 在看面试题的过程,你会遇到一些自己没接触过的或者以前没深入学习过的知识,例如最常问的HashMap内部实现原理,这就促使你得开始去看jdk的源码或者是学习一些新的东西。看源码是很重要的一步,起步很难,但是会让你收益良多,看源码的过程如果碰到无法理解的地方,可以百度看下别人的理解。我学习源码的过程中,看过几个人的关于源码的文章写的很不错,如下: 101 | 五月的仓颉 102 | 占小狼 103 | zhangshixi的Core java系列 104 | 105 | 3.熟悉项目 106 | 107 | 找出自己工作以来开发过的最叼的功能,将整个功能的流程和涉及的东西吃透。项目是面试中必问的环节,一般是以一个功能点为基础展开问,因此你必须对这个功能有很深的认识,不能有模糊的地方。如果有时间,能把涉及到的知识点也搞懂最好。 108 | 109 | 4.做面试题 110 | 111 | 有不少公司是有面试的,如果你没有准备过,很容易在各种小地方犯错,建议去一些面试题网站多做些题目,我自己是用的牛客网。 112 | 113 | 5.学习记录 114 | 115 | 把自己每天的学习时间和学习内容记录下来,可以让自己更有动力的学习,学习是一个枯燥的过程,你必须让自己时刻保持有动力。 116 | 117 | 投简历、约面试环节 118 | 1.在哪些网站投? 119 | 120 | 拉勾网、BOSS直聘、猎聘网。 121 | 122 | 2.是否该海投? 123 | 124 | 投简历分为两个情况。 125 | 1)没有社招面试经验:建议采取海投的方式,只要职位要求跟自己比较匹配都可以投,累计面试经验。这个环节可以把投简历的网站增加两家:智联和无忧。 126 | 2)自认为社招面试经验已经足够:投那些职位匹配、公司满意的职位。公司评价可以去看准网、百度、知乎等查询。 127 | 128 | 129 | 面试常见的问题上面给的面试题链接基本都有。我只提几点: 130 | )写SQL:写SQL很常考察group by、内连接和外连接。2)手写代码:手写代码一般考单例、排序、线程、消费者生产者。我建议排序算法除了冒泡排序,最好还能手写一种其他的排序代码。试想:如果一般面试者都写的冒泡排序,而你写的是快速排序/堆排序,肯定能给面试官留下不错的印象。 131 | 132 | 2.面试流程? 133 | 134 | 1)让你自我介绍 135 | 2)问Java基础知识 136 | 3)问项目 137 | 4)情景问题,例如:你的一个功能上了生产环境后,服务器压力骤增,该怎么排查。 138 | 5)你有什么想问面试官的 139 | 140 | 3.面试常问的知识点? 141 | 142 | 1)集合相关问题(必问): 143 | 细聊Map家族那些事(一)——Map 144 | 细聊Map家族那些事(二)——HashMap 145 | 细聊Map家族那些事(三)——LinkedHashMap 146 | 细聊Map家族那些事(四)——ConcurrentHashMap 147 | 148 | 细聊List家族那些事(一)——List 149 | 细聊List家族那些事(二)——ArrayList 150 | 细聊List家族那些事(三)——LinkedList 151 | 细聊List家族那些事(四)——Vector 152 | 153 | 154 | 2)线程相关问题(必问): 155 | 创建线程的3种方式。 156 | 什么是线程安全。 157 | Runnable接口和Callable接口的区别。 158 | wait方法和sleep方法的区别。 159 | synchronized、Lock、ReentrantLock、ReadWriteLock。 160 | 介绍下CAS(无锁技术)。 161 | 什么是ThreadLocal。 162 | 创建线程池的4种方式。 163 | ThreadPoolExecutor的内部工作原理。 164 | 分布式环境下,怎么保证线程安全。 165 | 3)JVM相关问题: 166 | 介绍下垃圾收集机制(在什么时候,对什么,做了什么)。 167 | 垃圾收集有哪些算法,各自的特点。 168 | 类加载的过程。 双亲委派模型。 169 | 有哪些类加载器。 170 | 能不能自己写一个类叫java.lang.String。 171 | 4)设计模式相关问题(必问): 172 | 先问你熟悉哪些设计模式,然后再具体问你某个设计模式具体实现和相关扩展问题。 173 | 174 | 175 | 7)其他遇到问题: 176 | 介绍下栈和队列。 177 | IO和NIO的区别。 178 | 接口和抽象类的区别。 179 | int和Integer的自动拆箱/装箱相关问题。 常量池相关问题。 180 | ==和equals的区别。 181 | 重载和重写的区别。 182 | String和StringBuilder、StringBuffer的区别。 183 | 静态变量、实例变量、局部变量线程安全吗,为什么。 try、catch、finally都有return语句时执行哪个。 184 | 介绍下B树、二叉树。 185 | ajax的4个字母分别是什么意思。 186 | xml全称是什么。 187 | 分布式锁的实现。 188 | 分布式session存储解决方案。 189 | 常用的linux命令。 -------------------------------------------------------------------------------- /src/17-other/KimSanYinFour.md: -------------------------------------------------------------------------------- 1 | 金三银四跳槽季,大厂面试题,你能答对几道? 2 | 3 | 金三已经到了月末,想跳槽的同学得好好抓紧银四了。不过今年的寒冬确实有点冷~ 各路大厂都有不同程度的裁员。 4 | 因此我们要在面试过程的多做一些准备,我呢也在网上搜罗了一些大厂的面试题,在这里给大家分享一下,希望能给大家一些帮助与参考~ 5 | 6 | >以下来自网络: 7 | 8 | ## 阿里面试题 9 | 10 | - 开发中Java用了比较多的数据结构有哪些? 11 | - 谈谈你对HashMap的理解,底层原理的基本实现,HashMap怎么解决碰撞问题的? 12 | - 对JVM熟不熟悉?简单说说类加载过程,里面执行的哪些操作?问了GC和内存管理,平时在tomect里面有没有进行过相关的配置 13 | - 然后问了http协议,get和post的基本区别,接着tcp/ip协议,三次握手,窗口滑动机制。 14 | - 开发中用了那些数据库?回答mysql,储存引擎有哪些?然后问了我悲观锁和乐观锁问题使用场景、分布式集群实现的原理。 15 | - springmvc和mybatis的工作原理,有没有看过底层源码? 16 | - 多个线程同时读写,读线程的数量远远大于写线程,你认为应该如何解决并发的问题?你会选择加什么样的锁? 17 | - JAVA的AQS是否了解,它是干嘛的? 18 | - 除了synchronized关键字之外,你是怎么来保障线程安全的? 19 | - SpringBoot没有放到web容器里为什么能跑HTTP服务? 20 | - 如果查询很慢,你会想到的第一个方式是什么?索引是干嘛的? 21 | - 查询死掉了,想要找出执行的查询进程用什么命令?找出来之后一般你会干嘛? 22 | - 读写分离是怎么做的?你认为中间件会怎么来操作?这样操作跟事务有什么关系? 23 | - JAVA类加载器包括几种?它们之间的父子关系是怎么样的?双亲委派机制是什么意思?有什么好处? 24 | - 堆内存设置的参数是什么? 25 | - HashMap和Hashtable的区别? 26 | - 实现一个保证迭代顺序的HashMap? 27 | - 说一说排序算法,稳定性,复杂度? 28 | - 说一说GC? 29 | - JVM如何加载一个类的过程,双亲委派模型中有哪些方法? 30 | 31 | ## 京东面试题 32 | 33 | - java常用的数据结构有哪些?哪些是线程安全的?是怎么保证线程安全的? 34 | - Redis中的Lua有没有使用过? 可以用来做什么? 为什么可以这么用? 35 | - 线程池内部工作原理可以说一下么? 36 | - 死锁是什么意思,形成条件是什么?出现死锁是可以通过什么方式去排查。 37 | - 在交易过程中如何放在用户在支付时的重复支付(交叉支付),请写出你了解的方案或使用的过的方案? 38 | - 数据库索引有哪几种,他们之间的区别? 39 | - Dubbo超时重试;Dubbo超时时间设置 40 | - 如何保障请求执行顺序 41 | - 分布式事物与分布式锁(扣款不要出现负数) 42 | - 分布式session设置 43 | - 执行某操作,前50次成功,第51次失败a全部回滚b前50次提交第51次抛异常,ab场景分别如何设置Spring(传播性) 44 | - Zookeeper有哪些用 45 | - JVM内存模型 46 | - 数据库垂直和水平拆分 47 | - 熟悉IO么?与NIO的区别,阻塞与非阻塞的区别 48 | - 分布式session一致性 49 | - synchronized 的原理是什么?synchronized 和 ReentrantLock 有什么不同? 50 | - 数据库的锁,你能给我介绍下吗? 51 | - 知道哪些负载均衡算法? 52 | - 说一下Btree的查找原理? 53 | - 简述三次握手,如果c端发起握手请求,s端无法立刻建立连接应该回应什么? 54 | 55 | 56 | ## 携程面试题 57 | 58 | - 做过哪些项目?怎么实现的? 59 | - 公司整体架构?你做了啥? 60 | - spring mvc和struts mvc的区别? 61 | - 抽象类和接口的区别? 62 | - 覆盖和重载? 63 | - 进程和线程的区别? 64 | - 线程有哪些状态? 65 | - 如何创建线程? 66 | - wait和sleep的区别 67 | - 集合有那几种?list和set的区别? 68 | - hashmap的实现,冲突,初始容量和加载因子?怎么扩容? 69 | - 如何取出hashmap中的value存入list中? 70 | - spring的aop和Ioc讲讲吧? 71 | - 快速排序怎么实现的? 72 | - 数据库索引为什么用B树?原理是啥 73 | - 数据库的锁有哪几种?语句怎么实现? 74 | - 数据库分页查询? 75 | - 数据库的搜索引擎? 76 | - 分布式接口的幂等性设计「不能重复扣款」 77 | - 什么是线程,多线程的优点是什么?以及简单说一下多线程的几种实现方式。 78 | - ThreadLocal 用途是什么,原理是什么,用的时候要注意什么?ThreadPool用法与优势可以说一下么? 79 | - 线程池是什么?为什么要使用它?如何创建一个Java线程池? 80 | - 你有没有继续研究新接触到的技术? 81 | - 你有什么学习方法吗? 82 | - 你对工作地点有要求吗? 83 | 84 | ## 去哪儿网面试题 85 | 86 | - 自我介绍,项目介绍? 87 | - mysql数据库调优? 88 | - like能用索引吗? 89 | - java对象四种引用? 90 | - 类加载过程? 91 | - 说一说GC原理? 92 | - jvm内存结构。 93 | - 说一下你学过jvm 在书写代码上对你有什么帮助和提高。 94 | - 千万数据量的查询你会怎么做? 95 | - HashMap在jdk1.7和1.8的区别,为什么引入这个概念?hash碰撞怎么解决,为什么1.8要比1.7更好,好在哪? 96 | - 关于你的项目,如果并发很大,你会怎么改造。 97 | - 方法区里什么样的对象有可能被回收。 98 | - 线上cpu飙升100%你怎么处理。 99 | - 频繁FullGC怎么处理。 100 | - 线程池创建有几种,为什么创建定长的线程池个数最好是5,10,15这样的数字。 101 | - linux命令。 102 | - 伊甸区和幸存区可动态变化吗? 103 | - ThreadLocal用过吗,给我介绍下他的使用场景? 104 | - redis和memcached区别? 105 | - 说几个jdk命令,jmap是什么意思? 106 | - 如果并发很大,你对数据的正确性怎么保证? 107 | - TCP的三次握手和四次握手? 108 | 109 | ## 饿了么面试题 110 | 111 | - jvm,jre以及jdk三者之间的关系? 112 | - Dubbo的底层原理,Zookeeper是什么 113 | - cincurrentMap的机制;TreeMap;Volatil关键字 114 | - 快速排序;广度优先搜索(队列实现 115 | - 缓存的雪崩以及穿透的理解? 116 | - HashMap的key可以重复吗? 117 | - synchronized和lock的区别? 118 | - 开发一个大型网站你会考虑哪些问题? 119 | - Java的设计模式,单例有什么模式,懒汉为什么加volotile,volotile的内存屏障,如何避免死锁? 120 | - 考虑单例模式的编写,要线程安全且内存消耗小? 121 | - String、StringBuilder、StringBuffer区别;String类能被继承吗?为什么? 122 | - String为什么要设计成Final? 123 | - 在白纸上手写二分法排序算法(lintcode上原题);二分查找的思想。 124 | - 查找单链表中倒数第k个节点的算法,手写(lintcode上原题);最常见的排序算法你见过哪些,快排的基本思想及时间复杂度。 125 | - 常见的数据结构有哪些? 126 | - 自旋锁,偏向锁,轻量级锁,重量级锁的介绍以及升级过程? 127 | - hashmap、hashcode一样,不equals怎么处理 ;hashcode实现原理,currentHashMap原理,实现细节,怎么实现同步的;类为什么要有hascode方法,是不是主要在集合类中都要实现hashcode方法;equals方法怎么实现;两个不同的对象可能有相同的hashcode值吗;常用集合有哪些。 128 | - tcp三次握手,四次挥手协议? 129 | - 架构设计一个开发性问题,设计一个Nginx管理的中间件,怎么设计? 130 | - 所有的类都继承与object,你用过object类的直接子类有哪些,object类常用的方法有哪些。 131 | - Java会出现内存泄漏吗,如果会,在哪种情况下? 132 | - 抽象类和接口的区别? 133 | - 平时怎么扩展自己的专业知识水平? 134 | 135 | ## 百度面试题 136 | 137 | - 什么是 Java 的反射机制。 138 | - Cookie 和 Session的区别。 139 | - get 和 post请求的区别。 140 | - IOC的优点是什么。 141 | - IO 和 NIO的区别,NIO优点。 142 | - JRE、JDK、JVM 及 JIT 之间有什么不同。 143 | - Hashcode 的作用。 144 | - 简述一致性 Hash 算法。 145 | - 说出几点 Java 中使用 Collections 的最佳实践? 146 | - GC是什么?为什么要有GC。 147 | - 什么时候会导致垃圾回收。 148 | - GC 有几种方式?怎么配置。 149 | - 什么时候一个对象会被GC? 如何判断一个对象是否存活。 150 | - 垃圾回收器的基本原理是什么? 151 | - JVM 中一次完整的 GC 流程是怎样的? 对象如何晋升到老年代。 152 | - 吞吐量优先和响应优先的垃圾收集器选择。 153 | - 说说你知道的几种主要的jvm 参数。 154 | - 有T1,T2,T3三个线程,怎么确保它们按顺序执行?怎样保证T2在T1执行完后执行,T3在T2行完后执行同步块内的线程抛出异常会发生什么? 155 | - 什么是乐观锁(Optimistic Locking)?如何实现乐观锁?如何避免ABA问题。 156 | - Java中活锁和死锁有什么区别? 157 | - Executors类是什么? Executor和Executors的区别? 158 | - 什么是设计模式(Design Patterns)?你用过哪种设计模式?用在什么场合? 159 | - 你能写出三种单例模式实现么? 160 | - 你知道Google是如何在一秒内把搜索结果返回给用户? 161 | - 高并发下,如何做到安全的修改同一行数据? 162 | - 大型网站在架构上应当考虑哪些问题? 163 | - 最近有在看什么书么,印象最深刻的是什么? 164 | - 你们线上应用的 JVM 参数有哪些? 165 | - 能简单说下你对算法的理解么? 166 | 167 | 168 | ## 文末 169 | 可以看出大厂的面试题涉及的知识面不仅广而且深!因此我们平时学习的时候就要打好基础,面试的时候也要做足充分的准备! 170 | 最后祝大家找到满意的坑~ 也欢迎关注小编,会持续分享技术文章! 171 | 172 | > 欢迎关注公众号:**Coder编程** 173 | > 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 174 | 175 | **欢迎大家关注并Star~** 176 | [Github个人主页目录](https://github.com/CoderMerlin/coder-programming) 177 | [Gitee个人主页目录](https://gitee.com/573059382/coder-programming) 178 | -------------------------------------------------------------------------------- /src/17-other/README.md: -------------------------------------------------------------------------------- 1 | # :mega: 关注公众号:Coder编程 2 | 3 | **记录本人学习PMBOK第六版的学习笔记。** 4 | 5 | 备考知识总汇! 6 | 7 | ## PMBOK序章 8 | 9 | [PMP备考指南之相关事项介绍](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483746&idx=1&sn=05b6bdc1c46e535027ce8c65715c4c30&scene=19#wechat_redirect) 10 | 11 | [PMP备考指南之第一章:引论](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483751&idx=1&sn=d25153700d9a167ef6b577ff008b0cc2&scene=19#wechat_redirect) 12 | 13 | [PMP备考指南之第二章:项目运作环境](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483757&idx=1&sn=aa8960891e2f829310e4ba7056e1e53a&scene=19#wechat_redirect) 14 | 15 | [PMP备考指南之第三章:项目经理的角色](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483790&idx=1&sn=21d48b52547121878dc10338840116f7&scene=19#wechat_redirect) 16 | 17 | [PMP备考指南之第四章:项目整合管理](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483798&idx=1&sn=29a60203e33406b72f5933f111f01e3f&scene=19#wechat_redirect) 18 | 19 | [PMP备考指南之第五章:项目范围管理](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483802&idx=1&sn=cf2f367567d8b0afdfbb28205adbd16c&scene=19#wechat_redirect) 20 | 21 | [PMP备考指南之第六章:项目进度管理](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483806&idx=1&sn=00628bb912ec96cb0e5700101f751683&scene=19#wechat_redirect) 22 | 23 | [PMP备考指南之第七章:项目成本管理](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483810&idx=1&sn=4ba72b234fdd8ea99665db2099c90a0b&scene=19#wechat_redirect) 24 | 25 | [PMP备考指南之第八章:项目质量管理](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483814&idx=1&sn=927c309793ec2879683e9638ee5cdfb0&scene=19#wechat_redirect) 26 | 27 | [PMP备考指南之第九章:项目资源管理](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483824&idx=1&sn=cf5a0b481554d1b9a4c0497062f662b9&scene=19#wechat_redirect) 28 | 29 | [PMP备考指南之第十章:项目沟通管理](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483828&idx=1&sn=af8b7b6f8858029abf56c05b1f95cf21&scene=19#wechat_redirect) 30 | 31 | [PMP备考指南之第十一章:项目风险管理](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483833&idx=1&sn=c3ea00b633e28bc3e27d2eb7bbfa3e68&scene=19#wechat_redirect) 32 | 33 | [PMP备考指南之第十二章:项目采购管理](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483840&idx=1&sn=f9c6f2025d3ee9acc5e542e6aacf5290&scene=19#wechat_redirect) 34 | 35 | [PMP备考指南之第十三章:项目干系人管理](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483841&idx=1&sn=d06aa1363f57416363ddd5185e87f74f&scene=19#wechat_redirect) 36 | 37 | ## 考试技巧 38 | 39 | [PMP 备考指南之计算题汇总](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484021&idx=1&sn=e5c680d1215ec7fe602ff3fea63002ba&scene=19#wechat_redirect) 40 | 41 | [PMP备考指南之关键字总结](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484035&idx=2&sn=03c9833e6132b342f1019b85f413218d&scene=19#wechat_redirect) 42 | 43 | [PMP备考指南之相关概念区分](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484035&idx=1&sn=9b4e558a7d07d0e1e8916c66316e448d&scene=19#wechat_redirect) 44 | 45 | [PMP备考指南之:有效的固定答题](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484040&idx=1&sn=947c546b07ceafeaf0e54daf276c4fd0&scene=19#wechat_redirect) 46 | 47 | [PMP备考指南之常见的翻译问题](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484044&idx=1&sn=d4b8a49e7024534dc83ed333d2163bcc&scene=19#wechat_redirect) 48 | 49 | 欢迎大家点赞并关注微信公众号~ 50 | 51 | 欢迎关注个人微信公众号:Coder编程 52 | 欢迎关注Coder编程公众号,主要分享数据结构与算法、Java相关知识体系、框架知识及原理、Spring全家桶、微服务项目实战、DevOps实践之路、每日一篇互联网大厂面试或笔试题以及PMP项目管理知识等。更多精彩内容正在路上~ 53 | 新建了一个qq群:315211365,欢迎大家进群交流一起学习。谢谢了!也可以介绍给身边有需要的朋友。 54 | 55 | ![微信公众号](https://upload-images.jianshu.io/upload_images/7326374-51dd330909b7a67c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 56 | 57 | -------------------------------------------------------------------------------- /src/17-other/WebsiteCollection.md: -------------------------------------------------------------------------------- 1 | # :mega: 关注公众号:Coder编程 2 | 3 | **记录平时使用的高效网站** 4 | 5 | ## 图标类: 6 | - Shields徽章:https://shields.io/ 7 | - 阿里巴巴矢量图标库:https://www.iconfont.cn 8 | - 优质图标库:https://nucleoapp.com/ 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/17-other/learning_resource.md: -------------------------------------------------------------------------------- 1 | ## 关于资源 2 | 以下资源均来自网络收集,`若触及商业利益请及时联系我,我会第一时间删除`。为了减少大家找资源所或还在为找不到学习资料而浪费大量时间,我就将自己的寻找资源分享给大家,方便大家一起学习,一起进步。如果链接已失效,请在后台留言,我将第一时间进行回复!如果资料对你有帮助,也一并`分享`给你身边的朋友,关注公众号`Coder编程`~ 大家一起努力进步! 3 | 4 | > 如微信屏蔽外链,请移步至Github/Gitee中获取 5 | > Github: 6 | > Gitee: 7 | 8 | --- 9 | 10 | ## 1.编程基础知识 11 | 12 | **计算机组成原理** 13 | 14 | 链接:https://pan.baidu.com/s/1uSYpLLqNBQs2RY4DptiyYg 密码:lxi6 15 | 16 | **操作系统** 17 | 18 | 链接:https://pan.baidu.com/s/1vYaA-2FwE6s6JvIDmddwUQ 密码:nyoe 19 | 20 | **数据结构** 21 | 22 | 链接:https://pan.baidu.com/s/1eX14FIUQhPhnuIwIWuh0bA 密码:xd6e 23 | 24 | **Linux** 25 | 26 | - 老段带你学鸟哥-基础篇 27 | 链接:https://pan.baidu.com/s/1rOwFbPjDZSH35vcRvJcbPg 密码:i8p9 28 | 29 | - 老段带你学鸟哥-服务器篇 30 | 31 | 链接:https://pan.baidu.com/s/1E9hijOCFalD2952ffovkDg 密码:3wpv 32 | 33 | - Linux环境部署工具 34 | 35 | 链接:https://pan.baidu.com/s/1OktWLPpvAyweSr5sdktFLQ 密码:q6yl 36 | 37 | --- 38 | ## 2.Java基础 39 | 40 | - 20天横扫Java基础(课堂实录) 41 | 42 | 链接: https://pan.baidu.com/s/1htTzZRQ 43 | 44 | - JDBC视频教程 45 | 46 | 链接: https://pan.baidu.com/s/1c3XBTk8 47 | 48 | - Java8新特性 49 | 50 | 链接: http://pan.baidu.com/s/1cgWOH4 51 | 52 | - Java——JUC 53 | 54 | 链接: http://pan.baidu.com/s/1hsoh76k 55 | 56 | - Java——NIO 57 | 58 | 链接: http://pan.baidu.com/s/1c2N1ADy 59 | 60 | - Java9新特性 视频 61 | 62 | 链接: https://pan.baidu.com/s/1ge85H4Z 密码: 9e1k 63 | 64 | - 数据库 65 | 66 | https://pan.baidu.com/s/1VMhUoAYO-Yh9D_GEb6wvmA 密码:1qwc 67 | 68 | --- 69 | 70 | 71 | ## 3.JavaWeb基础知识 72 | 73 | - Struts2视频教程 74 | 75 | 链接: https://pan.baidu.com/s/1jI6xxkE 76 | 77 | - Hibernate 4视频教程 78 | 79 | 链接: https://pan.baidu.com/s/1bqpEEej 80 | 81 | - Spring 4视频教程 82 | 83 | 链接: https://pan.baidu.com/s/1O_avNG9liWl87teF31FEZg 84 | 85 | - SSH整合&综合案例视频 86 | 87 | 链接: https://pan.baidu.com/s/1dFbTMxV 88 | 89 | - SVN视频教程 90 | 91 | 链接: https://pan.baidu.com/s/1kWZz9vp 92 | 93 | - SpringMVC视频教程 94 | 95 | 链接: https://pan.baidu.com/s/1gfoaUw7 96 | 97 | 98 | - JPA视频教程 99 | 100 | 链接: https://pan.baidu.com/s/1hsqGMOW 101 | 102 | - SpringData视频 103 | 104 | 链接: https://pan.baidu.com/s/1c38938W 105 | 106 | - SSSP整合&分页视频 107 | 108 | 链接:https://pan.baidu.com/s/1miEVgr2 109 | 110 | --- 111 | 112 | ## 4.Java基础框架 113 | 114 | - spring官方文档:http://spring.io/docs/reference 115 | 116 | - spring所有开源项目:https://github.com/spring-projects 117 | 118 | - springboot官方Demo:https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples 119 | 120 | - springboot推荐Demo:https://github.com/dyc87112/SpringBoot-Learning 121 | 122 | - Maven视频 https://pan.baidu.com/s/1dHfbx8d 123 | 124 | - Shiro视频 https://pan.baidu.com/s/1yXiOStKfxSCYoMHsNrnCFQ 125 | 126 | - MySQL高级视频 https://pan.baidu.com/s/1i7ircH3 127 | 128 | - MyBatis 视频 https://pan.baidu.com/s/1snbVg77 129 | 130 | - SSM高级视频 https://pan.baidu.com/s/1eTcHjRc 131 | 132 | - MySQL基础视频178集 https://pan.baidu.com/s/1mjCyBm4 密码: p03n 133 | 134 | - 最新 Spring注解驱动开发 https://pan.baidu.com/s/1SzHGre2Upj8NzzGZ_6qM4Q 135 | 136 | - 最新 SpringBoot-核心技术篇 https://pan.baidu.com/s/1isXPv_NrBX2Fuf9pRLU2sQ 密码: sya6 137 | 138 | - 最新 SpringBoot-整合篇 https://pan.baidu.com/s/1LHFKQsuQDOmUyzMfRkq63w 密码:1n9z 139 | --- 140 | 141 | 142 | ## 5.JavaEE知识 143 | 144 | - 对SpringMVC和Mybatis的补充 145 | 146 | 链接:https://pan.baidu.com/s/1nvXKEpj密码:krzl 147 | 148 | - 手写SPringMvc框架【核心原理】 149 | 150 | 链接:https://pan.baidu.com/s/1ug04Ve4uol2WpWNNnfybGw 密码:7ugb 151 | 152 | - SSMShiro构建安全认证登录 153 | 154 | 链接:https://pan.baidu.com/s/1XB4jdavebwvfkQNNBFOMAA 密码:wunv 155 | 156 | - Maven构建Spring Springmvc Mybatis Shiro 157 | 158 | 链接:https://pan.baidu.com/s/1-Flm-XWJ7rJxOw2elIxRZQ 密码:lc4r 159 | 160 | - 框架必学 161 | 162 | 链接:https://pan.baidu.com/s/1HjsILxtxQsOnMu-zBseHLg 密码:e0h2 163 | 164 | - 分布式消处理RabbitMQ 165 | 166 | 链接:https://pan.baidu.com/s/1kOwcqorKKWayqt4XKI0bSg 密码:qrg7 167 | 168 | - 分布式搜索引擎elasticsearch 169 | 170 | 链接:https://pan.baidu.com/s/1-rsDq9AIlGTKnQZeIjUgoQ 密码:pqbr 171 | 172 | - Dubbo分布式服务框架精讲 173 | 174 | 链接:https://pan.baidu.com/s/1dijyfXnIFLEleBQm7WlkQQ 密码:l6ia 175 | 176 | - nginx视频教程视频教程 177 | 178 | 链接: https://pan.baidu.com/s/1nxccLg1 密码:x2q8 179 | 180 | - mongodb视频教程视频教程 181 | 182 | 链接: https://pan.baidu.com/s/1c3EE7U4 密码:7v8b 183 | 184 | - Redis视频 185 | 186 | http://pan.baidu.com/s/1pLKsBOJ 187 | 188 | - 最新 Mapper视频 189 | 190 | 链接: https://pan.baidu.com/s/1yfzUkHjMiF613uiiM5KBYw 密码:l0xw 191 | 192 | - 最新 Linux视频 193 | 194 | 链接: https://pan.baidu.com/s/1AmDqMODihyifgW9gevrSwA 密码:kxpp 195 | 196 | - 最新 SVN高级视频 197 | 198 | 链接: https://pan.baidu.com/s/1ADe9Db5ZbcKC4I_V2RiwDw 密码:6ean 199 | 200 | - 最新 SpringCloud视频 201 | 202 | 链接: https://pan.baidu.com/s/1nB23cEOZJmbCkJebAU4hCg 密码:w4vq 203 | 204 | - 最新 RBAC权限实战项目 205 | 206 | 链接: https://pan.baidu.com/s/1jsLd7V2CPiC9uoWOctWpQg 密码:6vby 207 | 208 | - 最新 Git&GitHup视频 209 | 210 | 链接: https://pan.baidu.com/s/1JDQKVDtdK7_Tiw3Tejottg 密码:2mvj 211 | 212 | - 最新 Jenkins视频 213 | 214 | 链接: https://pan.baidu.com/s/1WKDJpwKmJBUi8FYBe_9r1g 密码:9jbl 215 | 216 | - 最新 MyBatisPlus视频 217 | 218 | 链接: https://pan.baidu.com/s/1XVX5rwawjIjQe5pO79HYZg 密码:0ba6 219 | 220 | - 最新 Docker核心技术 221 | 222 | 链接: https://pan.baidu.com/s/17rJ5ME_7p804Nh-Qcc0T6A 密码:5pmr 223 | 224 | - 最新 Dubbo视频教程 225 | 226 | 链接: https://pan.baidu.com/s/1Iv4zxskhHBHGiBD4e2-5dQ 密码:22nr 227 | 228 | --- 229 | 230 | ## 6.网络通信框架 231 | 232 | **dubbo分布式服务框架** 233 | 234 | -dubbo官方文档:http://dubbo.io/Developer+Guide-zh.htm 235 | 236 | -dubbo开源地址:https://github.com/alibaba/dubbo 237 | 238 | -dubbo入门:http://blog.csdn.net/top_code/article/details/51010614 239 | 240 | -dubbo集成到springboot:https://github.com/teaey/spring-boot-starter-dubbo 241 | 242 | -dubbo架构设计详解:http://shiyanjun.cn/archives/325.html 243 | 244 | **zookeeper分布式应用程序协调服务** 245 | 246 | -官方文档:http://zookeeper.apache.org 247 | 248 | -开源地址:https://github.com/apache/zookeeper 249 | 250 | -安装部署:http://coolxing.iteye.com/blog/1871009 251 | 252 | -架构原理:http://blog.csdn.net/xhh198781/article/details/10949697 253 | 254 | **netty网络应用通讯框架** 255 | 256 | -官方文档:http://netty.io/wiki/index.html 257 | 258 | -开源地址:https://github.com/netty/netty 259 | 260 | -开源案例:https://github.com/blynkkk/blynk-server 261 | 262 | -原理实现讲解:http://www.infoq.com/cn/articles/netty-high-performance 263 | 264 | 265 | **MQ消息队列** 266 | 267 | -什么是消息队列:http://blog.csdn.net/shaobingj126/article/details/50585035 268 | 269 | -各种消息队列框架对比:http://blog.csdn.net/sunxinhere/article/details/7968886 270 | 271 | -RocketMQ原理与实践:http://www.jianshu.com/p/453c6e7ff81c 272 | 273 | -RocketMQ入门:http://www.jianshu.com/p/ba2934571c77 274 | 275 | **序列化框架** 276 | 277 | -开源地址: https://github.com/google/protobuf 278 | 279 | -开发指南:http://blog.csdn.net/menuconfig/article/details/12837173 280 | 281 | -安装教程:http://www.cnblogs.com/TerryBlog/archive/2011/04/20/2022502.html 282 | 283 | -netty中使用protobuf:https://github.com/longdafeng/netty-protobuff 284 | 285 | --- 286 | 287 | ## 7. 项目实战 288 | 289 | -ssm整合: 290 | 291 | 链接:https://pan.baidu.com/s/1VPKg3J2qKAbhIaDnOZGwaQ 密码:mnvo 292 | 293 | - javaweb网上商城: 294 | 295 | 链接:https://pan.baidu.com/s/1iqjQ3g2f1HYhECRZJZJEtg 密码:nrig 296 | 297 | - 尚学堂Spring4: 298 | 299 | 链接:https://pan.baidu.com/s/1Wu9mxJ6WtAniw3OS8S9EZA 密码:gxph 300 | 301 | - 尚学堂SpringMvc: 302 | 303 | 链接:https://pan.baidu.com/s/1t_bx1yZ1kuOOqtZsQTpitQ 密码:taie 304 | 305 | - SSM12天淘淘商城项目: 306 | 307 | 链接:https://pan.baidu.com/s/1hV-KrXK47C2go_2qjX1F7w 密码:oaho 308 | 309 | - 牛客网中级项目: 310 | 311 | 链接:https://pan.baidu.com/s/1k8DKaFBsEGf6_ohnvbPRIQ 密码:4l69 312 | 313 | - 牛客网高级项目: 314 | 315 | 链接:https://pan.baidu.com/s/1khlaaumttKcddtGq-xazlw 密码:ytw3 316 | 317 | - 传智32期 318 | 319 | 链接:https://pan.baidu.com/s/1iA_vWaknZ4iTcgWrCltC-Q 密码:ldr0 320 | 321 | 322 | 323 | --- 324 | 325 | 326 | ## 8.其他语言 327 | 328 | **区块链教程** 329 | 330 | - 最新Go语言核心编程课程 331 | 332 | 链接: https://pan.baidu.com/s/1QYT1AJmHpuFp7KHcR6Y8gA 密码:cybt 333 | 334 | **Android教程** 335 | 336 | - Android核心基础_15天精讲精练 https://pan.baidu.com/s/1b86u2E 337 | - Android自定义控件视频 https://pan.baidu.com/s/1hrOVZd6 338 | - Android—JNI视频 http://pan.baidu.com/s/1kVqBCmr 339 | - Android与H5互调 https://pan.baidu.com/s/1miHaDbM 340 | - Android常用第三方框架源码分析 http://pan.baidu.com/s/1o789Vjc 341 | - Android视频《多渠道打包》 http://pan.baidu.com/s/1dEVpQyX 342 | 343 | **Android项目实战** 344 | 345 | - Android项目实战—手机影音 http://pan.baidu.com/s/1i5wLMbN 346 | - 最新Android项目—硅谷新闻 https://pan.baidu.com/s/1nvASXvF 347 | - 最新Android项目实战—硅谷社交 https://pan.baidu.com/s/1dFyXZxR 348 | - 最新Android项目—硅谷商城[新] http://pan.baidu.com/s/1o8MyptC 349 | - 最新Android项目—硅谷P2P金融 https://pan.baidu.com/s/1KJbXUd3ymMhmJUPCqT9gzA 350 | 351 | **Android进阶** 352 | 353 | - Android_软件框架搭建 https://pan.baidu.com/s/1hsFIYig 354 | - Android_OKHttp使用方法 https://pan.baidu.com/s/1c5McVW 355 | - Android_JSON解析 http://pan.baidu.com/s/1c23eePE 356 | - Android_xUtils3 https://pan.baidu.com/s/1nvGsExF 357 | - Android_Afinal http://pan.baidu.com/s/1c7lXH8 358 | - Android_Volley http://pan.baidu.com/s/1jIkBalg 359 | - Android_ButterKnife http://pan.baidu.com/s/1pKOgh9x 360 | - Android_EventBus http://pan.baidu.com/s/1qXYTyA4 361 | - Android_ImageLoader http://pan.baidu.com/s/1o7DsPmy 362 | - Android_Picasso http://pan.baidu.com/s/1c1JITo8 363 | - Android_Glide http://pan.baidu.com/s/1hswlhu0 364 | - Android_Fresco http://pan.baidu.com/s/1qXHtwdA 365 | - Android_RecyclerView http://pan.baidu.com/s/1kVjTLJ5 366 | - Android_Pulltorefresh http://pan.baidu.com/s/1c20xVm4 367 | - Android_UniversalVideoView http://pan.baidu.com/s/1mhEK9EK 368 | - Android_JieCaoVideoPlayer https://pan.baidu.com/s/1geZZ1Ov 369 | - Android_Banner https://pan.baidu.com/s/1nv2jpDB 370 | - CountdownView秒杀 https://pan.baidu.com/s/1nvAWFMT 371 | - OpenDanmaku弹幕 https://pan.baidu.com/s/1eS2x2Hc 372 | - TabLayout&ViewPager https://pan.baidu.com/s/1mhCKJag#list/path=%2F 373 | 374 | **Python教程** 375 | 376 | - 最新 Python核心基础视频:https://pan.baidu.com/s/1LVvNP5NPgE-IxyhW6KgDqA 密码:b2ma 377 | - 最新 Python项目之谷粒教育:: https://pan.baidu.com/s/1GocU4kmLxup3Pf7y4O1nfA 提取码:7ap5 378 | - 最新 AI人工智能视频: https://pan.baidu.com/s/16VDApDKAd6P0RKznbPLqrg 提取码:glin 379 | - Python链接:h链接:链接:https://pan.baidu.com/s/1scBXSLdiTs9SBixdkykflw 密码:ck51 380 | - Python链接:链接:https://pan.baidu.com/s/1m4cMqkOYRUFv-2T5Rm6Qmg 密码: 密码:2kpz 381 | 382 | 383 | --- 384 | 385 | 386 | ## 9.公开课教程 387 | 388 | - 横扫Java基础核心技术 https://pan.baidu.com/s/1cGZpyY 389 | - Java基础加强 https://pan.baidu.com/s/1qXNcgpu 390 | - 数据库关键技术 https://pan.baidu.com/s/1dEHsT0H 391 | - 光棍节,4晚搞定面向对象 https://pan.baidu.com/s/1skTfGyp 392 | - Java就业面试攻略(含:简历模板、面试技巧) https://pan.baidu.com/s/1hsQWvVQ 393 | - JavaWeb书城实战 https://pan.baidu.com/s/1dFOuFbb 394 | - Android从入门到实战 https://pan.baidu.com/s/1pL7LnUN 395 | - 锋利的JavaScript https://pan.baidu.com/s/1bKgJxW 396 | - 从容面对Java基础笔试&面试 https://pan.baidu.com/s/1slVxKjN 397 | - 最流行的JS框架_jQuery https://pan.baidu.com/s/1dEX7Ozv 398 | - Android实战_来电拦截专家 https://pan.baidu.com/s/1cAnHam 399 | - Java基础实战_战队组建管理系统 https://pan.baidu.com/s/1o7TlL2a 400 | - 深入解析Ajax网页无刷新技术与项目实战 https://pan.baidu.com/s/1hs7Z3cK 401 | - 30分钟打造Android万能播放器 https://pan.baidu.com/s/1gfq0FS3 402 | - Android高薪就业攻略 https://pan.baidu.com/s/1jIEbd4a 403 | - 客户信息管理系统 https://pan.baidu.com/s/1c2niZJE 404 | - Android全套视频-学习指导&答疑 https://pan.baidu.com/s/1b9X0Sm 405 | - Android Studio入门及使用技巧 https://pan.baidu.com/s/1nv63xHZ 406 | - 玩转Android与H5互调 https://pan.baidu.com/s/1dFo2PNF 407 | - HTML5实战_天猫商城品牌墙 https://pan.baidu.com/s/1hr4v8Pm 408 | - 全栈开发_node服务端开发 https://pan.baidu.com/s/1hrOEuuK 409 | - 实战:360度全景图片 https://pan.baidu.com/s/1hsKkPJQ 410 | - HTML5特效实战 https://pan.baidu.com/s/1kVBrpZp 411 | - 3小时玩转微信小程序入门 https://pan.baidu.com/s/1eUnMTii 412 | - CSS3特效实战 https://pan.baidu.com/s/1dESOjFr 413 | - 轻松搞定毕业设计:论文写作+项目实战 https://pan.baidu.com/s/1eS2DVjW 414 | - Java8新特性全剖析 https://pan.baidu.com/s/1boL0IMr 415 | - BAT前端面试揭秘 https://pan.baidu.com/s/1i4WdO4t 416 | - 1小时带你走进大数据世界 https://pan.baidu.com/s/1c4NYTC 417 | - 大数据项目实战--仿天猫用户行为分析 https://pan.baidu.com/s/1dEKGHQL 418 | - 如何做互联网时代的“出彩”Java工程师 https://pan.baidu.com/s/1pRTDurhOPJS-_-41TSyvOg 419 | - 下一个风口--Python与人工智能 https://pan.baidu.com/s/1nuS7Qwp 420 | - 1小时解密程序员的黑魔法Python https://pan.baidu.com/s/1kV9Voyj 421 | - 1小时参悟Java8面向对象 https://pan.baidu.com/s/1hsAHvbI 422 | - 更好的Java IDE之争:IDEA挑战Eclipse https://pan.baidu.com/s/1c2LEPJ6 423 | - Python学员作品之《雷电战机》 https://pan.baidu.com/s/1o7Ha6eA 424 | - HTML5实战之Canvas刮刮卡 https://pan.baidu.com/s/1ge7Lw9l 425 | - 强大的R语言 https://pan.baidu.com/s/1gfENmeR 426 | - 毕业的三岔口 https://pan.baidu.com/s/1o8qmSlk 427 | - 模块化打包神器:pack http://pan.baidu.com/s/1nvxHvnz 428 | - 快速入门JVM http://pan.baidu.com/s/1pKHalAz 429 | - 抛开噱头,看大数据与人工智能 https://pan.baidu.com/s/1c20dj1m 430 | - Java8&9就业面试攻略 https://pan.baidu.com/s/1nv45pZr 431 | - 大话浏览器渲染原理 https://pan.baidu.com/s/1HJbyPLNDi3aw0dB7vLlWYg 432 | - Python核心语法实战:学生管理系统 https://pan.baidu.com/s/14tDNHX6rpsWUlQtFAGUq5w 433 | - 移动端重力感应:摇一摇的实现 https://pan.baidu.com/s/1oGJSWf_RaPdKXP8vMllnCQ 434 | - Spring Boot实战/Spring Cloud https://pan.baidu.com/s/1RvuG5-ArHwA7e8DeaheL-A 435 | - 大数据架构师课程:高薪实战课程 https://pan.baidu.com/s/1QvkUkEvtMA29bLBFIZ-hJw 436 | - 大数据项目实战--智慧出行 https://pan.baidu.com/s/1y-RefkxcEHo7ZjRgL1Jq1A 437 | - 从比特币到区块链 https://pan.baidu.com/s/10MDp4MvcjAsz5OQnaY6o0w 438 | 439 | ## 文末 440 | 以上均来自网络,整理出来的学习资料。如果对你有帮助,不妨点波关注哦~ 公众号:`Coder编程` 441 | ![微信公众号](https://img-blog.csdnimg.cn/20190418222324896.jpg) -------------------------------------------------------------------------------- /src/17-other/pay-image/README.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,欢迎关注微信公众号:Coder编程。内容将持续更新! # 2 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # :mega: 谢谢关注,内容正在努力加载中...... # 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/UpdateLog.md: -------------------------------------------------------------------------------- 1 | # :mega: 更新日志 # 2 | 3 | > 2019.3.15 4 | 5 | 初始化项目工程 6 | 7 | > 2019.3.19 8 | 9 | [通过面试题,让我们来了解Collection](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483669&idx=1&sn=b726d81212af6a857d59cd488c0e0aa2&chksm=96e67006a191f9103b3c790e04d5ed4ddcbc76f3efbb52e6ce8349619f56a68e72aca156571e&token=424171447&lang=zh_CN#rd) 10 | 11 | > 2019.3.21 12 | 13 | [一篇让你理解进程与线程的区别与联系](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483676&idx=1&sn=9033af3cb37754446779e1fcd89e3329&chksm=96e6700fa191f91919b4c2a46a8a99a7a7dda037181e97b5377835500e99a5f66cb1d3337898&token=424171447&lang=zh_CN#rd) 14 | 15 | 16 | > 2019.3.25 17 | 18 | [聊一聊Iterable与Iterator的那些事!](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483690&idx=1&sn=2cebca2e43e329d428263b0b8a801047&chksm=96e67039a191f92fd5fe353f70d7ef4767eb52af752a9a53bc57db61f389a1f0a1b5f218a2f0&token=424171447&lang=zh_CN#rd) 19 | 20 | > 2019.3.29 21 | 22 | [通过“表白”的方式,让我们快速了解网络协议](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483701&idx=1&sn=b21d65f8ba4ae7f861a6e6175be2303c&chksm=96e67026a191f930c540a8c823c6ad5355dc4cb92824eadc9485aa195167768560dc506af358&token=1104592742&lang=zh_CN&scene=21#wechat_redirect) 23 | 24 | > 2019.3.30 25 | 26 | [一篇带你读懂TCP之“滑动窗口”协议 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483706&idx=1&sn=8eed9d160013bd8ed6203ad511711553&chksm=96e67029a191f93fdd1543af2bf06025397d9c3bd0f0692c7fe247ab9c139cd869d69ab05498&token=1104592742&lang=zh_CN#rd) 27 | 28 | 29 | > 2019.4.07 30 | 31 | [带你了解数据库中JOIN的用法 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483713&idx=1&sn=d61ad0aed42dc36d64d17732db352288&chksm=96e67052a191f9445bbe3d5825ce547ad3171c3874b571a93b97977d0668413e37a164c3e0bc&token=1144933717&lang=zh_CN#rd) 32 | 33 | > 2019.4.08 34 | 35 | [ 带你了解数据库中group by的用法 ](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247483717&idx=1&sn=157a8a021c29043a10480d0294b39ca0&chksm=96e67056a191f940668812ebb092fe9984b22eb619a18339cc052e1051c659a7e9d907c48814&token=1144933717&lang=zh_CN#rd) -------------------------------------------------------------------------------- /src/temp.md: -------------------------------------------------------------------------------- 1 | 2 | ![图片](xxxx.png) 3 | ## 前言 4 | 在上一个章节xxx,本章节接下来将主要介绍xxxx的相关知识。如有错误还请大家及时指出~ 5 | 6 | >本文已同步至 [GitHub](https://github.com/CoderMerlin/coder-programming)/[Gitee](https://gitee.com/573059382/coder-programming)/公众号,感兴趣的同学帮忙点波关注~ 7 | 8 | 问题: 9 | 10 | - xxxxx? 11 | - xxxxx? 12 | 13 | 14 | ## 正文 15 | 16 | 17 | ## 文末 18 | 19 | 本章节主要简单介绍了xxx 的相关知识,后续我们将会继续xxx。 20 | 21 | >欢迎关注公众号:**Coder编程** 22 | 获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识! 23 | 24 | 参考文章: 25 | 26 | xxxxx 27 | 28 | xxxxx 29 | 30 | 31 | ![微信公众号](https://user-gold-cdn.xitu.io/2019/4/16/16a26835c75c12fc?w=300&h=390&f=png&s=18217) 32 | 33 | ## 推荐阅读 34 | 35 | xxxxx 36 | 37 | xxxxx 38 | 39 | xxxxx --------------------------------------------------------------------------------