提交 97ea291d 编写于 作者: G guide

[refractor]delete some unused blogs

上级 3900388e
此差异已折叠。
......@@ -83,14 +83,14 @@ JDK 1.5 以后的 `AtomicStampedReference 类`就提供了此种能力,其中
CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。但是从 JDK 1.5开始,提供了`AtomicReference类`来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用`AtomicReference类`把多个共享变量合并成一个共享变量来操作。
### CAS与synchronized的使用情景
### CAS与`synchronized`的使用情景
> **简单的来说CAS适用于写比较少的情况下(多读场景,冲突一般较少),synchronized适用于写比较多的情况下(多写场景,冲突一般较多)**
1. 对于资源竞争较少(线程冲突较轻)的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。
1. 对于资源竞争较少(线程冲突较轻)的情况,使用`synchronized`同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。
2. 对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。
补充: Java并发编程这个领域中synchronized关键字一直都是元老级的角色,很久之前很多人都会称它为 **“重量级锁”** 。但是,在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的 **偏向锁****轻量级锁** 以及其它**各种优化**之后变得在某些情况下并不是那么重了。synchronized的底层实现主要依靠 **Lock-Free** 的队列,基本思路是 **自旋后阻塞****竞争切换后继续竞争锁****稍微牺牲了公平性,但获得了高吞吐量**。在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。
补充: Java并发编程这个领域中`synchronized`关键字一直都是元老级的角色,很久之前很多人都会称它为 **“重量级锁”** 。但是,在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的 **偏向锁****轻量级锁** 以及其它**各种优化**之后变得在某些情况下并不是那么重了。`synchronized`的底层实现主要依靠 **Lock-Free** 的队列,基本思路是 **自旋后阻塞****竞争切换后继续竞争锁****稍微牺牲了公平性,但获得了高吞吐量**。在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。
## 公众号
......
本文数据统计于 1.1 号凌晨,由 SnailClimb 整理。
### 1. JavaGuide
- **Github地址**[https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide)
- **star**: 18.2k
- **介绍**: 【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。
### 2. mall
- **Github地址**[https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
- **star**: 3.3k
- **介绍**: mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
### 3. advanced-java
- **Github地址**[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
- **star**: 3.3k
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲
### 4. matrix
- **Github地址**[https://github.com/Tencent/matrix](https://github.com/Tencent/matrix)
- **star**: 2.5k
- **介绍**: Matrix 是一款微信研发并日常使用的 APM(Application Performance Manage),当前主要运行在 Android 平台上。 Matrix 的目标是建立统一的应用性能接入框架,通过各种性能监控方案,对性能监控项的异常数据进行采集和分析,输出相应的问题分析、定位与优化建议,从而帮助开发者开发出更高质量的应用。
### 5. miaosha
- **Github地址**[https://github.com/qiurunze123/miaosha](https://github.com/qiurunze123/miaosha)
- **star**: 2.4k
- **介绍**: 高并发大流量如何进行秒杀架构,我对这部分知识做了一个系统的整理,写了一套系统。
### 6. arthas
- **Github地址**[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas)
- **star**: 8.2k
- **介绍**: Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。
### 7 spring-boot
- **Github地址**[https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot)
- **star:** 32.6k
- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。
**关于Spring Boot官方的介绍:**
> Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可)
### 8. tutorials
- **Github地址**[https://github.com/eugenp/tutorials](https://github.com/eugenp/tutorials)
- **star**: 10k
- **介绍**: 该项目是一系列小而专注的教程 - 每个教程都涵盖Java生态系统中单一且定义明确的开发领域。 当然,它们的重点是Spring Framework - Spring,Spring Boot和Spring Securiyt。 除了Spring之外,还有以下技术:核心Java,Jackson,HttpClient,Guava。
### 9. qmq
- **Github地址**[https://github.com/qunarcorp/qmq](https://github.com/qunarcorp/qmq)
- **star**: 1.1k
- **介绍**: QMQ是去哪儿网内部广泛使用的消息中间件,自2012年诞生以来在去哪儿网所有业务场景中广泛的应用,包括跟交易息息相关的订单场景; 也包括报价搜索等高吞吐量场景。
### 10. symphony
- **Github地址**[https://github.com/b3log/symphony](https://github.com/b3log/symphony)
- **star**: 9k
- **介绍**: 一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)平台。
### 11. incubator-dubbo
- **Github地址**[https://github.com/apache/incubator-dubbo](https://github.com/apache/incubator-dubbo)
- **star**: 23.6k
- **介绍**: 阿里开源的一个基于Java的高性能开源RPC框架。
### 12. apollo
- **Github地址**[https://github.com/ctripcorp/apollo](https://github.com/ctripcorp/apollo)
- **star**: 10k
- **介绍**: Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
### 1. JavaGuide
- **Github地址**[https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide)
- **star**: 22.8k
- **介绍**: 【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。
### 2. advanced-java
- **Github地址**[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
- **star**: 7.9k
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲
### 3. fescar
- **Github地址**[https://github.com/alibaba/fescar](https://github.com/alibaba/fescar)
- **star**: 4.6k
- **介绍**: 具有 **高性能****易用性****微服务架构****分布式事务** 的解决方案。(特点:高性能且易于使用,旨在实现简单并快速的事务提交与回滚。
### 4. mall
- **Github地址**[https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
- **star**: 5.6 k
- **介绍**: mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
### 5. miaosha
- **Github地址**[https://github.com/qiurunze123/miaosha](https://github.com/qiurunze123/miaosha)
- **star**: 4.4k
- **介绍**: 高并发大流量如何进行秒杀架构,我对这部分知识做了一个系统的整理,写了一套系统。
### 6. flink
- **Github地址**[https://github.com/apache/flink](https://github.com/apache/flink)
- **star**: 7.1 k
- **介绍**: Apache Flink是一个开源流处理框架,具有强大的流和批处理功能。
### 7. cim
- **Github地址**[https://github.com/crossoverJie/cim](https://github.com/crossoverJie/cim)
- **star**: 1.8 k
- **介绍**: cim(cross IM) 适用于开发者的即时通讯系统。
### 8. symphony
- **Github地址**[https://github.com/b3log/symphony](https://github.com/b3log/symphony)
- **star**: 10k
- **介绍**: 一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)平台。
### 9. spring-boot
- **Github地址**[https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot)
- **star:** 32.6k
- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。
**关于Spring Boot官方的介绍:**
> Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可)
### 10. arthas
- **Github地址**[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas)
- **star**: 9.5k
- **介绍**: Arthas 是Alibaba开源的Java诊断工具。
**概览:**
当你遇到以下类似问题而束手无策时,`Arthas`可以帮助你解决:
0. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
1. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
2. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
3. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
4. 是否有一个全局视角来查看系统的运行状况?
5. 有什么办法可以监控到JVM的实时运行状态?
`Arthas`支持JDK 6+,支持Linux/Mac/Winodws,采用命令行交互模式,同时提供丰富的 `Tab` 自动补全功能,进一步方便进行问题的定位和诊断。
# 年末将至,值得你关注的16个Java 开源项目!
Star 的数量统计于 2019-12-29。
### 1.JavaGuide
Guide 哥大三开始维护的,目前算是纯 Java 类型项目中 Star 数量最多的项目了。但是,本仓库的价值远远(+N次 )比不上像 Spring Boot、Elasticsearch 等等这样非常非常非常优秀的项目。希望以后我也有能力为这些项目贡献一些有价值的代码。
- **Github 地址**<https://github.com/Snailclimb/JavaGuide>
- **Star**: 66.3k
- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。
### 2.java-design-patterns
感觉还不错。根据官网介绍:
> 设计模式是程序员在设计应用程序或系统时可以用来解决常见问题的最佳形式化实践。 设计模式可以通过提供经过测试的,经过验证的开发范例来加快开发过程。 重用设计模式有助于防止引起重大问题的细微问题,并且还可以提高熟悉模式的编码人员和架构师的代码可读性。
![java-design-patterns-website](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/java-design-patterns-website.jpg)
- **Github 地址** : [https://github.com/iluwatar/java-design-patterns](https://github.com/iluwatar/java-design-patterns)
- **Star**: 53.8k
- **介绍**: 用 Java 实现的设计模式。[https://java-design-patterns.com](https://java-design-patterns.com/)
### 3.elasticsearch
搜索引擎界的扛把子,但不仅仅是搜素引擎那么简单。
- **Github 地址** : [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch)
- **Star**: 46.2k
- **介绍**: 开源,分布式,RESTful 搜索引擎。
### 4.spring-boot
必须好好学啊,一定要好好学!现在 Java 后端新项目有不用 Spring Boot 开发的有吗?如果有的话,请把这个人的联系方式告诉我,我有很多话想给他交流交流!
- **Github地址**[https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot)
- **star:** 34.8k (1,073 stars this month)
- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。
### 5.RxJava
这个没怎么用过,不做太多评价。
- **Github 地址** : [https://github.com/ReactiveX/RxJava](https://github.com/ReactiveX/RxJava)
- **Star**: 41.5k
- **介绍**: `RxJava` 是一个 基于事件流、实现异步操作的库。
### 6.advanced-java
本项目大部分内容来自中华石杉的一个课程,内容涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识,非常不错了!
- **Github 地址**[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
- **Star**: 36.7k
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务等领域知识,后端同学必看,前端同学也可学习。
### 7.mall
很牛逼的实战项目,还附有详细的文档,作为毕设或者练手项目都再好不过了。
- **Github地址**[https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
- **star**: 27.6k
- **介绍**: mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
### 8.okhttp
给我感觉是安卓项目中用的居多。当然,Java 后端项目也会经常用,但是一般使用 Spring Boot 进行开发的时候,如果需要远程调用的话建议使用 Spring 封装的 `RestTemplate `
- **Github地址**[https://github.com/square/okhttp](https://github.com/square/okhttp)
- **star**: 35.4k
- **介绍**: 适用于Android,Kotlin和Java的HTTP客户端。https://square.github.io/okhttp/。
### 9.guava
很厉害很厉害!提供了很多非常实用的工具类、更加实用的集合类、一些常用的数据结构比如布隆过滤器、缓存等等。
- **Github地址**[https://github.com/google/guava](https://github.com/google/guava)
- **star**: 35.3k
- **介绍**: Guava是一组核心库,其中包括新的集合类型(例如 multimap 和 multiset),不可变集合,图形库以及用于并发,I / O,哈希,基元,字符串等的实用程序!
### 10.Spark
我木有用过,留下了没有技术的眼泪。
- **Github地址**[https://github.com/apache/spark](https://github.com/apache/spark)
- **star**: 24.7k
- **介绍**: Spark 是一个快速、通用的大规模数据处理引擎,和Hadoop的MapReduce计算框架类似,但是相对于MapReduce,Spark凭借其可伸缩、基于内存计算等特点,以及可以直接读写Hadoop上任何格式数据的优势,进行批处理时更加高效,并有更低的延迟。
### 11.arthas
虽然我自己没有亲身用过,但是身边用过的朋友评价都还挺好的。根据官网介绍,这家伙可以解决下面这些让人脑壳疼的问题:
1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
5. 是否有一个全局视角来查看系统的运行状况?
6. 有什么办法可以监控到JVM的实时运行状态?
7. 怎么快速定位应用的热点,生成火焰图?
- **Github 地址**[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas)
- **star**: 18.8 k
- **介绍**: Arthas 是 Alibaba 开源的 Java 诊断工具。
### 12.spring-boot-examples
学习 Spring Boot 必备!配合上我的 **springboot-guide**[https://github.com/Snailclimb/springboot-guide](https://github.com/Snailclimb/springboot-guide),效果杠杠滴!
- **Github 地址**[https://github.com/ityouknow/spring-boot-examples](https://github.com/ityouknow/spring-boot-examples)
- **star**: 20.2 k
- **介绍**: Spring Boot 教程、技术栈示例代码,快速简单上手教程。
### 13.lombok
使用 Lombok 我们可以简化我们的 Java 代码,比如使用它之后我们通过注释就可以实现 getter/setter、equals等方法。
- **Github 地址**[https://github.com/rzwitserloot/lombok](https://github.com/rzwitserloot/lombok)
- **star**: 20.2 k
- **介绍**: 对 Java 编程语言的非常刺激的补充。[https://projectlombok.org/](https://projectlombok.org/)
### 14.p3c
与我而言,没有特别惊艳,但是一些提供的一些代码规范确实挺有用的!
- **Github 地址**[https://github.com/alibaba/p3c](https://github.com/alibaba/p3c)
- **star**: 19.8 k
- **介绍**: 阿里巴巴Java编码指南pmd实现和IDE插件。
### 15.spring-boot-demo
- **Github 地址**[https://github.com/xkcoding/spring-boot-demo](https://github.com/xkcoding/spring-boot-demo)
- **Star**: 8.8k
- **介绍**: spring boot demo 是一个用来深度学习并实战 spring boot 的项目。
### 16. awesome-java
Guide 哥半个多月前开始维护的,虽然现在 Star 数量比较少,我相信后面一定会有更多人喜欢上这个项目,我也会继续认真维护下去。
- **Github 地址**[https://github.com/Snailclimb/awesome-java](https://github.com/Snailclimb/awesome-java)
- **Star**: 0.3 k
- **介绍**: Github 上非常棒的 Java 开源项目集合。
### 1. JavaGuide
- **Github地址**[https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide)
- **Star**: 27.2k (4,437 stars this month)
- **介绍**: 【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。
### 2.DoraemonKit
- **Github地址**<https://github.com/didi/DoraemonKit>
- **Star**: 5.2k (3,786 stars this month)
- **介绍**: 简称 "DoKit" 。一款功能齐全的客户端( iOS 、Android )研发助手,你值得拥有。
### 3.advanced-java
- **Github地址**[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
- **Star**:11.2k (3,042 stars this month)
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲。
### 4. spring-boot-examples
- **Github地址**<https://github.com/ityouknow/spring-boot-examples>
- **star**: 9.6 k (1,764 stars this month)
- **介绍**: Spring Boot 教程、技术栈示例代码,快速简单上手教程。
### 5. mall
- **Github地址**[https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
- **star**: 7.4 k (1,736 stars this month)
- **介绍**: mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
### 6. fescar
- **Github地址**[https://github.com/alibaba/fescar](https://github.com/alibaba/fescar)
- **star**: 6.0 k (1,308 stars this month)
- **介绍**: 具有 **高性能****易用性****微服务架构****分布式事务** 的解决方案。(特点:高性能且易于使用,旨在实现简单并快速的事务提交与回滚。)
### 7. h4cker
- **Github地址**<https://github.com/The-Art-of-Hacking/h4cker>
- **star**: 2.1 k (1,303 stars this month)
- **介绍**: 该仓库主要由Omar Santos维护,包括与道德黑客/渗透测试,数字取证和事件响应(DFIR),漏洞研究,漏洞利用开发,逆向工程等相关的资源。
### 8. spring-boot
- **Github地址**[https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot)
- **star:** 34.8k (1,073 stars this month)
- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。
**关于Spring Boot官方的介绍:**
> Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可)
### 9. arthas
- **Github地址**[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas)
- **star**: 10.5 k (970 stars this month)
- **介绍**: Arthas 是Alibaba开源的Java诊断工具。
### 10. tutorials
- **Github地址**[https://github.com/eugenp/tutorials](https://github.com/eugenp/tutorials)
- **star**: 12.1 k (789 stars this month)
- **介绍**: 该项目是一系列小而专注的教程 - 每个教程都涵盖Java生态系统中单一且定义明确的开发领域。 当然,它们的重点是Spring Framework - Spring,Spring Boot和Spring Securiyt。 除了Spring之外,还有以下技术:核心Java,Jackson,HttpClient,Guava。
### 1. JavaGuide
- **Github 地址**[https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide)
- **Star**: 32.9k (6,196 stars this month)
- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。
### 2.advanced-java
- **Github 地址**[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
- **Star**: 15.1k (4,012 stars this month)
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲。
### 3.spring-boot-examples
- **Github 地址**[https://github.com/ityouknow/spring-boot-examples](https://github.com/ityouknow/spring-boot-examples)
- **Star**: 12.8k (3,462 stars this month)
- **介绍**: Spring Boot 教程、技术栈示例代码,快速简单上手教程。
### 4. mall
- **Github 地址**[https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
- **star**: 9.7 k (2,418 stars this month)
- **介绍**: mall 项目是一套电商系统,包括前台商城系统及后台管理系统,基于 SpringBoot+MyBatis 实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
### 5. seata
- **Github 地址** : [https://github.com/seata/seata](https://github.com/seata/seata)
- **star**: 7.2 k (1359 stars this month)
- **介绍**: Seata 是一种易于使用,高性能,基于 Java 的开源分布式事务解决方案。
### 6. quarkus
- **Github 地址**[https://github.com/quarkusio/quarkus](https://github.com/quarkusio/quarkus)
- **star**: 12 k (1,224 stars this month)
- **介绍**: Quarkus 是为 GraalVM 和 HotSpot 量身定制的 Kubernetes Native Java 框架,由最佳的 Java 库和标准精心打造而成。Quarkus 的目标是使 Java 成为 Kubernetes 和无服务器环境中的领先平台,同时为开发人员提供统一的反应式和命令式编程模型,以优化地满足更广泛的分布式应用程序架构。
### 7. arthas
- **Github 地址**[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas)
- **star**: 11.6 k (1,199 stars this month)
- **介绍**: Arthas 是 Alibaba 开源的 Java 诊断工具。
### 8.DoraemonKit
- **Github 地址**<https://github.com/didi/DoraemonKit>
- **Star**: 6.2k (1,177 stars this month)
- **介绍**: 简称 "DoKit" 。一款功能齐全的客户端( iOS 、Android )研发助手,你值得拥有。
### 9.elasticsearch
- **Github 地址** [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch)
- **Star**: 39.7k (1,069 stars this month)
- **介绍**: 开源,分布式,RESTful 搜索引擎。
### 10. tutorials
- **Github 地址**[https://github.com/eugenp/tutorials](https://github.com/eugenp/tutorials)
- **star**: 13 k (998 stars this month)
- **介绍**: 该项目是一系列小而专注的教程 - 每个教程都涵盖 Java 生态系统中单一且定义明确的开发领域。 当然,它们的重点是 Spring Framework - Spring,Spring Boot 和 Spring Securiyt。 除了 Spring 之外,还有以下技术:核心 Java,Jackson,HttpClient,Guava。
以下涉及到的数据统计与 2019 年 5 月 1 日 12 点,数据来源:<https://github.com/trending/java?since=monthly>
下面的内容从 Java 学习文档到最热门的框架再到热门的工具应有尽有,比如下面推荐到的开源项目 Hutool 就是近期比较热门的项目之一,它是 Java 工具包,能够帮助我们简化代码!我觉得下面这些项目对于学习 Java 的朋友还是很有帮助的!
### 1. JavaGuide
- **Github 地址**[https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide)
- **Star**: 37.9k (5,660 stars this month)
- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。
### 2. advanced-java
- **Github 地址**[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
- **Star**: 15.1k (4,654 stars this month)
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲。
### 3. CS-Notes
- **Github 地址**<https://github.com/CyC2018/CS-Notes>
- **Star**: 59.2k (4,012 stars this month)
- **介绍**: 技术面试必备基础知识。
### 4. ghidra
- **Github 地址**<https://github.com/NationalSecurityAgency/ghidra>
- **Star**: 15.0k (2,995 stars this month)
- **介绍**: Ghidra是一个软件逆向工程(SRE)框架。
### 5. mall
- **Github 地址**[https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
- **star**: 11.6 k (2,100 stars this month)
- **介绍**: mall 项目是一套电商系统,包括前台商城系统及后台管理系统,基于 SpringBoot+MyBatis 实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
### 6. ZXBlog
- **Github 地址**<https://github.com/ZXZxin/ZXBlog>
- **star**: 2.1 k (2,086 stars this month)
- **介绍**: 记录各种学习笔记(算法、Java、数据库、并发......)。
### 7.DoraemonKit
- **Github地址**<https://github.com/didi/DoraemonKit>
- **Star**: 7.6k (1,541 stars this month)
- **介绍**: 简称 "DoKit" 。一款功能齐全的客户端( iOS 、Android )研发助手,你值得拥有。
### 8. spring-boot
- **Github地址**[https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot)
- **star:** 37.3k (1,489 stars this month)
- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。
**Spring Boot官方的介绍:**
> Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可)
### 9. spring-boot-examples
- **Github 地址**[https://github.com/ityouknow/spring-boot-examples](https://github.com/ityouknow/spring-boot-examples)
- **Star**: 12.8k (1,453 stars this month)
- **介绍**: Spring Boot 教程、技术栈示例代码,快速简单上手教程。
### 10. seata
- **Github 地址** : [https://github.com/seata/seata](https://github.com/seata/seata)
- **star**: 8.4 k (1441 stars this month)
- **介绍**: Seata 是一种易于使用,高性能,基于 Java 的开源分布式事务解决方案。
### 11. litemall
- **Github 地址**[https://github.com/ityouknow/spring-boot-examples](https://github.com/ityouknow/spring-boot-examples)
- **Star**: 6.0k (1,427 stars this month)
- **介绍**: 又一个小商城。litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 + Vue用户移动端。
### 12. skywalking
- **Github 地址**<https://github.com/apache/skywalking>
- **Star**: 8.0k (1,381 stars this month)
- **介绍**: 针对分布式系统的应用性能监控,尤其是针对微服务、云原生和面向容器的分布式系统架构。
### 13. elasticsearch
- **Github 地址** [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch)
- **Star**: 4.0k (1,068stars this month)
- **介绍**: 开源,分布式,RESTful 搜索引擎。
### 14. arthas
- **Github地址**[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas)
- **star**: 12.6 k (1,080 stars this month)
- **介绍**: Arthas 是Alibaba开源的Java诊断工具。
### 15. hutool
- **Github地址**<https://github.com/looly/hutool>
- **star**: 4.5 k (1,031 stars this month)
- **介绍**: Hutool是一个Java工具包,也只是一个工具包,它帮助我们简化每一行代码,减少每一个方法,让Java语言也可以“甜甜的”。Hutool最初是我项目中“util”包的一个整理,后来慢慢积累并加入更多非业务相关功能,并广泛学习其它开源项目精髓,经过自己整理修改,最终形成丰富的开源工具集。官网:<https://www.hutool.cn/>
\ No newline at end of file
以下涉及到的数据统计与 2019 年 6 月 1 日 18 点,数据来源:<https://github.com/trending/java?since=monthly> 。下面推荐的内容从 Java 学习文档到最热门的框架再到热门的工具应有尽有,建议收藏+在看!
### 1.LeetCodeAnimation
- **Github 地址**<https://github.com/MisterBooo/LeetCodeAnimation>
- **Star**: 29.0k (11,492 stars this month)
- **介绍**: Demonstrate all the questions on LeetCode in the form of animation.(用动画的形式呈现解LeetCode题目的思路)。
### 2.CS-Notes
- **Github 地址**<https://github.com/CyC2018/CS-Notes>
- **Star**: 64.4k (5513 stars this month)
- **介绍**: 技术面试必备基础知识、Leetcode 题解、后端面试、Java 面试、春招、秋招、操作系统、计算机网络、系统设计。
### 3.JavaGuide
- **Github 地址**<https://github.com/Snailclimb/JavaGuide>
- **Star**: 42.0k (4,442 stars this month)
- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。
### 4.mall
- **Github 地址**[https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
- **star**: 14.6 k (3,086 stars this month)
- **介绍**: mall 项目是一套电商系统,包括前台商城系统及后台管理系统,基于 SpringBoot+MyBatis 实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
### 5.advanced-java
- **Github 地址**[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
- **Star**: 20.8k (2,394 stars this month)
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲。
### 6.spring-boot
- **Github地址**[https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot)
- **star:** 38.5k (1,339 stars this month)
- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。
**Spring Boot官方的介绍:**
> Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可)
### 7. Java
- **Github 地址**<https://github.com/TheAlgorithms/Java>
- **Star**:14.3k (1,334 stars this month)
- **介绍**: All Algorithms implemented in Java。
### 8.server
- **Github 地址**<https://github.com/wildfirechat/server>
- **star**: 2.2 k (1,275 stars this month)
- **介绍**: 全开源即时通讯(IM)系统。
### 9.litemall
- **Github 地址**<https://github.com/linlinjava/litemall>
- **Star**: 7.1k (1,114 stars this month)
- **介绍**: 又一个小商城。litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 + Vue用户移动端。
### 10.Linkage-RecyclerView
- **Github 地址**<https://github.com/KunMinX/Linkage-RecyclerView>
- **Star**: 10.0k (1,093 stars this month)
- **介绍**: 即使不用饿了么订餐,也请务必收藏好该库!🔥 一行代码即可接入,二级联动订餐列表 - Even if you don't order food by PrubHub, be sure to collect this library, please! 🔥 This secondary linkage list widget can be accessed by only one line of code. Supporting by RecyclerView & AndroidX.
### 11.toBeTopJavaer
- **Github 地址** : <https://github.com/hollischuang/toBeTopJavaer>
- **Star**: 3.3k (1,007 stars this month)
- **介绍**: To Be Top Javaer - Java工程师成神之路
### 12.elasticsearch
- **Github 地址** : [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch)
- **Star**: 48.0k (968 stars this month)
- **介绍**: 开源,分布式,RESTful 搜索引擎。
### 13.java-design-patterns
- **Github 地址** : <https://github.com/iluwatar/java-design-patterns>
- **Star**: 41.5k (955 stars this month)
- **介绍**: Design patterns implemented in Java。
### 14.apollo
- **Github 地址** : <https://github.com/ctripcorp/apollo>
- **Star**: 14.5k (927 stars this month)
- **介绍**: Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
### 15.arthas
- **Github地址**[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas)
- **star**: 13.5 k (933 stars this month)
- **介绍**: Arthas 是Alibaba开源的Java诊断工具。
### 16.dubbo
- **Github地址**<https://github.com/apache/dubbo>
- **star**: 26.9 k (769 stars this month)
- **介绍**: Apache Dubbo是一个基于Java的高性能开源RPC框架。
### 17.DoraemonKit
- **Github地址**<https://github.com/didi/DoraemonKit>
- **Star**: 8.5k (909 stars this month)
- **介绍**: 简称 "DoKit" 。一款功能齐全的客户端( iOS 、Android )研发助手,你值得拥有。
### 18.halo
- **Github地址**<https://github.com/halo-dev/halo>
- **Star**: 4.1k (829 stars this month)
- **介绍**: Halo 可能是最好的 Java 博客系统。
### 19.seata
- **Github 地址** : [https://github.com/seata/seata](https://github.com/seata/seata)
- **star**: 9.2 k (776 stars this month)
- **介绍**: Seata 是一种易于使用,高性能,基于 Java 的开源分布式事务解决方案。
### 20.hutool
- **Github地址**<https://github.com/looly/hutool>
- **star**: 5,3 k (812 stars this month)
- **介绍**: Hutool是一个Java工具包,也只是一个工具包,它帮助我们简化每一行代码,减少每一个方法,让Java语言也可以“甜甜的”。Hutool最初是我项目中“util”包的一个整理,后来慢慢积累并加入更多非业务相关功能,并广泛学习其它开源项目精髓,经过自己整理修改,最终形成丰富的开源工具集。官网:<https://www.hutool.cn/>
\ No newline at end of file
### 1.CS-Notes
- **Github 地址**:https://github.com/CyC2018/CS-Notes
- **Star**: 69.8k
- **介绍**: 技术面试必备基础知识、Leetcode 题解、后端面试、Java 面试、春招、秋招、操作系统、计算机网络、系统设计。
### 2.toBeTopJavaer
- **Github 地址:**[https://github.com/hollischuang/toBeTopJavaer](https://github.com/hollischuang/toBeTopJavaer)
- **Star**: 4.7k
- **介绍**: To Be Top Javaer - Java工程师成神之路。
### 3.p3c
- **Github 地址:** [https://github.com/alibaba/p3c](https://github.com/alibaba/p3c)
- **Star**: 16.6k
- **介绍**: Alibaba Java Coding Guidelines pmd implements and IDE plugin。Eclipse 和 IDEA 上都有该插件,推荐使用!
### 4.SpringCloudLearning
- **Github 地址:** [https://github.com/forezp/SpringCloudLearning](https://github.com/forezp/SpringCloudLearning)
- **Star**: 8.7k
- **介绍**: 史上最简单的Spring Cloud教程源码。
### 5.dubbo
- **Github地址**<https://github.com/apache/dubbo>
- **star**: 27.6 k
- **介绍**: Apache Dubbo是一个基于Java的高性能开源RPC框架。
### 6.jeecg-boot
- **Github地址**[https://github.com/zhangdaiscott/jeecg-boot](https://github.com/zhangdaiscott/jeecg-boot)
- **star**: 3.3 k
- **介绍**: 一款基于代码生成器的JAVA快速开发平台!全新架构前后端分离:SpringBoot 2.x,Ant Design&Vue,Mybatis,Shiro,JWT。强大的代码生成器让前后端代码一键生成,无需写任何代码,绝对是全栈开发福音!! JeecgBoot的宗旨是提高UI能力的同时,降低前后分离的开发成本,JeecgBoot还独创在线开发模式,No代码概念,一系列在线智能开发:在线配置表单、在线配置报表、在线设计流程等等。
### 7.advanced-java
- **Github 地址**[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
- **Star**: 24.2k
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务等领域知识,后端同学必看,前端同学也可学习。
### 8.FEBS-Shiro
- **Github 地址**[https://github.com/wuyouzhuguli/FEBS-Shiro](https://github.com/wuyouzhuguli/FEBS-Shiro)
- **Star**: 2.6k
- **介绍**: Spring Boot 2.1.3,Shiro1.4.0 & Layui 2.5.4 权限管理系统。预览地址:http://49.234.20.223:8080/login。
### 9.SpringAll
- **Github 地址**: [https://github.com/wuyouzhuguli/SpringAll](https://github.com/wuyouzhuguli/SpringAll)
- **Star**: 5.4k
- **介绍**: 循序渐进,学习Spring Boot、Spring Boot & Shiro、Spring Cloud、Spring Security & Spring Security OAuth2,博客Spring系列源码。
### 10.JavaGuide
- **Github 地址**<https://github.com/Snailclimb/JavaGuide>
- **Star**: 47.2k
- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。
### 11.vhr
- **Github 地址**[https://github.com/lenve/vhr](https://github.com/lenve/vhr)
- **Star**: 4.9k
- **介绍**: 微人事是一个前后端分离的人力资源管理系统,项目采用SpringBoot+Vue开发。
### 12. tutorials
- **Github 地址**[https://github.com/eugenp/tutorials](https://github.com/eugenp/tutorials)
- **star**: 15.4 k
- **介绍**: 该项目是一系列小而专注的教程 - 每个教程都涵盖 Java 生态系统中单一且定义明确的开发领域。 当然,它们的重点是 Spring Framework - Spring,Spring Boot 和 Spring Securiyt。 除了 Spring 之外,还有以下技术:核心 Java,Jackson,HttpClient,Guava。
### 13.EasyScheduler
- **Github 地址**[https://github.com/analysys/EasyScheduler](https://github.com/analysys/EasyScheduler)
- **star**: 1.1 k
- **介绍**: Easy Scheduler是一个分布式工作流任务调度系统,主要解决“复杂任务依赖但无法直接监控任务健康状态”的问题。Easy Scheduler以DAG方式组装任务,可以实时监控任务的运行状态。同时,它支持重试,重新运行等操作... 。https://analysys.github.io/easyscheduler_docs_cn/
### 14.thingsboard
- **Github 地址**[https://github.com/thingsboard/thingsboard](https://github.com/thingsboard/thingsboard)
- **star**: 3.7 k
- **介绍**: 开源物联网平台 - 设备管理,数据收集,处理和可视化。 [https://thingsboard.io](https://thingsboard.io/)
### 15.mall-learning
- **Github 地址**: [https://github.com/macrozheng/mall-learning](https://github.com/macrozheng/mall-learning)
- **star**: 0.6 k
- **介绍**: mall学习教程,架构、业务、技术要点全方位解析。mall项目(16k+star)是一套电商系统,使用现阶段主流技术实现。 涵盖了SpringBoot2.1.3、MyBatis3.4.6、Elasticsearch6.2.2、RabbitMQ3.7.15、Redis3.2、Mongodb3.2、Mysql5.7等技术,采用Docker容器化部署。 https://github.com/macrozheng/mall
### 16. flink
- **Github地址**[https://github.com/apache/flink](https://github.com/apache/flink)
- **star**: 9.3 k
- **介绍**: Apache Flink是一个开源流处理框架,具有强大的流和批处理功能。
### 17.spring-cloud-kubernetes
- **Github地址**[https://github.com/spring-cloud/spring-cloud-kubernetes](https://github.com/spring-cloud/spring-cloud-kubernetes)
- **star**: 1.4 k
- **介绍**: Kubernetes 集成 Spring Cloud Discovery Client, Configuration, etc...
### 18.springboot-learning-example
- **Github地址**[https://github.com/JeffLi1993/springboot-learning-example](https://github.com/JeffLi1993/springboot-learning-example)
- **star**: 10.0 k
- **介绍**: spring boot 实践学习案例,是 spring boot 初学者及核心技术巩固的最佳实践。
### 19.canal
- **Github地址**[https://github.com/alibaba/canal](https://github.com/alibaba/canal)
- **star**: 9.3 k
- **介绍**: 阿里巴巴 MySQL binlog 增量订阅&消费组件。
### 20.react-native-device-info
- **Github地址**[https://github.com/react-native-community/react-native-device-info](https://github.com/react-native-community/react-native-device-info)
- **star**: 4.0 k
- **介绍**: React Native iOS和Android的设备信息。
\ No newline at end of file
- [2018 年 12 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2018-12.md)
- [2019 年 1 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-1.md)
- [2019 年 2 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-2.md)
- [2019 年 3 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-3.md)
- [2019 年 4 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-4.md)
- [2019 年 5 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-5.md)
- [2019 年 6 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-6.md)
## synchronized / Lock
1. JDK 1.5之前
,Java通过
synchronized
关键字来实现
功能
- synchronized是JVM实现的**内置锁**,锁的获取和释放都是由JVM**隐式**实现的
2. JDK 1.5
,并发包中新增了
Lock接口
来实现锁功能
- 提供了与synchronized类似的同步功能,但需要**显式**获取和释放锁
3. Lock同步锁是基于
Java
实现的,而synchronized是基于底层操作系统的
Mutex Lock
实现的
- 每次获取和释放锁都会带来**用户态和内核态的切换**,从而增加系统的**性能开销**
- 在锁竞争激烈的情况下,synchronized同步锁的性能很糟糕
-**JDK 1.5**,在**单线程重复申请锁**的情况下,synchronized锁性能要比Lock的性能**差很多**
4. **JDK 1.6**,Java对synchronized同步锁做了**充分的优化**,甚至在某些场景下,它的性能已经超越了Lock同步锁
## 实现原理
复制
```
public class SyncTest {
public synchronized void method1() {
}
public void method2() {
Object o = new Object();
synchronized (o) {
}
}
}
```
复制
```
$ javac -encoding UTF-8 SyncTest.java
$ javap -v SyncTest
```
### 修饰方法
复制
```
public synchronized void method1();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=0, locals=1, args_size=1
0: return
```
1. JVM使用**ACC_SYNCHRONIZED**访问标识来区分一个方法是否为**同步方法**
2. 在方法调用时,会检查方法是否被设置了
ACC_SYNCHRONIZED
访问标识
- 如果是,执行线程会将先尝试**持有Monitor对象**,再执行方法,方法执行完成后,最后**释放Monitor对象**
### 修饰代码块
复制
```
public void method2();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: new #2 // class java/lang/Object
3: dup
4: invokespecial #1 // Method java/lang/Object."<init>":()V
7: astore_1
8: aload_1
9: dup
10: astore_2
11: monitorenter
12: aload_2
13: monitorexit
14: goto 22
17: astore_3
18: aload_2
19: monitorexit
20: aload_3
21: athrow
22: return
```
1. synchronized修饰同步代码块时,由**monitorenter****monitorexit**指令来实现同步
2. 进入**monitorenter**指令后,线程将**持有****Monitor对象**,进入**monitorexit**指令,线程将**释放****Monitor对象**
### 管程模型
1. JVM中的**同步**是基于进入和退出**管程****Monitor**)对象实现的
2. **每个Java对象实例都会有一个Monitor**,Monitor可以和Java对象实例一起被创建和销毁
3. Monitor是由**ObjectMonitor**实现的,对应[ObjectMonitor.hpp](https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/runtime/objectMonitor.hpp)
4. 当多个线程同时访问一段同步代码时,会先被放在**EntryList**
5. 当线程获取到Java对象的Monitor时(Monitor是依靠
底层操作系统
Mutex Lock
来实现
互斥
的)
- 线程申请Mutex成功,则持有该Mutex,其它线程将无法获取到该Mutex
6. 进入
WaitSet
- 竞争锁**失败**的线程会进入**WaitSet**
- 竞争锁**成功**的线程如果调用**wait**方法,就会**释放当前持有的Mutex**,并且该线程会进入**WaitSet**
- 进入**WaitSet**的进程会等待下一次唤醒,然后进入EntryList**重新排队**
7. 如果当前线程顺利执行完方法,也会释放Mutex
8. Monitor依赖于**底层操作系统**的实现,存在**用户态****内核态之间****切换**,所以增加了**性能开销**
[![img](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-monitor.png)](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-monitor.png)
复制
```
ObjectMonitor() {
_header = NULL;
_count = 0; // 记录个数
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL; // 持有该Monitor的线程
_WaitSet = NULL; // 处于wait状态的线程,会被加入 _WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; // 多个线程访问同步块或同步方法,会首先被加入 _EntryList
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
_previous_owner_tid = 0;
}
```
## 锁升级优化
1. 为了提升性能,在**JDK 1.6**引入**偏向锁、轻量级锁、重量级锁**,用来**减少锁竞争带来的上下文切换**
2. 借助JDK 1.6新增的**Java对象头**,实现了**锁升级**功能
### Java对象头
1.**JDK 1.6**的JVM中,对象实例在**堆内存**中被分为三部分:**对象头****实例数据****对齐填充**
2. 对象头的组成部分:**Mark Word****指向类的指针****数组长度**(可选,数组类型时才有)
3. Mark Word记录了**对象****锁**有关的信息,在64位的JVM中,Mark Word为**64 bit**
4. 锁升级功能主要依赖于Mark Word中**锁标志位****是否偏向锁标志位**
5. synchronized同步锁的升级优化路径:***偏向锁** -> **轻量级锁** -> **重量级锁***
[![img](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-mark-word.jpg)](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-mark-word.jpg)
### 偏向锁
1. 偏向锁主要用来优化**同一线程多次申请同一个锁**的竞争,在某些情况下,大部分时间都是同一个线程竞争锁资源
2. 偏向锁的作用
- 当一个线程再次访问同一个同步代码时,该线程只需对该对象头的**Mark Word**中去判断是否有偏向锁指向它
- **无需再进入Monitor去竞争对象**(避免用户态和内核态的**切换**
3. 当对象被当做同步锁,并有一个线程抢到锁时
- 锁标志位还是**01**,是否偏向锁标志位设置为**1**,并且记录抢到锁的**线程ID**,进入***偏向锁状态***
4. 偏向锁
**不会主动释放锁**
- 当线程1再次获取锁时,会比较**当前线程的ID****锁对象头部的线程ID**是否一致,如果一致,无需CAS来抢占锁
- 如果不一致,需要查看
锁对象头部记录的线程
是否存活
- 如果**没有存活**,那么锁对象被重置为**无锁**状态(也是一种撤销),然后重新偏向线程2
- 如果
存活
,查找线程1的栈帧信息
- 如果线程1还是需要继续持有该锁对象,那么暂停线程1(**STW**),**撤销偏向锁**,**升级为轻量级锁**
- 如果线程1不再使用该锁对象,那么将该锁对象设为**无锁**状态(也是一种撤销),然后重新偏向线程2
5. 一旦出现其他线程竞争锁资源时,偏向锁就会被
撤销
- 偏向锁的撤销**可能需要**等待**全局安全点**,暂停持有该锁的线程,同时检查该线程**是否还在执行该方法**
- 如果还没有执行完,说明此刻有**多个线程**竞争,升级为**轻量级锁**;如果已经执行完毕,唤醒其他线程继续**CAS**抢占
6.
高并发
场景下,当
大量线程
同时竞争同一个锁资源时,偏向锁会被
撤销
,发生
STW
,加大了
性能开销
- 默认配置
- `-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=4000`
- 默认开启偏向锁,并且**延迟生效**,因为JVM刚启动时竞争非常激烈
- 关闭偏向锁
- `-XX:-UseBiasedLocking`
- 直接
设置为重量级锁
- `-XX:+UseHeavyMonitors`
红线流程部分:偏向锁的**获取****撤销**
[![img](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-1.png)](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-1.png)
### 轻量级锁
1. 当有另外一个线程竞争锁时,由于该锁处于**偏向锁**状态
2. 发现对象头Mark Word中的线程ID不是自己的线程ID,该线程就会执行
CAS
操作获取锁
- 如果获取**成功**,直接替换Mark Word中的线程ID为自己的线程ID,该锁会***保持偏向锁状态***
- 如果获取**失败**,说明当前锁有一定的竞争,将偏向锁**升级**为轻量级锁
3. 线程获取轻量级锁时会有两步
- 先把**锁对象的Mark Word**复制一份到线程的**栈帧**中(**DisplacedMarkWord**),主要为了**保留现场**!!
- 然后使用**CAS**,把对象头中的内容替换为**线程栈帧中DisplacedMarkWord的地址**
4. 场景
- 在线程1复制对象头Mark Word的同时(CAS之前),线程2也准备获取锁,也复制了对象头Mark Word
- 在线程2进行CAS时,发现线程1已经把对象头换了,线程2的CAS失败,线程2会尝试使用**自旋锁**来等待线程1释放锁
5. 轻量级锁的适用场景:线程**交替执行**同步块,***绝大部分的锁在整个同步周期内都不存在长时间的竞争***
红线流程部分:升级轻量级锁
[![img](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-2.png)](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-2.png)
### 自旋锁 / 重量级锁
1. 轻量级锁
CAS
抢占失败,线程将会被挂起进入
阻塞
状态
- 如果正在持有锁的线程在**很短的时间**内释放锁资源,那么进入**阻塞**状态的线程被**唤醒**后又要**重新抢占**锁资源
2. JVM提供了**自旋锁**,可以通过**自旋**的方式**不断尝试获取锁**,从而***避免线程被挂起阻塞***
3.
JDK 1.7
开始,
自旋锁默认启用
,自旋次数
不建议设置过大
(意味着
长时间占用CPU
- `-XX:+UseSpinning -XX:PreBlockSpin=10`
4. 自旋锁重试之后如果依然抢锁失败,同步锁会升级至
重量级锁
,锁标志位为
10
- 在这个状态下,未抢到锁的线程都会**进入Monitor**,之后会被阻塞在**WaitSet**
5.
锁竞争不激烈
锁占用时间非常短
的场景下,自旋锁可以提高系统性能
- 一旦锁竞争激烈或者锁占用的时间过长,自旋锁将会导致大量的线程一直处于**CAS重试状态****占用CPU资源**
6.
高并发
的场景下,可以通过
关闭自旋锁
来优化系统性能
- ```
-XX:-UseSpinning
```
- 关闭自旋锁优化
- ```
-XX:PreBlockSpin
```
- 默认的自旋次数,在**JDK 1.7**后,**由JVM控制**
[![img](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-3.png)](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-3.png)
## 小结
1. JVM在**JDK 1.6**中引入了**分级锁**机制来优化synchronized
2. 当一个线程获取锁时,首先对象锁成为一个
偏向锁
- 这是为了避免在**同一线程重复获取同一把锁**时,**用户态和内核态频繁切换**
3. 如果有多个线程竞争锁资源,锁将会升级为
轻量级锁
- 这适用于在**短时间**内持有锁,且分锁**交替切换**的场景
- 轻量级锁还结合了**自旋锁****避免线程用户态与内核态的频繁切换**
4. 如果锁竞争太激烈(自旋锁失败),同步锁会升级为重量级锁
5. 优化synchronized同步锁的关键:
减少锁竞争
- 应该尽量使synchronized同步锁处于**轻量级锁****偏向锁**,这样才能提高synchronized同步锁的性能
- 常用手段
- **减少锁粒度**:降低锁竞争
- **减少锁的持有时间**,提高synchronized同步锁在自旋时获取锁资源的成功率,**避免升级为重量级锁**
6.**锁竞争激烈**时,可以考虑**禁用偏向锁****禁用自旋锁**
\ No newline at end of file
<!-- TOC -->
- [Collections 工具类和 Arrays 工具类常见方法](#collections-工具类和-arrays-工具类常见方法)
- [Collections](#collections)
- [排序操作](#排序操作)
- [查找,替换操作](#查找替换操作)
- [同步控制](#同步控制)
- [Arrays类的常见操作](#arrays类的常见操作)
- [排序 : `sort()`](#排序--sort)
- [查找 : `binarySearch()`](#查找--binarysearch)
- [比较: `equals()`](#比较-equals)
- [填充 : `fill()`](#填充--fill)
- [转列表 `asList()`](#转列表-aslist)
- [转字符串 `toString()`](#转字符串-tostring)
- [复制 `copyOf()`](#复制-copyof)
<!-- /TOC -->
# Collections 工具类和 Arrays 工具类常见方法
## Collections
Collections 工具类常用方法:
1. 排序
2. 查找,替换操作
3. 同步控制(不推荐,需要线程安全的集合类型时请考虑使用 JUC 包下的并发集合)
### 排序操作
```java
void reverse(List list)//反转
void shuffle(List list)//随机排序
void sort(List list)//按自然排序的升序排序
void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑
void swap(List list, int i , int j)//交换两个索引位置的元素
void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面。
```
**示例代码:**
```java
ArrayList<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(-1);
arrayList.add(3);
arrayList.add(3);
arrayList.add(-5);
arrayList.add(7);
arrayList.add(4);
arrayList.add(-9);
arrayList.add(-7);
System.out.println("原始数组:");
System.out.println(arrayList);
// void reverse(List list):反转
Collections.reverse(arrayList);
System.out.println("Collections.reverse(arrayList):");
System.out.println(arrayList);
Collections.rotate(arrayList, 4);
System.out.println("Collections.rotate(arrayList, 4):");
System.out.println(arrayList);
// void sort(List list),按自然排序的升序排序
Collections.sort(arrayList);
System.out.println("Collections.sort(arrayList):");
System.out.println(arrayList);
// void shuffle(List list),随机排序
Collections.shuffle(arrayList);
System.out.println("Collections.shuffle(arrayList):");
System.out.println(arrayList);
// void swap(List list, int i , int j),交换两个索引位置的元素
Collections.swap(arrayList, 2, 5);
System.out.println("Collections.swap(arrayList, 2, 5):");
System.out.println(arrayList);
// 定制排序的用法
Collections.sort(arrayList, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
System.out.println("定制排序后:");
System.out.println(arrayList);
```
### 查找,替换操作
```java
int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的
int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll)
int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c)
void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素。
int frequency(Collection c, Object o)//统计元素出现次数
int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target).
boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替换旧元素
```
**示例代码:**
```java
ArrayList<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(-1);
arrayList.add(3);
arrayList.add(3);
arrayList.add(-5);
arrayList.add(7);
arrayList.add(4);
arrayList.add(-9);
arrayList.add(-7);
ArrayList<Integer> arrayList2 = new ArrayList<Integer>();
arrayList2.add(-3);
arrayList2.add(-5);
arrayList2.add(7);
System.out.println("原始数组:");
System.out.println(arrayList);
System.out.println("Collections.max(arrayList):");
System.out.println(Collections.max(arrayList));
System.out.println("Collections.min(arrayList):");
System.out.println(Collections.min(arrayList));
System.out.println("Collections.replaceAll(arrayList, 3, -3):");
Collections.replaceAll(arrayList, 3, -3);
System.out.println(arrayList);
System.out.println("Collections.frequency(arrayList, -3):");
System.out.println(Collections.frequency(arrayList, -3));
System.out.println("Collections.indexOfSubList(arrayList, arrayList2):");
System.out.println(Collections.indexOfSubList(arrayList, arrayList2));
System.out.println("Collections.binarySearch(arrayList, 7):");
// 对List进行二分查找,返回索引,List必须是有序的
Collections.sort(arrayList);
System.out.println(Collections.binarySearch(arrayList, 7));
```
### 同步控制
Collections提供了多个`synchronizedXxx()`方法·,该方法可以将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题。
我们知道 HashSet,TreeSet,ArrayList,LinkedList,HashMap,TreeMap 都是线程不安全的。Collections提供了多个静态方法可以把他们包装成线程同步的集合。
**最好不要用下面这些方法,效率非常低,需要线程安全的集合类型时请考虑使用 JUC 包下的并发集合。**
方法如下:
```java
synchronizedCollection(Collection<T> c) //返回指定 collection 支持的同步(线程安全的)collection。
synchronizedList(List<T> list)//返回指定列表支持的同步(线程安全的)List。
synchronizedMap(Map<K,V> m) //返回由指定映射支持的同步(线程安全的)Map。
synchronizedSet(Set<T> s) //返回指定 set 支持的同步(线程安全的)set。
```
### Collections还可以设置不可变集合,提供了如下三类方法:
```java
emptyXxx(): 返回一个空的不可变的集合对象此处的集合既可以是List也可以是Set还可以是Map
singletonXxx(): 返回一个只包含指定对象只有一个或一个元素的不可变的集合对象此处的集合可以是ListSetMap
unmodifiableXxx(): 返回指定集合对象的不可变视图此处的集合可以是ListSetMap
上面三类方法的参数是原有的集合对象返回值是该集合的只读版本
```
**示例代码:**
```java
ArrayList<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(-1);
arrayList.add(3);
arrayList.add(3);
arrayList.add(-5);
arrayList.add(7);
arrayList.add(4);
arrayList.add(-9);
arrayList.add(-7);
HashSet<Integer> integers1 = new HashSet<>();
integers1.add(1);
integers1.add(3);
integers1.add(2);
Map scores = new HashMap();
scores.put("语文" , 80);
scores.put("Java" , 82);
//Collections.emptyXXX();创建一个空的、不可改变的XXX对象
List<Object> list = Collections.emptyList();
System.out.println(list);//[]
Set<Object> objects = Collections.emptySet();
System.out.println(objects);//[]
Map<Object, Object> objectObjectMap = Collections.emptyMap();
System.out.println(objectObjectMap);//{}
//Collections.singletonXXX();
List<ArrayList<Integer>> arrayLists = Collections.singletonList(arrayList);
System.out.println(arrayLists);//[[-1, 3, 3, -5, 7, 4, -9, -7]]
//创建一个只有一个元素,且不可改变的Set对象
Set<ArrayList<Integer>> singleton = Collections.singleton(arrayList);
System.out.println(singleton);//[[-1, 3, 3, -5, 7, 4, -9, -7]]
Map<String, String> nihao = Collections.singletonMap("1", "nihao");
System.out.println(nihao);//{1=nihao}
//unmodifiableXXX();创建普通XXX对象对应的不可变版本
List<Integer> integers = Collections.unmodifiableList(arrayList);
System.out.println(integers);//[-1, 3, 3, -5, 7, 4, -9, -7]
Set<Integer> integers2 = Collections.unmodifiableSet(integers1);
System.out.println(integers2);//[1, 2, 3]
Map<Object, Object> objectObjectMap2 = Collections.unmodifiableMap(scores);
System.out.println(objectObjectMap2);//{Java=82, 语文=80}
//添加出现异常:java.lang.UnsupportedOperationException
// list.add(1);
// arrayLists.add(arrayList);
// integers.add(1);
```
## Arrays类的常见操作
1. 排序 : `sort()`
2. 查找 : `binarySearch()`
3. 比较: `equals()`
4. 填充 : `fill()`
5. 转列表: `asList()`
6. 转字符串 : `toString()`
7. 复制: `copyOf()`
### 排序 : `sort()`
```java
// *************排序 sort****************
int a[] = { 1, 3, 2, 7, 6, 5, 4, 9 };
// sort(int[] a)方法按照数字顺序排列指定的数组。
Arrays.sort(a);
System.out.println("Arrays.sort(a):");
for (int i : a) {
System.out.print(i);
}
// 换行
System.out.println();
// sort(int[] a,int fromIndex,int toIndex)按升序排列数组的指定范围
int b[] = { 1, 3, 2, 7, 6, 5, 4, 9 };
Arrays.sort(b, 2, 6);
System.out.println("Arrays.sort(b, 2, 6):");
for (int i : b) {
System.out.print(i);
}
// 换行
System.out.println();
int c[] = { 1, 3, 2, 7, 6, 5, 4, 9 };
// parallelSort(int[] a) 按照数字顺序排列指定的数组(并行的)。同sort方法一样也有按范围的排序
Arrays.parallelSort(c);
System.out.println("Arrays.parallelSort(c):");
for (int i : c) {
System.out.print(i);
}
// 换行
System.out.println();
// parallelSort给字符数组排序,sort也可以
char d[] = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
Arrays.parallelSort(d);
System.out.println("Arrays.parallelSort(d):");
for (char d2 : d) {
System.out.print(d2);
}
// 换行
System.out.println();
```
在做算法面试题的时候,我们还可能会经常遇到对字符串排序的情况,`Arrays.sort()` 对每个字符串的特定位置进行比较,然后按照升序排序。
```java
String[] strs = { "abcdehg", "abcdefg", "abcdeag" };
Arrays.sort(strs);
System.out.println(Arrays.toString(strs));//[abcdeag, abcdefg, abcdehg]
```
### 查找 : `binarySearch()`
```java
// *************查找 binarySearch()****************
char[] e = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
// 排序后再进行二分查找,否则找不到
Arrays.sort(e);
System.out.println("Arrays.sort(e)" + Arrays.toString(e));
System.out.println("Arrays.binarySearch(e, 'c'):");
int s = Arrays.binarySearch(e, 'c');
System.out.println("字符c在数组的位置:" + s);
```
### 比较: `equals()`
```java
// *************比较 equals****************
char[] e = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
char[] f = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
/*
* 元素数量相同,并且相同位置的元素相同。 另外,如果两个数组引用都是null,则它们被认为是相等的 。
*/
// 输出true
System.out.println("Arrays.equals(e, f):" + Arrays.equals(e, f));
```
### 填充 : `fill()`
```java
// *************填充fill(批量初始化)****************
int[] g = { 1, 2, 3, 3, 3, 3, 6, 6, 6 };
// 数组中所有元素重新分配值
Arrays.fill(g, 3);
System.out.println("Arrays.fill(g, 3):");
// 输出结果:333333333
for (int i : g) {
System.out.print(i);
}
// 换行
System.out.println();
int[] h = { 1, 2, 3, 3, 3, 3, 6, 6, 6, };
// 数组中指定范围元素重新分配值
Arrays.fill(h, 0, 2, 9);
System.out.println("Arrays.fill(h, 0, 2, 9);:");
// 输出结果:993333666
for (int i : h) {
System.out.print(i);
}
```
### 转列表 `asList()`
```java
// *************转列表 asList()****************
/*
* 返回由指定数组支持的固定大小的列表。
* (将返回的列表更改为“写入数组”。)该方法作为基于数组和基于集合的API之间的桥梁,与Collection.toArray()相结合 。
* 返回的列表是可序列化的,并实现RandomAccess 。
* 此方法还提供了一种方便的方式来创建一个初始化为包含几个元素的固定大小的列表如下:
*/
List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
System.out.println(stooges);
```
### 转字符串 `toString()`
```java
// *************转字符串 toString()****************
/*
* 返回指定数组的内容的字符串表示形式。
*/
char[] k = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
System.out.println(Arrays.toString(k));// [a, f, b, c, e, A, C, B]
```
### 复制 `copyOf()`
```java
// *************复制 copy****************
// copyOf 方法实现数组复制,h为数组,6为复制的长度
int[] h = { 1, 2, 3, 3, 3, 3, 6, 6, 6, };
int i[] = Arrays.copyOf(h, 6);
System.out.println("Arrays.copyOf(h, 6);:");
// 输出结果:123333
for (int j : i) {
System.out.print(j);
}
// 换行
System.out.println();
// copyOfRange将指定数组的指定范围复制到新数组中
int j[] = Arrays.copyOfRange(h, 6, 11);
System.out.println("Arrays.copyOfRange(h, 6, 11):");
// 输出结果66600(h数组只有9个元素这里是从索引6到索引11复制所以不足的就为0)
for (int j2 : j) {
System.out.print(j2);
}
// 换行
System.out.println();
```
......@@ -1173,11 +1173,32 @@ String s = input.readLine();
## 3. Java 核心技术
### 3.1. 集合
### 3.1. 反射机制
#### 3.1.1. Collections 工具类和 Arrays 工具类常见方法总结
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。
详见笔主的这篇文章: https://gitee.com/SnailClimb/JavaGuide/blob/master/docs/java/basic/Arrays,CollectionsCommonMethods.md
#### 3.1.1.静态编译和动态编译
- **静态编译:** 在编译时确定类型,绑定对象
- **动态编译:** 运行时确定类型,绑定对象
#### 3.1.2.反射机制优缺点
- **优点:** 运行期类型的判断,动态加载类,提高代码灵活度。
- **缺点:** 1,性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的 java 代码要慢很多。2,安全问题,让我们可以动态操作改变类的属性同时也增加了类的安全隐患。
#### 3.1.3.反射的应用场景
**反射是框架设计的灵魂。**
在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架也大量使用到了反射机制。
举例:
1. 我们在使用 JDBC 连接数据库时使用 `Class.forName()`通过反射加载数据库的驱动程序;
2. Spring 框架的 IOC(动态加载管理 Bean)创建对象以及 AOP(动态代理)功能都和反射有联系;
3. 动态配置实例的属性;
4. ......
### 3.2. 异常
......
## 一 先从 ArrayList 的构造函数说起
**ArrayList有三种方式来初始化,构造方法源码如下:**
```java
/**
* 默认初始容量大小
*/
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
*默认构造函数,使用初始容量10构造一个空列表(无参数构造)
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 带初始容量参数的构造函数。(用户自己指定容量)
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {//初始容量大于0
//创建initialCapacity大小的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {//初始容量等于0
//创建空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {//初始容量小于0,抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
*构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回
*如果指定的集合为null,throws NullPointerException。
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
```
细心的同学一定会发现 :**以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为10。** 下面在我们分析 ArrayList 扩容时会讲到这一点内容!
## 二 一步一步分析 ArrayList 扩容机制
这里以无参构造函数创建的 ArrayList 为例分析
### 1. 先来看 `add` 方法
```java
/**
* 将指定的元素追加到此列表的末尾。
*/
public boolean add(E e) {
//添加元素之前,先调用ensureCapacityInternal方法
ensureCapacityInternal(size + 1); // Increments modCount!!
//这里看到ArrayList添加元素的实质就相当于为数组赋值
elementData[size++] = e;
return true;
}
```
> **注意** :JDK11 移除了 `ensureCapacityInternal()` 和 `ensureExplicitCapacity()` 方法
### 2. 再来看看 `ensureCapacityInternal()` 方法
可以看到 `add` 方法 首先调用了`ensureCapacityInternal(size + 1)`
```java
//得到最小扩容量
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 获取默认的容量和传入参数的较大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
```
**当 要 add 进第1个元素时,minCapacity为1,在Math.max()方法比较后,minCapacity 为10。**
### 3. `ensureExplicitCapacity()` 方法
如果调用 `ensureCapacityInternal()` 方法就一定会进过(执行)这个方法,下面我们来研究一下这个方法的源码!
```java
//判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//调用grow方法进行扩容,调用此方法代表已经开始扩容了
grow(minCapacity);
}
```
我们来仔细分析一下:
- 当我们要 add 进第1个元素到 ArrayList 时,elementData.length 为0 (因为还是一个空的 list),因为执行了 `ensureCapacityInternal()` 方法 ,所以 minCapacity 此时为10。此时,`minCapacity - elementData.length > 0 `成立,所以会进入 `grow(minCapacity)` 方法。
- 当add第2个元素时,minCapacity 为2,此时e lementData.length(容量)在添加第一个元素后扩容成 10 了。此时,`minCapacity - elementData.length > 0 ` 不成立,所以不会进入 (执行)`grow(minCapacity)` 方法。
- 添加第3、4···到第10个元素时,依然不会执行grow方法,数组容量都为10。
直到添加第11个元素,minCapacity(为11)比elementData.length(为10)要大。进入grow方法进行扩容。
### 4. `grow()` 方法
```java
/**
* 要分配的最大数组大小
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* ArrayList扩容的核心方法。
*/
private void grow(int minCapacity) {
// oldCapacity为旧容量,newCapacity为新容量
int oldCapacity = elementData.length;
//将oldCapacity 右移一位,其效果相当于oldCapacity /2,
//我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍,
int newCapacity = oldCapacity + (oldCapacity >> 1);
//然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,
//如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
```
**int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍左右(oldCapacity为偶数就是1.5倍,否则是1.5倍左右)!** 奇偶不同,比如 :10+10/2 = 15, 33+33/2=49。如果是奇数的话会丢掉小数.
> ">>"(移位运算符):>>1 右移一位相当于除2,右移n位相当于除以 2 的 n 次方。这里 oldCapacity 明显右移了1位所以相当于oldCapacity /2。对于大数据的2进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源  
**我们再来通过例子探究一下`grow()` 方法 :**
- 当add第1个元素时,oldCapacity 为0,经比较后第一个if判断成立,newCapacity = minCapacity(为10)。但是第二个if判断不会成立,即newCapacity 不比 MAX_ARRAY_SIZE大,则不会进入 `hugeCapacity` 方法。数组容量为10,add方法中 return true,size增为1。
- 当add第11个元素进入grow方法时,newCapacity为15,比minCapacity(为11)大,第一个if判断不成立。新容量没有大于数组最大size,不会进入hugeCapacity方法。数组容量扩为15,add方法中return true,size增为11。
- 以此类推······
**这里补充一点比较重要,但是容易被忽视掉的知识点:**
- java 中的 `length `属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了 length 这个属性.
- java 中的 `length()` 方法是针对字符串说的,如果想看这个字符串的长度则用到 `length()` 这个方法.
- java 中的 `size()` 方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看!
### 5. `hugeCapacity()` 方法。
从上面 `grow()` 方法源码我们知道: 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`
```java
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//对minCapacity和MAX_ARRAY_SIZE进行比较
//若minCapacity大,将Integer.MAX_VALUE作为新数组的大小
//若MAX_ARRAY_SIZE大,将MAX_ARRAY_SIZE作为新数组的大小
//MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
```
## 三 `System.arraycopy()` 和 `Arrays.copyOf()`方法
阅读源码的话,我们就会发现 ArrayList 中大量调用了这两个方法。比如:我们上面讲的扩容操作以及`add(int index, E element)``toArray()` 等方法中都用到了该方法!
### 3.1 `System.arraycopy()` 方法
```java
/**
* 在此列表中的指定位置插入指定的元素。
*先调用 rangeCheckForAdd 对index进行界限检查;然后调用 ensureCapacityInternal 方法保证capacity足够大;
*再将从index开始之后的所有成员后移一个位置;将element插入index位置;最后size加1。
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
//arraycopy()方法实现数组自己复制自己
//elementData:源数组;index:源数组中的起始位置;elementData:目标数组;index + 1:目标数组中的起始位置; size - index:要复制的数组元素的数量;
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
size++;
}
```
我们写一个简单的方法测试以下:
```java
public class ArraycopyTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] a = new int[10];
a[0] = 0;
a[1] = 1;
a[2] = 2;
a[3] = 3;
System.arraycopy(a, 2, a, 3, 3);
a[2]=99;
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
}
}
```
结果:
```
0 1 99 2 3 0 0 0 0 0
```
### 3.2 `Arrays.copyOf()`方法
```java
/**
以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); 返回的数组的运行时类型是指定数组的运行时类型。
*/
public Object[] toArray() {
//elementData:要复制的数组;size:要复制的长度
return Arrays.copyOf(elementData, size);
}
```
个人觉得使用 `Arrays.copyOf()`方法主要是为了给原有数组扩容,测试代码如下:
```java
public class ArrayscopyOfTest {
public static void main(String[] args) {
int[] a = new int[3];
a[0] = 0;
a[1] = 1;
a[2] = 2;
int[] b = Arrays.copyOf(a, 10);
System.out.println("b.length"+b.length);
}
}
```
结果:
```
10
```
### 3.3 两者联系和区别
**联系:**
看两者源代码可以发现 copyOf() 内部实际调用了 `System.arraycopy()` 方法
**区别:**
`arraycopy()` 需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 `copyOf()` 是系统自动在内部新建一个数组,并返回该数组。
## 四 `ensureCapacity`方法
ArrayList 源码中有一个 `ensureCapacity` 方法不知道大家注意到没有,这个方法 ArrayList 内部没有被调用过,所以很显然是提供给用户调用的,那么这个方法有什么作用呢?
```java
/**
如有必要,增加此 ArrayList 实例的容量,以确保它至少可以容纳由minimum capacity参数指定的元素数。
*
* @param minCapacity 所需的最小容量
*/
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
```
**最好在 add 大量元素之前用 `ensureCapacity` 方法,以减少增量重新分配的次数**
我们通过下面的代码实际测试以下这个方法的效果:
```java
public class EnsureCapacityTest {
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<Object>();
final int N = 10000000;
long startTime = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
list.add(i);
}
long endTime = System.currentTimeMillis();
System.out.println("使用ensureCapacity方法前:"+(endTime - startTime));
}
}
```
运行结果:
```
使用ensureCapacity方法前:2158
```
```java
public class EnsureCapacityTest {
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<Object>();
final int N = 10000000;
list = new ArrayList<Object>();
long startTime1 = System.currentTimeMillis();
list.ensureCapacity(N);
for (int i = 0; i < N; i++) {
list.add(i);
}
long endTime1 = System.currentTimeMillis();
System.out.println("使用ensureCapacity方法后:"+(endTime1 - startTime1));
}
}
```
运行结果:
```
使用ensureCapacity方法前:1773
```
通过运行结果,我们可以看出向 ArrayList 添加大量元素之前最好先使用`ensureCapacity` 方法,以减少增量重新分配的次数。
......@@ -23,8 +23,10 @@ List list=Collections.synchronizedList(new LinkedList(...));
```
## <font face="楷体" id="2">内部结构分析</font>
**如下图所示:**
![LinkedList内部结构](https://user-gold-cdn.xitu.io/2018/3/19/1623e363fe0450b0?w=600&h=481&f=jpeg&s=18502)
![LinkedList内部结构](images/linkedlist/LinkedList内部结构.png)
看完了图之后,我们再看LinkedList类中的一个<font color="red">**内部私有类Node**</font>就很好理解了:
```java
private static class Node<E> {
E item;//节点值
......
此差异已折叠。
<!-- TOC -->
- [0.0.1. 泛型的实际应用:实现最小值函数](#001-%e6%b3%9b%e5%9e%8b%e7%9a%84%e5%ae%9e%e9%99%85%e5%ba%94%e7%94%a8%e5%ae%9e%e7%8e%b0%e6%9c%80%e5%b0%8f%e5%80%bc%e5%87%bd%e6%95%b0)
- [0.0.2. 使用数组实现栈](#002-%e4%bd%bf%e7%94%a8%e6%95%b0%e7%bb%84%e5%ae%9e%e7%8e%b0%e6%a0%88)
- [0.0.3. 实现线程安全的 LRU 缓存](#003-%e5%ae%9e%e7%8e%b0%e7%ba%bf%e7%a8%8b%e5%ae%89%e5%85%a8%e7%9a%84-lru-%e7%bc%93%e5%ad%98)
<!-- /TOC -->
### 0.0.1. 泛型的实际应用:实现最小值函数
自己设计一个泛型的获取数组最小值的函数.并且这个方法只能接受Number的子类并且实现了Comparable接口。
```java
//注意:Number并没有实现Comparable
private static <T extends Number & Comparable<? super T>> T min(T[] values) {
if (values == null || values.length == 0) return null;
T min = values[0];
for (int i = 1; i < values.length; i++) {
if (min.compareTo(values[i]) > 0) min = values[i];
}
return min;
}
```
测试:
```java
int minInteger = min(new Integer[]{1, 2, 3});//result:1
double minDouble = min(new Double[]{1.2, 2.2, -1d});//result:-1d
String typeError = min(new String[]{"1","3"});//报错
```
### 0.0.2. 使用数组实现栈
**自己实现一个栈,要求这个栈具有`push()`、`pop()`(返回栈顶元素并出栈)、`peek()` (返回栈顶元素不出栈)、`isEmpty()`、`size()`这些基本的方法。**
提示:每次入栈之前先判断栈的容量是否够用,如果不够用就用`Arrays.copyOf()`进行扩容;
```java
public class MyStack {
private int[] storage;//存放栈中元素的数组
private int capacity;//栈的容量
private int count;//栈中元素数量
private static final int GROW_FACTOR = 2;
//不带初始容量的构造方法。默认容量为8
public MyStack() {
this.capacity = 8;
this.storage=new int[8];
this.count = 0;
}
//带初始容量的构造方法
public MyStack(int initialCapacity) {
if (initialCapacity < 1)
throw new IllegalArgumentException("Capacity too small.");
this.capacity = initialCapacity;
this.storage = new int[initialCapacity];
this.count = 0;
}
//入栈
public void push(int value) {
if (count == capacity) {
ensureCapacity();
}
storage[count++] = value;
}
//确保容量大小
private void ensureCapacity() {
int newCapacity = capacity * GROW_FACTOR;
storage = Arrays.copyOf(storage, newCapacity);
capacity = newCapacity;
}
//返回栈顶元素并出栈
private int pop() {
if (count == 0)
throw new IllegalArgumentException("Stack is empty.");
count--;
return storage[count];
}
//返回栈顶元素不出栈
private int peek() {
if (count == 0){
throw new IllegalArgumentException("Stack is empty.");
}else {
return storage[count-1];
}
}
//判断栈是否为空
private boolean isEmpty() {
return count == 0;
}
//返回栈中元素的个数
private int size() {
return count;
}
}
```
验证
```java
MyStack myStack = new MyStack(3);
myStack.push(1);
myStack.push(2);
myStack.push(3);
myStack.push(4);
myStack.push(5);
myStack.push(6);
myStack.push(7);
myStack.push(8);
System.out.println(myStack.peek());//8
System.out.println(myStack.size());//8
for (int i = 0; i < 8; i++) {
System.out.println(myStack.pop());
}
System.out.println(myStack.isEmpty());//true
myStack.pop();//报错:java.lang.IllegalArgumentException: Stack is empty.
```
<!-- TOC -->
- [1. LRU 缓存介绍](#1-lru-%e7%bc%93%e5%ad%98%e4%bb%8b%e7%bb%8d)
- [2. ConcurrentLinkedQueue简单介绍](#2-concurrentlinkedqueue%e7%ae%80%e5%8d%95%e4%bb%8b%e7%bb%8d)
- [3. ReadWriteLock简单介绍](#3-readwritelock%e7%ae%80%e5%8d%95%e4%bb%8b%e7%bb%8d)
- [4. ScheduledExecutorService 简单介绍](#4-scheduledexecutorservice-%e7%ae%80%e5%8d%95%e4%bb%8b%e7%bb%8d)
- [5. 徒手撸一个线程安全的 LRU 缓存](#5-%e5%be%92%e6%89%8b%e6%92%b8%e4%b8%80%e4%b8%aa%e7%ba%bf%e7%a8%8b%e5%ae%89%e5%85%a8%e7%9a%84-lru-%e7%bc%93%e5%ad%98)
- [5.1. 实现方法](#51-%e5%ae%9e%e7%8e%b0%e6%96%b9%e6%b3%95)
- [5.2. 原理](#52-%e5%8e%9f%e7%90%86)
- [5.3. put方法具体流程分析](#53-put%e6%96%b9%e6%b3%95%e5%85%b7%e4%bd%93%e6%b5%81%e7%a8%8b%e5%88%86%e6%9e%90)
- [5.4. 源码](#54-%e6%ba%90%e7%a0%81)
- [6. 实现一个线程安全并且带有过期时间的 LRU 缓存](#6-%e5%ae%9e%e7%8e%b0%e4%b8%80%e4%b8%aa%e7%ba%bf%e7%a8%8b%e5%ae%89%e5%85%a8%e5%b9%b6%e4%b8%94%e5%b8%a6%e6%9c%89%e8%bf%87%e6%9c%9f%e6%97%b6%e9%97%b4%e7%9a%84-lru-%e7%bc%93%e5%ad%98)
<!-- /TOC -->
最近被读者问到“不用LinkedHashMap的话,如何实现一个线程安全的 LRU 缓存?网上的代码太杂太乱,Guide哥哥能不能帮忙写一个?”。
*划重点,手写一个 LRU 缓存在面试中还是挺常见的!*
很多人就会问了:“网上已经有这么多现成的缓存了!为什么面试官还要我们自己实现一个呢?” 。咳咳咳,当然是为了面试需要。哈哈!开个玩笑,我个人觉得更多地是为了学习吧!今天Guide哥教大家:
1. 实现一个线程安全的 LRU 缓存
2. 实现一个线程安全并且带有过期时间的 LRU 缓存
考虑到了线程安全性我们使用了 `ConcurrentHashMap``ConcurrentLinkedQueue` 这两个线程安全的集合。另外,还用到 `ReadWriteLock`(读写锁)。为了实现带有过期时间的缓存,我们用到了 `ScheduledExecutorService`来做定时任务执行。
如果有任何不对或者需要完善的地方,请帮忙指出!
### 1. LRU 缓存介绍
**LRU (Least Recently Used,最近最少使用)是一种缓存淘汰策略。**
LRU缓存指的是当缓存大小已达到最大分配容量的时候,如果再要去缓存新的对象数据的话,就需要将缓存中最近访问最少的对象删除掉以便给新来的数据腾出空间。
### 2. ConcurrentLinkedQueue简单介绍
**ConcurrentLinkedQueue是一个基于单向链表的无界无锁线程安全的队列,适合在高并发环境下使用,效率比较高。** 我们在使用的时候,可以就把它理解为我们经常接触的数据结构——队列,不过是增加了多线程下的安全性保证罢了。**和普通队列一样,它也是按照先进先出(FIFO)的规则对接点进行排序。** 另外,队列元素中不可以放置null元素。
`ConcurrentLinkedQueue` 整个继承关系如下图所示:
![](./../../../media/pictures/java/my-lru-cache/ConcurrentLinkedQueue-Diagram.png)
`ConcurrentLinkedQueue中`最主要的两个方法是:`offer(value)``poll()`,分别实现队列的两个重要的操作:入队和出队(`offer(value)`等价于 `add(value)`)。
我们添加一个元素到队列的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。
![单链表](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/单链表2.png)
利用`ConcurrentLinkedQueue`队列先进先出的特性,每当我们 `put`/`get`(缓存被使用)元素的时候,我们就将这个元素存放在队列尾部,这样就能保证队列头部的元素是最近最少使用的。
### 3. ReadWriteLock简单介绍
`ReadWriteLock` 是一个接口,位于`java.util.concurrent.locks`包下,里面只有两个方法分别返回读锁和写锁:
```java
public interface ReadWriteLock {
/**
* 返回读锁
*/
Lock readLock();
/**
* 返回写锁
*/
Lock writeLock();
}
```
`ReentrantReadWriteLock``ReadWriteLock`接口的具体实现类。
**读写锁还是比较适合缓存这种读多写少的场景。读写锁可以保证多个线程和同时读取,但是只有一个线程可以写入。**
读写锁的特点是:写锁和写锁互斥,读锁和写锁互斥,读锁之间不互斥。也就说:同一时刻只能有一个线程写,但是可以有多个线程
读。读写之间是互斥的,两者不能同时发生(当进行写操作时,同一时刻其他线程的读操作会被阻塞;当进行读操作时,同一时刻所有线程的写操作会被阻塞)。
另外,**同一个线程持有写锁时是可以申请读锁,但是持有读锁的情况下不可以申请写锁。**
### 4. ScheduledExecutorService 简单介绍
`ScheduledExecutorService` 是一个接口,`ScheduledThreadPoolExecutor` 是其主要实现类。
![](./../../../media/pictures/java/my-lru-cache/ScheduledThreadPoolExecutor-diagram.png)
**`ScheduledThreadPoolExecutor` 主要用来在给定的延迟后运行任务,或者定期执行任务。** 这个在实际项目用到的比较少,因为有其他方案选择比如`quartz`。但是,在一些需求比较简单的场景下还是非常有用的!
**`ScheduledThreadPoolExecutor` 使用的任务队列 `DelayQueue` 封装了一个 `PriorityQueue`,`PriorityQueue` 会对队列中的任务进行排序,执行所需时间短的放在前面先被执行,如果执行所需时间相同则先提交的任务将被先执行。**
### 5. 徒手撸一个线程安全的 LRU 缓存
#### 5.1. 实现方法
`ConcurrentHashMap` + `ConcurrentLinkedQueue` +`ReadWriteLock`
#### 5.2. 原理
`ConcurrentHashMap` 是线程安全的Map,我们可以利用它缓存 key,value形式的数据。`ConcurrentLinkedQueue`是一个线程安全的基于链表的队列(先进先出),我们可以用它来维护 key 。每当我们put/get(缓存被使用)元素的时候,我们就将这个元素对应的 key 存放在队列尾部,这样就能保证队列头部的元素是最近最少使用的。当我们的缓存容量不够的时候,我们直接移除队列头部对应的key以及这个key对应的缓存即可!
另外,我们用到了`ReadWriteLock`(读写锁)来保证线程安全。
#### 5.3. put方法具体流程分析
为了方便大家理解,我将代码中比较重要的 `put(key,value)`方法的原理图画了出来,如下图所示:
![](./../../../media/pictures/java/my-lru-cache/MyLRUCachePut.png)
#### 5.4. 源码
```java
/**
* @author shuang.kou
* <p>
* 使用 ConcurrentHashMap+ConcurrentLinkedQueue+ReadWriteLock实现线程安全的 LRU 缓存
* 这里只是为了学习使用,本地缓存推荐使用 Guava 自带的,使用 Spring 的话,推荐使用Spring Cache
*/
public class MyLruCache<K, V> {
/**
* 缓存的最大容量
*/
private final int maxCapacity;
private ConcurrentHashMap<K, V> cacheMap;
private ConcurrentLinkedQueue<K> keys;
/**
* 读写锁
*/
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock writeLock = readWriteLock.writeLock();
private Lock readLock = readWriteLock.readLock();
public MyLruCache(int maxCapacity) {
if (maxCapacity < 0) {
throw new IllegalArgumentException("Illegal max capacity: " + maxCapacity);
}
this.maxCapacity = maxCapacity;
cacheMap = new ConcurrentHashMap<>(maxCapacity);
keys = new ConcurrentLinkedQueue<>();
}
public V put(K key, V value) {
// 加写锁
writeLock.lock();
try {
//1.key是否存在于当前缓存
if (cacheMap.containsKey(key)) {
moveToTailOfQueue(key);
cacheMap.put(key, value);
return value;
}
//2.是否超出缓存容量,超出的话就移除队列头部的元素以及其对应的缓存
if (cacheMap.size() == maxCapacity) {
System.out.println("maxCapacity of cache reached");
removeOldestKey();
}
//3.key不存在于当前缓存。将key添加到队列的尾部并且缓存key及其对应的元素
keys.add(key);
cacheMap.put(key, value);
return value;
} finally {
writeLock.unlock();
}
}
public V get(K key) {
//加读锁
readLock.lock();
try {
//key是否存在于当前缓存
if (cacheMap.containsKey(key)) {
// 存在的话就将key移动到队列的尾部
moveToTailOfQueue(key);
return cacheMap.get(key);
}
//不存在于当前缓存中就返回Null
return null;
} finally {
readLock.unlock();
}
}
public V remove(K key) {
writeLock.lock();
try {
//key是否存在于当前缓存
if (cacheMap.containsKey(key)) {
// 存在移除队列和Map中对应的Key
keys.remove(key);
return cacheMap.remove(key);
}
//不存在于当前缓存中就返回Null
return null;
} finally {
writeLock.unlock();
}
}
/**
* 将元素添加到队列的尾部(put/get的时候执行)
*/
private void moveToTailOfQueue(K key) {
keys.remove(key);
keys.add(key);
}
/**
* 移除队列头部的元素以及其对应的缓存 (缓存容量已满的时候执行)
*/
private void removeOldestKey() {
K oldestKey = keys.poll();
if (oldestKey != null) {
cacheMap.remove(oldestKey);
}
}
public int size() {
return cacheMap.size();
}
}
```
**非并发环境测试:**
```java
MyLruCache<Integer, String> myLruCache = new MyLruCache<>(3);
myLruCache.put(1, "Java");
System.out.println(myLruCache.get(1));// Java
myLruCache.remove(1);
System.out.println(myLruCache.get(1));// null
myLruCache.put(2, "C++");
myLruCache.put(3, "Python");
System.out.println(myLruCache.get(2));//C++
myLruCache.put(4, "C");
myLruCache.put(5, "PHP");
System.out.println(myLruCache.get(2));// C++
```
**并发环境测试:**
我们初始化了一个固定容量为 10 的线程池和count为10的`CountDownLatch`。我们将1000000次操作分10次添加到线程池,然后我们等待线程池执行完成这10次操作。
```java
int threadNum = 10;
int batchSize = 100000;
//init cache
MyLruCache<String, Integer> myLruCache = new MyLruCache<>(batchSize * 10);
//init thread pool with 10 threads
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(threadNum);
//init CountDownLatch with 10 count
CountDownLatch latch = new CountDownLatch(threadNum);
AtomicInteger atomicInteger = new AtomicInteger(0);
long startTime = System.currentTimeMillis();
for (int t = 0; t < threadNum; t++) {
fixedThreadPool.submit(() -> {
for (int i = 0; i < batchSize; i++) {
int value = atomicInteger.incrementAndGet();
myLruCache.put("id" + value, value);
}
latch.countDown();
});
}
//wait for 10 threads to complete the task
latch.await();
fixedThreadPool.shutdown();
System.out.println("Cache size:" + myLruCache.size());//Cache size:1000000
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.out.println(String.format("Time cost:%dms", duration));//Time cost:511ms
```
### 6. 实现一个线程安全并且带有过期时间的 LRU 缓存
实际上就是在我们上面时间的LRU缓存的基础上加上一个定时任务去删除缓存,单纯利用 JDK 提供的类,我们实现定时任务的方式有很多种:
1. `Timer` :不被推荐,多线程会存在问题。
2. `ScheduledExecutorService` :定时器线程池,可以用来替代 `Timer`
3. `DelayQueue` :延时队列
4. `quartz` :一个很火的开源任务调度框架,很多其他框架都是基于 `quartz` 开发的,比如当当网的`elastic-job `就是基于`quartz`二次开发之后的分布式调度解决方案
5. ......
最终我们选择了 `ScheduledExecutorService`,主要原因是它易用(基于`DelayQueue`做了很多封装)并且基本能满足我们的大部分需求。
我们在我们上面实现的线程安全的 LRU 缓存基础上,简单稍作修改即可!我们增加了一个方法:
```java
private void removeAfterExpireTime(K key, long expireTime) {
scheduledExecutorService.schedule(() -> {
//过期后清除该键值对
cacheMap.remove(key);
keys.remove(key);
}, expireTime, TimeUnit.MILLISECONDS);
}
```
我们put元素的时候,如果通过这个方法就能直接设置过期时间。
**完整源码如下:**
```java
/**
* @author shuang.kou
* <p>
* 使用 ConcurrentHashMap+ConcurrentLinkedQueue+ReadWriteLock+ScheduledExecutorService实现线程安全的 LRU 缓存
* 这里只是为了学习使用,本地缓存推荐使用 Guava 自带的,使用 Spring 的话,推荐使用Spring Cache
*/
public class MyLruCacheWithExpireTime<K, V> {
/**
* 缓存的最大容量
*/
private final int maxCapacity;
private ConcurrentHashMap<K, V> cacheMap;
private ConcurrentLinkedQueue<K> keys;
/**
* 读写锁
*/
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock writeLock = readWriteLock.writeLock();
private Lock readLock = readWriteLock.readLock();
private ScheduledExecutorService scheduledExecutorService;
public MyLruCacheWithExpireTime(int maxCapacity) {
if (maxCapacity < 0) {
throw new IllegalArgumentException("Illegal max capacity: " + maxCapacity);
}
this.maxCapacity = maxCapacity;
cacheMap = new ConcurrentHashMap<>(maxCapacity);
keys = new ConcurrentLinkedQueue<>();
scheduledExecutorService = Executors.newScheduledThreadPool(3);
}
public V put(K key, V value, long expireTime) {
// 加写锁
writeLock.lock();
try {
//1.key是否存在于当前缓存
if (cacheMap.containsKey(key)) {
moveToTailOfQueue(key);
cacheMap.put(key, value);
return value;
}
//2.是否超出缓存容量,超出的话就移除队列头部的元素以及其对应的缓存
if (cacheMap.size() == maxCapacity) {
System.out.println("maxCapacity of cache reached");
removeOldestKey();
}
//3.key不存在于当前缓存。将key添加到队列的尾部并且缓存key及其对应的元素
keys.add(key);
cacheMap.put(key, value);
if (expireTime > 0) {
removeAfterExpireTime(key, expireTime);
}
return value;
} finally {
writeLock.unlock();
}
}
public V get(K key) {
//加读锁
readLock.lock();
try {
//key是否存在于当前缓存
if (cacheMap.containsKey(key)) {
// 存在的话就将key移动到队列的尾部
moveToTailOfQueue(key);
return cacheMap.get(key);
}
//不存在于当前缓存中就返回Null
return null;
} finally {
readLock.unlock();
}
}
public V remove(K key) {
writeLock.lock();
try {
//key是否存在于当前缓存
if (cacheMap.containsKey(key)) {
// 存在移除队列和Map中对应的Key
keys.remove(key);
return cacheMap.remove(key);
}
//不存在于当前缓存中就返回Null
return null;
} finally {
writeLock.unlock();
}
}
/**
* 将元素添加到队列的尾部(put/get的时候执行)
*/
private void moveToTailOfQueue(K key) {
keys.remove(key);
keys.add(key);
}
/**
* 移除队列头部的元素以及其对应的缓存 (缓存容量已满的时候执行)
*/
private void removeOldestKey() {
K oldestKey = keys.poll();
if (oldestKey != null) {
cacheMap.remove(oldestKey);
}
}
private void removeAfterExpireTime(K key, long expireTime) {
scheduledExecutorService.schedule(() -> {
//过期后清除该键值对
cacheMap.remove(key);
keys.remove(key);
}, expireTime, TimeUnit.MILLISECONDS);
}
public int size() {
return cacheMap.size();
}
}
```
**测试效果:**
```java
MyLruCacheWithExpireTime<Integer,String> myLruCache = new MyLruCacheWithExpireTime<>(3);
myLruCache.put(1,"Java",3000);
myLruCache.put(2,"C++",3000);
myLruCache.put(3,"Python",1500);
System.out.println(myLruCache.size());//3
Thread.sleep(2000);
System.out.println(myLruCache.size());//2
```
<!-- TOC -->
- [Linux IO](#linux-io)
- [操作系统的内核](#操作系统的内核)
- [操作系统的用户态与内核态](#操作系统的用户态与内核态)
- [为什么要有用户态与内核态?](#为什么要有用户态与内核态)
- [用户态切换到内核态的几种方式](#用户态切换到内核态的几种方式)
- [阻塞和非阻塞](#阻塞和非阻塞)
- [同步与异步](#同步与异步)
- [Linux IO 模型](#linux-io模型)
- [阻塞 IO](#阻塞io)
- [非阻塞 IO(网络 IO 模型)](#非阻塞io网络io模型)
- [IO 多路复用(网络 IO 模型)](#io多路复用网络io模型)
- [信号驱动 IO(网络 IO 模型)](#信号驱动io网络io模型)
- [异步 IO](#异步io)
<!-- /TOC -->
# Linux IO
> 图源: https://www.jianshu.com/p/85e931636f27 (如有侵权,请联系俺,俺会立刻删除)
### 操作系统的内核
**操作系统的内核是操作系统的核心部分。它负责系统的内存,硬件设备,文件系统以及应用程序的管理。**
#### 操作系统的用户态与内核态
unix 与 linux 的体系架构:用户态与内核态。
用户态与内核态与内核态是操作系统对执行权限进行分级后的不同的运行模式。
![用户态与内核态](../../media/pictures/java/linux_io/用户态与内核态.png)
#### 为什么要有用户态与内核态?
在 cpu 的所有指令中,有些指令是非常危险的,如果使用不当,将会造成系统崩溃等后果。为了避免这种情况发生,cpu 将指令划分为**特权级(内核态)指令****非特权级(用户态)指令。**
**对于那些危险的指令只允许内核及其相关模块调用,对于那些不会造成危险的指令,就允许用户应用程序调用。**
- 内核态(核心态,特权态): **内核态是操作系统内核运行的模式。** 内核态控制计算机的硬件资源,如硬件设备,文件系统等等,并为上层应用程序提供执行环境。
- 用户态: **用户态是用户应用程序运行的状态。** 应用程序必须依托于内核态运行,因此用户态的态的操作权限比内核态是要低的,如磁盘,文件等,访问操作都是受限的。
- 系统调用: 系统调用是操作系统为应用程序提供能够访问到内核态的资源的接口。
#### 用户态切换到内核态的几种方式
- 系统调用: 系统调用是用户态主动要求切换到内核态的一种方式,用户应用程序通过操作系统调用内核为上层应用程序开放的接口来执行程序。
- 异常: 当 cpu 在执行用户态的应用程序时,发生了某些不可知的异常。于是当前用户态的应用进程切换到处理此异常的内核的程序中去。
- 硬件设备的中断: 当硬件设备完成用户请求后,会向 cpu 发出相应的中断信号,这时 cpu 会暂停执行下一条即将要执行的指令,转而去执行与中断信号对应的应用程序,如果先前执行的指令是用户态下程序的指令,那么这个转换过程也是用户态到内核台的转换。
#### 阻塞和非阻塞
1. 阻塞: 一个线程调用一个方法计算 1 - 100 的和,如果该方法没有返回结果,
那么调用方法的线程就一直等待直到该方法执行完毕。
2. 非阻塞: 一个线程调用一个方法计算 1 - 100 的和,该方法立刻返回,如果方法返回没有结果,
调用者线程也无需一直等待该方法的结果,可以执行其他任务,但是在方法返回结果之前,
**线程仍然需要轮询的检查方法是否已经有结果。**
**结论: 阻塞与非阻塞针对调用者的立场而言。**
#### 同步与异步
1. **同步**: 一个线程调用一个方法计算 1 - 100 的和,如果方法没有计算完,就不返回。
2. **异步**: 一个线程调用一个方法计算 1 - 100 的和,该方法立刻返回,但是由于方法没有返回结果,
所以就需要被调用的这个方法来通知调用线程 1 - 100 的结果,
或者线程在调用方法的时候指定一个回调函数来告诉被调用的方法执行完后就执行回调函数。
**结论:同步和异步是针对被调用者的立场而言的。**
### Linux IO 模型
Linux 下共有 5 种 IO 模型:
1. 阻塞 IO
2. 非阻塞 IO
3. IO 多路复用
4. 信号驱动 IO
5. 异步 IO
#### 阻塞 IO
阻塞 IO 是很常见的一种 IO 模型。在这种模型中,**用户态的应用程序会执行一个操作系统的调用,检查内核的数据是否准备好。如果内核的数据已经准备好,就把数据复制到用户应用进程。如果内核没有准备好数据,那么用户应用进程(线程)就阻塞,直到内核准备好数据并把数据从内核复制到用户应用进程,** 最后应用程序再处理数据。
![BIO原理](../../media/pictures/java/linux_io/BIO原理.png)
**阻塞 IO 是同步阻塞的。**
1. 阻塞 IO 的同步体现在: **内核只有准备好数据并把数据复制到用户应用进程才会返回。**
2. 阻塞 IO 的阻塞体现在:**用户应用进程等待内核准备数据和把数据从用户态拷贝到内核态的这整个过程,
用户应用进程都必须一直等待。** 当然,如果是本地磁盘 IO,内核准备数据的时间可能会很短。但网络 IO 就不一样了,因为服务端不知道客户端何时发送数据,内核就仍需要等待 socket 数据,时间就可能会很长。
**阻塞 IO 的优点是对于数据是能够保证无延时的,因为应用程序进程会一直阻塞直到 IO 完成。**但应用程序的阻塞就意味着应用程序进程无法执行其他任务,这会大大降低程序性能。一个不太可行的办法是为每个客户端 socket 都分配一个线程,这样就会提升 server 处理请求的能力。不过操作系统的线程资源是有限的,如果请求过多,可能造成线程资源耗尽,系统卡死等后果。
#### 非阻塞 IO(网络 IO 模型)
在非阻塞 IO 模型中,用户态的应用程序也会执行一个操作系统的调用,检查内核的数据是否准备完成。**如果内核没有准备好数据,
内核会立刻返回结果,用户应用进程不用一直阻塞等待内核准备数据,而是可以执行其他任务,但仍需要不断的向内核发起系统调用,检测数据是否准备好,这个过程就叫轮询。** 轮询直到内核准备好数据,然后内核把数据拷贝到用户应用进程,再进行数据处理。
![NIO原理](../../media/pictures/java/linux_io/NIO原理.png)
非阻塞 IO 的非阻塞体现在: **用户应用进程不用阻塞在对内核的系统调用上**
非阻塞 IO 的优点在于用户应用进程在轮询阶段可以执行其它任务。但这也是它的缺点,轮询就代表着用户应用进程不是时刻都会发起系统调用。
**可能数据准备好了,而用户应用进程可能等待其它任务执行完毕才会发起系统调用,这就意味着数据可能会被延时获取。**
#### IO 多路复用(网络 IO 模型)
在 IO 多路复用模型中,**用户应用进程会调用操作系统的 select/poll/epoll 函数,它会使内核同步的轮询指定的 socket,
(在 NIO 中,socket 就是注册到 Selector 上的 SocketChannel,可以允许多个)直至监听的 socket 有数据可读或可写,select/poll/epoll 函数才会返回,用户应用进程也会阻塞的等待 select/poll/epoll 函数返回。**
当 select/poll/epoll 函数返回后,即某个 socket 有事件发生了,用户应用进程就会发起系统调用,处理事件,将 socket 数据复制到用户进程内,然后进行数据处理。
![IO多路复用原理](../../media/pictures/java/linux_io/IO多路复用原理.png)
**IO 多路复用模型是同步阻塞的**
1. IO 多路复用模型的同步体现在: **select 函数只有监听到某个 socket 有事件才会返回。**
2. IO 多路复用模型的阻塞体现在: **用户应用进程会阻塞在对 select 函数上的调用上。**
**IO 多路复用的优点在于内核可以处理多个 socket,相当于一个用户进程(线程)就可以处理多个 socket 连接。**
这样不仅降低了系统的开销,并且对于需要高并发的应用是非常有利的。而非阻塞 IO 和阻塞 IO 的一个用户应用进程只能处理一个 socket,要想处理多 socket,只能新开进程或线程,但这样很消耗系统资源。
**PS:
在 IO 多路复用模型中, socket 一般应该为非阻塞的,这就是 Java 中 NIO 被称为非阻塞 IO 的原因。但实际上 NIO 属于 IO 多路复用,它是同步阻塞的 IO。具体原因见 [知乎讨论](https://www.zhihu.com/question/37271342)**
**PS:
select/poll/epoll 函数是 IO 多路复用模型的基础,所以如果想深入了解 IO 多路复用模型,就需要了解这 3 个函数以及它们的优缺点。**
#### 信号驱动 IO(网络 IO 模型)
在信号驱动 IO 模型中,**用户应用进程发起 sigaction 系统调用,内核收到并立即返回。用户应用进程可以继续执行其他任务,不会阻塞。当内核准备好数据后向用户应用进程发送 SIGIO 信号,应用进程收到信号后,发起系统调用,将数据从内核拷贝到用户进程,** 然后进行数据处理。
![信号驱动IO原理](../../media/pictures/java/linux_io/信号驱动IO原理.png)
个人感觉在内核收到系统调用就立刻返回这一点很像异步 IO 的方式了,不过与异步 IO 仍有很大差别。
#### 异步 IO
在异步 IO 模型中,**用户进程发起 aio_read 系统调用,无论内核的数据是否准备好,都会立即返回。用户应用进程不会阻塞,可以继续执行其他任务。当内核准备好数据,会直接把数据复制到用户应用进程。最后内核会通知用户应用进程 IO 完成。**
![异步IO原理](../../media/pictures/java/linux_io/异步IO原理.png)
**异步 IO 的异步体现在:内核不用等待数据准备好就立刻返回,所以内核肯定需要在 IO 完成后通知用户应用进程。**
---
```text
弄清楚了阻塞与非阻塞,同步与异步和上面5种IO模型,相信再看
Java中的IO模型,也只是换汤不换药。
```
- BIO : 阻塞 IO
- NIO : IO 多路复用
- AIO : 异步 IO
本来打算写 Java 中的 IO 模型的,发现上面几乎讲完了(剩 API 使用吧),没啥要写的了,
所以暂时就这样吧。如果各位同学有好的建议,欢迎 pr 或 issue。
<!-- TOC -->
* [完全使用GNU/Linux学习](#完全使用gnulinux学习)
* [为什么要写这篇文章?](#为什么要写这篇文章)
* [为什么我要从Windows切换到Linux?](#为什么我要从windows切换到linux)
* [Linux作为日常使用的缺点](#linux作为日常使用的缺点)
* [硬件驱动问题](#硬件驱动问题)
* [软件问题](#软件问题)
* [你真的需要完全使用Linux吗?](#你真的需要完全使用linux吗)
* [结尾](#结尾)
* [我使用Debian/Ubuntu时遇到的问题](#我使用debianubuntu时遇到的问题)
* [IDEA编辑Markdown预渲染问题](#idea编辑markdown预渲染问题)
* [wifi适配器找不到](#wifi适配器找不到)
* [XMind安装](#xmind安装)
* [Fcitx候选框的定位问题](#fcitx候选框的定位问题)
<!-- /TOC -->
# 完全使用GNU/Linux学习
喔,看到这个标题千万不要以为我要写和王垠前辈一样的内容啊,嘿嘿。不过在这里还是献上王垠前辈的那篇文章的链接吧:[完全用Linux工作](https://www.douban.com/group/topic/12121637/)
## 为什么要写这篇文章?
首先介绍本篇文章产出的时间,现在是2020/04/06。在三,四天之前,我其实并没有写这篇文章的打算,但是这三,四天以来,我一直在忙活从Ubuntu18换到Debian10 Buster的事情,没有时间写代码,手确实有些痒了。你可能想象不到,我这个之前一直使用Ubuntu的人,只是切换到Debian就花这么长时间,你可能以为我是在劝退各位同学,其实不是的,我只是想表达:我对Linux并不熟悉,这其中一部分原因是我使用的是对用户较为友好的发行版Ubuntu,另一部分原因是我仍然没有那么大的动力去学习Linux,即使它一直作为我的日常使用。
这篇文章并不是吹嘘或贬低Windows和Linux系统,而是想记录一下我一直以来使用Linux作为日常学习的心得,以及这几天再度折腾Debian以来的感触。
## 为什么我要从Windows切换到Linux?
Windows是商业软件,这使它具备易用的性质。Linux是自由软件,这使得它拥有开源的性质。
易用软件通常带来的是对用户的友好度,以致于Windows发展至今,被许许多多的普通用户所采用。自由软件通常带来的是其社区的发展,所以你现在可以在网上看到许多如 ask ubuntu 这样的论坛。
我非常赞同《完全用Linux工作》中的一个观点: **UNIX 不是计算机专家的专利。**
我对这句话的理解就是:即使你学习或工作的方向不是计算机,但你仍然可以去学习Unix/Linux,如果你是计算机方向的同学,那么,你就更应该去学习Unix/Linux了。
但这只是我从Win切换到Linux的一部分原因,另一个很重要的原因是我受够了Windows的 “易用性”。这里的易用性并不是说我排斥Windows的人性化,而是因为人性化给我带来了很多学习上的困难。举个很简单的栗子:你在学习一项技术的时候,无论是否有面试造火箭的需要,你是否都会好奇想了解其原理和实现,即使你可能知道它很复杂。
**为什么你会好奇一个事物的源头?**
我个人认为的答案是:有趣的事情就在眼前,为什么不去了解它呢?
而Windows只是有趣,但它并不在“眼前”。
我个人的体验哈,不知道有没有同学和我一样的经历,在很多时候,你的Windows可能会出现一些莫名奇妙的问题,但你却不知道如何解决它,你只能求助搜索引擎,当你解决完问题后,你不会想要去了解为什么会发生这种问题,因为Windows太庞大了。
就比如: 我现在安装了Git,使用起来没有任何问题。但等到过一段时间后,Git莫名奇妙的不能使用了,明明你啥都没干。更甚之,有一些流氓问题或流氓软件不能被解决和被屏蔽。
问题出现了,总得要解决吧,所以此时你开始在互联网上查询相关问题的解决方法,如果你的运气好,那么有人可能遇到过和你出现相同的问题,你也因此可能会得到答案。不过一般的答案只是教你怎么解决的,如打开注册表,添加或删除某个key,你不会想要知道为什么做,因为对于初学者来说,当你看到注册表那么多的内容时,你只想着不出错就行了,哪还有心思去学习这玩意啊。如果你的运气不好,且并没有更换系统的打算,那么你可能会将就着使用,但此时,你的心里可能已经衍生了对Windows的厌烦情绪。
我对流氓软件的定义是:当你想让一个软件如你的想法停止运行或停止弹出广告的时候,这个软件不能或不能做的很好的达到你的要求时,这就是一个流氓软件。你也许会说,每个人都有不同的要求,软件怎么可能达到每个人的标准呢?但我指的是停止和停止弹出广告等这样最基本的诉求,如果一个软件连最基本的诉求都实现不了,又何必再使用它呢?
综上所述,我从Window切换到Linux的最主要的原因有:**学习和自由。**
是的,你不得不承认Linux是你学习计算机的非常好的环境,与C/C++天然的集成,比你在Windows上冷冰冰的安装一个IDE就开始敲起代码来,显得多了那么一点感觉。
还有一点,可能有的同学和我一样,刚接触Linux的时候,是在Windows上安装一个虚拟机环境或使用Docker来进行学习。不可否认,这确实是在Windows上学习Linux的主要途径了,但是你有没有感觉到,你在采取这种方式学习的时候,对Linux始终有种陌生感,似乎我只是在为了学习而学习。
产生这种想法的主要原因就是你没有融入到Linux环境之中,当你融入到Linux环境之中时,你不再只是需要学习那些操作命令,你会不可避免的遇到某个你从来没有接触过的问题,这个问题不是你在Windows上“丢失图标”的那种烦人问题,而可能是令你有些害怕的因为Nvidia的驱动而黑屏的问题。你也会在互联网上查询为什么会出现这种问题,但你得到的并不是“修改注册表”这种答案,而是会学习到:为什么Nvidia在Linux上会出现这种问题?我怎么做才能解决驱动问题?其他驱动是否也有类似Nvidia这种问题? 当你解决问题后,你的电脑开始正常工作了,你便开始使用它作为你的日常使用...
关于使用Linux学习的原因的最后一点是我认为自己不够慎独,不够克制。当我使用Windows的时候,并不能完全克制住自己接触那些新鲜游戏的念头,我玩起游戏来,通常会连续很长时间,可能是一天-_-。不过我并不是说Linux上没有游戏,相反,Linux是对很多游戏的支持是很好的,你可以玩到很多游戏,但你是否会因为使用Linux对游戏不再那么执着,至少我是如此了。这一点可以归结为“使用Linux对戒游戏有帮助吧” ,哈哈。
再谈谈自由:
我对自由的理解是:软件在你的掌控之中,你可以了解它的每一部分,你可以去到你想到达的地方,不受任何限制,这只取决于你愿不愿意。
来看看基本的Linux目录吧:
![Linux目录](../../media/pictures/linux/Linux目录.png)
这些目录你可能有很多都不认识,但没关系,因为这就是Linux系统(大部分)所有的目录了,你稍微了解下,就知道这些目录里放的是什么文件了。
这也是我个人的体验而已,总之,Linux的自由是一种开源精神,比我描述的可大的多。至于Windows,我到现在连C盘的目录放了些什么都不太熟悉,但我并不是在贬低Windows,因为这就是Windows易用性的代价,相应的,Linux作为自由软件,它也有很多缺点。
## Linux作为日常使用的缺点
### 硬件驱动问题
硬件驱动问题一般是在安装Linux时会出现的问题,根据个人电脑配置的不同,你的电脑的硬件驱动可能与要安装的Linux发行版不兼容,导致系统出现相应的问题。我这几天对驱动问题最深刻的体会就明白了为啥Linus大神会吐槽: “Nvidia Fuck You”。很多驱动厂商对Linux系统是闭源的,你可以下载这些厂商的驱动,但是能不能用,或者用起来有什么毛病都得你自己买单。
随着Linux开始在普通用户中变得流行起来,我相信今后Linux的生态会发展的越来越好,且现在很多Linux发行版对各种硬件的兼容性也越来越好,就以我之前使用的Ubuntu18来说,Nvidia,Wifi,蓝牙等驱动使用都是没啥问题的。我现在使用的Debian10 Buster对Nvidia的支持可能还不是那么好,使用起来总有一些小毛病,不过无伤大雅,其实没毛病我还有点不适应,不是说Debian是Ubuntu的爸爸吗,哈哈。
### 软件问题
不得不承认的一点是Linux的软件生态确实没有Windows那么丰富,你在考虑切换系统之前,必须先调查清楚Linux上是否有你必需的软件,你所需的软件是否支持跨平台或者是否有可替代的应用。我个人对软件要求较为简单,大部分都是生产力工具,其他的应用如娱乐软件之类的都可以使用网页版作为替代。如果你在Linux系统上想尝试游戏的话,我认为也是OK的,因为我也尝试过Linux Dota2 ,体验非常好(不是广告-_-)。不过大多数国内游戏厂商对Linux的支持都是很差的,所以如果过不了这道坎,也不要切换系统了。
软件问题其实可以分为2部分看待,一部分就是刚刚介绍过的生态问题,另一部分就是当你在使用某些软件的时候,总会出现某些小Bug。
就以Fcitx来说,Fcitx是一款通用的Linux输入法框架,被称为小企鹅输入法,很多输入法都是在Fcitx之上开发的,如搜狗,Googlepinyin,Sunpinyin等。使用过Fcitx的同学可能会遇到这种问题:当你在使用Fcitx在某些软件上打字时,候选框并不会跟随你光标的位置,而是总会固定在某一个位置,并且你无法改变,这个问题是我目前见过的最大Bug。不过这个Bug只在部分软件上有,在Chrome,Typora上都没有这个问题,这让我怀疑是软件的国际化问题,而非Fcitx问题。
所以第二个部分总结起来就是某些软件可能会出现某些未知的Bug,你得寻求解决的办法,或者忍耐使用,使用Linux也是得牺牲一些代价的。
## 你真的需要完全使用Linux吗?
说到这里,其实我想借用知乎某位前辈的话来表达一下我的真实想法: “**Linux最好的地方在与开放自由,最大的毛病也是在这里。普通人没有能力去选择,也没有时间做选择。透明就一定好么?也有很多人喜欢被安排啊!**“ ([知乎 - 汉卿](https://www.zhihu.com/question/309704636))
就像我开头说过的: “我对Linux并不熟悉,这其中一部分原因是我使用的是对用户较为友好的发行版Ubuntu,另一部分原因是我仍然没有那么大的动力去学习Linux,即使它一直作为我的日常使用。”
我完全使用Linux是为了学习和自由,我确实在Linux上感受到了自由,且学到了很多东西,但我却一直沉溺在这种使用Linux带来的满足感之中,并不能真正理解Linux给我们带来的到底是什么。
这次从Ubuntu切换到Debian的原因是我想尝试换个新的环境,但是当我花了3,4天后,我明白了:我只是呆在一个地方久了,想换个新地方而已,但老地方不一定坏,因为我都没怎么了解过这个老地方,就像当初我从Windows换到Linux那样,我都没有深入的了解过Windows就换了,那一段时间我还抱怨Windows的各种缺点,现在看来,非常可笑。
#### 结尾
一文把想说的话几乎都给说了,个人文笔有限,且本文主观意识太强,如果觉得本文不符合您的胃口,就当看个笑话吧。
---
## 我使用Debian/Ubuntu时遇到的问题
**以下内容是我在Debian10 Buster下遇到的问题以及相关解决办法,
使用Ubuntu和Debian其他版本的同学也可借鉴。**
PS:欢迎各位同学在此处写下你遇到的问题和解决办法。
### IDEA编辑Markdown预渲染问题
这个问题花了我很长时间。
当我安装IDEA后,使用它编辑markdown文件的时候,就出现了如下图所示的情况:
![Debian10下IDEA的Markdown预渲染问题](../../media/pictures/linux/Debian10下IDEA的Markdown预渲染问题.png)
你可以看到右边渲染的画面明显有问题。刚开始的时候我一度怀疑是IDEA版本的问题,
于是我又安装IDEA其他版本,但也没有任何作用,这时我怀疑是显卡的原因:
![我的电脑配置](../../media/pictures/linux/我的电脑配置.png)
可以看到使用的是Intel的核显,于是当我查询相关资料,使用脚本将核显换为了独显,这里没留截图,当你换到独显后,
图形会显示独显的配置,使用nvidia-smi命令可以查看独显使用状态。
于是我满怀期待的打开IDEA,但还是无济于事。当我以为真的是Debian的Bug的时候,
我又发现Bumblebee可以管理显卡,何不一试?于是我安装Bumblebee后,使用optirun命令启动IDEA,没想到啊,
还真是可以:
![Debian10下IDEA的Markdown预渲染解决后](../../media/pictures/linux/Debian10下IDEA的Markdown预渲染解决后.png)
我真的就很奇怪,同样是使用了独显,为什么optirun启动就可以正常显示。
于是我后来又查询optirun是否开启了gpu加速,但很可惜,我并没有得到相关答案,不过这让我确定了这个问题出现在
显卡上。如果有知道原因的同学,敬请告之,感激不尽。
### wifi适配器找不到
我猜(不确定)这个问题应该发生在大多数使用联想笔记本的同学的电脑上,不止Debian,且Ubuntu也有这个问题。
当安装完系统后,我们打开设置会发现wifi一栏显示 “wifi适配器找不到” 此类的错误信息。
这个问题的大概原因是:无线网络适配器被阻塞了,需要手动将电脑上的wifi开关打开,而在我的笔记本上并wifi开关,
所以可以猜测是联想网络驱动的问题。
可以使用 rfkill list all命令查询你的wlan是否被阻塞了,没有此命令的同学可以使用
````text
sudo apt-get install rfkill
````
安装,当wlan显示Hard blocked: true , 就证明你的无线驱动被阻塞了。
解决办法是将阻塞无限驱动的那个模块从内核中移除掉,直接在 /etc/modprobe.d
目录下编辑 blacklist.conf文件,其内容为:
````text
blacklist ideapad_laptop
````
文件名不一定要与我的一致,但是要以.conf结尾。
你可以将modprobe.d目录下的文件理解为黑名单文件,
当Linux启动时就不会加载conf文件指定的模块,
这里的 ideapad_laptop 就是我们需要移除的那个无线模块。
**后遗症:
当我们移除 ideapad_laptop 模块后,以后开机的时候,有时会出现
蓝牙适配器找不到的情况,之前在Ubuntu上却并未发现这种问题,
看来Debian在驱动方面没有Ubuntu做的好,不过这也是可以理解的,
而且大多数时候还是可以正常使用的-_-。**
### XMind安装
XMind是使用Java编写的,依赖于Openjdk8。所以在Linux上使用XMind,
首先需要有Openjdk8的环境。
其次启动的时候需要编写Shell脚本来启动(不是唯一办法,但却是非常简单的办法),没想到吧,我也没想到,
这也是我趟过很多坑才玩出来的。
首先我们需要准备一张XMind的软件启动图片:XMind.png,
这个我已经放到[目录](https://github.com/guang19/framework-learning/tree/dev/img/linux)
下了,需要的同学请自取。
其次我们进入XMind_amd64目录下,32位系统的同学进入XMind_i386目录,
我们创建并编辑 start.sh 脚本,其内容为:
````text
#!/bin/bash
cd /home/guang19/SDK/xmind/XMind_amd64 (这个路径为你的XMind脚本的路径)
./XMind
````
这个脚本的内容很简单吧,当启动脚本的时候,进入目录,直接启动XMind。
脚本写完后需要让它能够被执行,使用
````text
chmod +x start.sh
````
命令让start.sh可以被执行。
此时你可以尝试执行 ./start.sh 命令来启动XMind,启动成功的话,
就已经完成了99%了,如果启动不成功,可以再检测下前面的步骤是否有误。
如果以后你只想用Shell启动XMind的话,那么到此也就为止了,连上面所说的图片都不需要了。
如果你想更方便的启动的话,那么就需要创建桌面文件启动。
在Debian/Ubuntu下,你所看到的桌面文件,都存储在 /usr/share/applications
目录下面(也有的在.local/share/applications目录下),这个目录下文件全是以.desktop结尾。
我们现在就需要在这个目录下创建xmind.desktop文件(名字可以不叫xmind)。
其内容为:
````text
[Desktop Entry]
Encoding=UTF-8
Name=XMind
Type=Application
Exec=sh /home/guang19/SDK/xmind/XMind_amd64/start.sh
Icon=/home/guang19/SDK/xmind/XMind.png
````
我们暂时只需要理解Icon和Exec属性。
Icon就是你在桌面上看到的应用的图标,把Icon的路径改为你XMind.png的路径就行了。
再看Exec属性,当我们在桌面上点击XMind的图标的时候,就会执行Exec对应的命令或脚本,
我们把Exec改为start.sh文件的路径就行了,别掉了sh命令,因为start.sh是脚本,
需要sh命令启动。
以上步骤完成,保存desktop文件后,你就可以在桌面上看到XMind应用了。
### Fcitx候选框的定位问题
这个问题贴一张我处境的截图就明白了:
![Fcitx候选框定位问题](../../media/pictures/linux/Fcitx候选框定位问题.png)
可以看到我的光标定位在第207行,但是我输入法的候选框停留在IDEA的左下角。
为什么我要说停留在IDEA的左下角?因为就目前我的使用而言,这个问题只在IDEA下存在,
不仅是Debian,Ubuntu也存在这种问题,我个人认为这应该是IDEA的问题,
查到的相关文章大部分都是说Swing的问题,看来这个问题还真是比较困难了。
如果有同学知道解决办法,还请不吝分享,非常感谢。
\ No newline at end of file
......@@ -8,8 +8,6 @@ ID是数据的唯一标识,传统的做法是利用UUID和数据库的自增ID
这篇文章并不会分析的特别详细,主要是做一些总结,以后再出一些详细某个方案的文章。
## 数据库自增ID
第一种方案仍然还是基于数据库的自增ID,需要单独使用一个数据库实例,在这个实例中新建一个单独的表:
......
......@@ -209,5 +209,6 @@ Guide 制作了一个涵盖上面所有重要内容的思维导图,便于小
1. 《阿里巴巴 Java 开发手册》
2. 《Clean Code》
3. Google Java 代码指南:https://google.github.io/styleguide/javaguide.html#s5.1-identifier-name
4. 告别编码5分钟,命名2小时!史上最全的Java命名规范参考:https://www.cnblogs.com/liqiangchn/p/12000361.html
> 下面的 10 个项目还是很推荐的!JS 的项目占比挺大,其他基本都是文档/学习类型的仓库。
说明:数据统计于 2019-11-27。
### 1. freeCodeCamp
- **Github地址**[https://github.com/freeCodeCamp/freeCodeCamp](https://github.com/freeCodeCamp/freeCodeCamp)
- **star**: 307 k
- **介绍**: 开放源码代码库和课程。与数百万人一起免费学习编程。网站:[https://www.freeCodeCamp.org](https://www.freecodecamp.org/) (一个友好的社区,您可以在这里免费学习编码。它由捐助者支持、非营利组织运营,以帮助数百万忙碌的成年人学习编程技术。这个社区已经帮助10,000多人获得了第一份开发人员的工作。这里的全栈Web开发课程是完全免费的,并且可以自行调整进度。这里还有数以千计的交互式编码挑战,可帮助您扩展技能。)
比如我想学习 ES6 的语法,学习界面是下面这样的,你可以很方便地边练习边学习:
![Learn ES6](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/freecodemap-es6.jpg)
### 2. 996.ICU
- **Github地址**[https://github.com/996icu/996.ICU](https://github.com/996icu/996.ICU)
- **star**: 248 k
- **介绍**: `996.ICU` 是指“工作 996, 生病 ICU” 。这是中国程序员之间的一种自嘲说法,意思是如果按照 996 的模式工作,那以后就得进 ICU 了。这个项目最早是某个中国程序员发起的,然后就火遍全网,甚至火到了全世界很多其他国家,其网站被翻译成了多种语言。网站地址:[https://996.icu](https://996.icu/)
![996.ICU-website](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/996.icu.jpg)
### 3. vue
- **Github地址**[https://github.com/vuejs/vue](https://github.com/vuejs/vue)
- **star**: 153 k
- **介绍**: 尤大的前端框架。国人用的最多(容易上手,文档比较丰富),所以 Star 数量比较多还是有道理的。Vue (读音 /vjuː/,类似于 **view**) 是一套用于构建用户界面的**渐进式框架**。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与[现代化的工具链](https://cn.vuejs.org/v2/guide/single-file-components.html)以及各种[支持类库](https://github.com/vuejs/awesome-vue#libraries--plugins)结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
### 4. React
- **Github地址**[https://gitstar-ranking.com/facebook/react](https://gitstar-ranking.com/facebook/react)
- **star**: 140 k
- **介绍**: Facebook 开源的,大公司有保障。用于构建用户界面的声明式、基于组件开发,高效且灵活的JavaScript框架。我司大部分项目的前端都是 React ,我自己也用过一段时间,感觉还不错,但是也有一些小坑。
### 5. tensorflow
- **Github地址**[https://github.com/tensorflow/tensorflow](https://github.com/tensorflow/tensorflow)
- **star**: 138 k
- **介绍**: 适用于所有人的开源机器学习框架。[TensorFlow](https://www.tensorflow.org/)是用于机器学习的端到端开源平台。TensorFlow最初是由Google机器智能研究组织内Google Brain团队的研究人员和工程师开发的,用于进行机器学习和深度神经网络研究。该系统具有足够的通用性,也可以适用于多种其他领域。TensorFlow提供了稳定的[Python](https://www.tensorflow.org/api_docs/python)[C ++](https://www.tensorflow.org/api_docs/cc) API,以及[其他语言的](https://www.tensorflow.org/api_docs)非保证的向后兼容API 。
### 6. bootstrap
- **Github地址**[https://github.com/twbs/bootstrap](https://github.com/twbs/bootstrap)
- **star**: 137 k
- **介绍**: 相信初学前端的时候,大家一定或多或少地接触过这个框架。官网说它是最受欢迎的HTML,CSS和JavaScript框架,用于在网络上开发响应式,移动优先项目。
### 7. free-programming-books
- **Github地址**[https://github.com/EbookFoundation/free-programming-books](https://github.com/EbookFoundation/free-programming-books)
- **star**: 132 k
- **介绍**: 免费提供的编程书籍。我自己没太搞懂为啥这个项目 Star 数这么多,知道的麻烦评论区吱一声。
### 8. Awesome
- **Github地址**[https://github.com/sindresorhus/awesome](https://github.com/sindresorhus/awesome)
- **star**: 120 k
- **介绍**: github 上很多的各种 Awesome 系列合集。
下面是这个开源仓库的目录,可以看出其涵盖了很多方面的内容。
<img src="https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/awsome-contents.jpg" style="zoom:50%;" />
举个例子,这个仓库里面就有两个让你的电脑更好用的开源仓库,Mac 和 Windows都有:
- Awesome Mac:https://github.com/jaywcjlove/awesome-mac/blob/master/README-zh.m
- Awsome Windows: https://github.com/Awesome-Windows/Awesome/blob/master/README-cn.md
### 9. You-Dont-Know-JS
- **Github地址**[https://github.com/getify/You-Dont-Know-JS](https://github.com/getify/You-Dont-Know-JS)
- **star**: 112 k
- **介绍**: 您还不认识JS(书籍系列)-第二版
### 10. oh-my-zsh
- **Github地址**[https://github.com/ohmyzsh/ohmyzsh](https://github.com/ohmyzsh/ohmyzsh)
- **star**: 99.4 k
- **介绍**: 一个令人愉快的社区驱动的框架(拥有近1500个贡献者),用于管理zsh配置。包括200多个可选插件(rails, git, OSX, hub, capistrano, brew, ant, php, python等),140多个主题,可为您的早晨增光添彩,以及一个自动更新工具,可让您轻松保持与来自社区的最新更新……
下面就是 oh-my-zsh 提供的一个花里胡哨的主题:
![oh-my-zsh-theme](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/ohmyzsh-theme.png)
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册