1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-16 18:10:13 +08:00

Merge branch 'Snailclimb:main' into main

This commit is contained in:
ZapBird 2022-08-08 20:32:55 +08:00 committed by GitHub
commit 9d2ac598ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
86 changed files with 585 additions and 277 deletions

View File

@ -90,11 +90,12 @@
**重要知识点详解**
- [JMMJava 内存模型)详解](./docs/java/concurrent/jmm.md)
- **线程池** [Java 线程池详解](./docs/java/concurrent/java-thread-pool-summary.md)、[Java 线程池最佳实践](./docs/java/concurrent/java-thread-pool-best-practices.md)
- [ThreadLocal 关键字解析](docs/java/concurrent/threadlocal.md)
- [ThreadLocal 详解](docs/java/concurrent/threadlocal.md)
- [Java 并发容器总结](docs/java/concurrent/java-concurrent-collections.md)
- [Atomic 原子类总结](docs/java/concurrent/atomic-classes.md)
- [AQS 原理以及 AQS 同步组件总结](docs/java/concurrent/aqs.md)
- [AQS 详解](docs/java/concurrent/aqs.md)
- [CompletableFuture入门](docs/java/concurrent/completablefuture-intro.md)
### JVM (必看 :+1:)

View File

@ -104,11 +104,11 @@ export const sidebarConfig = defineSidebarConfig({
icon: "star",
collapsable: true,
children: [
"jmm",
"java-thread-pool-summary",
"java-thread-pool-best-practices",
"java-concurrent-collections",
"aqs",
"reentrantlock",
"atomic-classes",
"threadlocal",
"completablefuture-intro",

View File

@ -103,18 +103,22 @@ star: 2
## 如何加入?
两年前,星球的定价是 **50/年** ,这是星球的最低定价,我还附送了 33 元优惠券。扣除了星球手续费,发了各种福利之后,几乎就是纯粹做公益。感兴趣的小伙伴可以看看我在 2020-01-03 发的头条:[做了一个很久没敢做的事情](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486049&idx=1&sn=e0161b409e8f164251bdaa0c83a476bc&chksm=cea245aaf9d5ccbcafdb95a546d959508814085620aabdbb4385c4b8cea6e50bf157c3697041&token=1614894361&lang=zh_CN#rd),去考古一下。
两年前,星球的定价是 **50/年** ,这是星球的最低定价,我还附送了 33 元优惠券。扣除了星球手续费,发了各种福利之后,几乎就是纯粹做公益。
随着时间推移,星球积累的干货资源越来越多,我花在星球上的时间也越来越多。于是,星球的定价慢慢被调整为了 **159/年** **续费半价**
感兴趣的小伙伴可以看看我在 2020-01-03 发的头条:[做了一个很久没敢做的事情](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486049&idx=1&sn=e0161b409e8f164251bdaa0c83a476bc&chksm=cea245aaf9d5ccbcafdb95a546d959508814085620aabdbb4385c4b8cea6e50bf157c3697041&token=1614894361&lang=zh_CN#rd),去考古一下。
目前已经有 **1.3w+** 人加入星球,随着加入的人数增加,这个定价还会继续调整,越早加入越划算!
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/xingqiu/image-20220311203414600.png)
为了感谢大家的支持,我这里再送出一张新人优惠券,使用优惠券 **139/年** 加入(数量有限,即将调整为 **199/年**)。
随着时间推移,星球积累的干货资源越来越多,我花在星球上的时间也越来越多。于是,我将星球的定价慢慢调整为了 **159/年**!后续会将星球的价格调整为 **199/年**,想要加入的小伙伴一定要尽早。
你可以添加我的微信(没有手机号再申请微信,故使用企业微信。不过,请放心,这个号的消息也是我本人处理,平时最常看这个微信)领取星球专属优惠券,限时 **130/年** 加入(续费半价)
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/IMG_3007.jpg)
或者你也可以直接使用下面这张 **20** 元的优惠券,**139/年** 加入。
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/xingqiu/xingqiuyouhuijuan-20.png)
如果你在犹豫要不要加入的话,我建议你进来体验一下,三天之内知识星球 APP 右上角可以申请全额退款,秒到账!绝对不会坑大家!
进入星球之后,你可以为自己制定一个目标,比如自己想要进入某某还不错的公司或者达成什么成就(一定要是还算有点挑战的目标)。待你完成目标在星球分享之后,我会将星球的门票费退还给你。
**真诚欢迎准备面试的小伙伴加入星球一起交流!真心希望能够帮助到更多小伙伴!**

View File

@ -7,6 +7,7 @@ head:
- - meta
- name: keywords
content: 操作系统,进程,进程通信方式,死锁,操作系统内存管理,块表,多级页表,虚拟内存,页面置换算法
- - meta
- name: description
content: 很多读者抱怨计算操作系统的知识点比较繁杂,自己也没有多少耐心去看,但是面试的时候又经常会遇到。所以,我带着我整理好的操作系统的常见问题来啦!这篇文章总结了一些我觉得比较重要的操作系统相关的问题比如进程管理、内存管理、虚拟内存等等。
---

View File

@ -76,7 +76,7 @@ MySQL 没有命中缓存,那么就会进入分析器,分析器主要是用
### 2.1 查询语句
说了以上这么多,那么究竟一条 SQL 语句是如何执行的呢?其实我们的 SQL 可以分为两种,一种是查询,一种是更新(增加,更新,删除)。我们先分析下查询语句,语句如下:
说了以上这么多,那么究竟一条 SQL 语句是如何执行的呢?其实我们的 SQL 可以分为两种,一种是查询,一种是更新(增加,修改,删除)。我们先分析下查询语句,语句如下:
```sql
select * from tb_student A where A.age='18' and A.name=' 张三 ';

View File

@ -8,6 +8,7 @@ head:
- - meta
- name: keywords
content: MySQL基础,MySQL基础架构,MySQL存储引擎,MySQL查询缓存,MySQL事务,MySQL锁等内容。
- - meta
- name: description
content: 一篇文章总结MySQL常见的知识点和面试题涵盖MySQL基础、MySQL基础架构、MySQL存储引擎、MySQL查询缓存、MySQL事务、MySQL锁等内容。
---

View File

@ -1,5 +1,5 @@
---
title: 缓存基础常见面试题总结
title: 缓存基础常见面试题总结(付费)
category: 数据库
tag:
- Redis

View File

@ -1,5 +1,5 @@
---
title: Redis 集群详解
title: Redis 集群详解(付费)
category: 数据库
tag:
- Redis

View File

@ -7,6 +7,7 @@ head:
- - meta
- name: keywords
content: Redis常见数据结构
- - meta
- name: description
content: Redis基础数据结构总结String字符串、List列表、Set集合、Hash散列、Zset有序集合
---

View File

@ -7,6 +7,7 @@ head:
- - meta
- name: keywords
content: Redis常见数据结构
- - meta
- name: description
content: Redis特殊数据结构总结HyperLogLogs基数统计、Bitmap 位存储、Geospatial (地理位置)。
---

View File

@ -7,6 +7,7 @@ head:
- - meta
- name: keywords
content: Redis基础,Redis常见数据结构,Redis线程模型,Redis内存管理,Redis事务,Redis性能优化
- - meta
- name: description
content: 一篇文章总结Redis常见的知识点和面试题涵盖Redis基础、Redis常见数据结构、Redis线程模型、Redis内存管理、Redis事务、Redis性能优化等内容。
---

View File

@ -7,6 +7,7 @@ head:
- - meta
- name: keywords
content: Redis基础,Redis常见数据结构,Redis线程模型,Redis内存管理,Redis事务,Redis性能优化
- - meta
- name: description
content: 一篇文章总结Redis常见的知识点和面试题涵盖Redis基础、Redis常见数据结构、Redis线程模型、Redis内存管理、Redis事务、Redis性能优化等内容。
---

View File

@ -1,5 +1,5 @@
---
title: 分布式事务
title: 分布式事务(付费)
category: 分布式
---

View File

@ -1,5 +1,5 @@
---
title: 降级&熔断
title: 降级&熔断(付费)
category: 高可用
---

View File

@ -5,6 +5,7 @@ head:
- - meta
- name: keywords
content: 客户端负载均衡,服务负载均衡,Nginx,负载均衡算法,七层负载均衡,DNS解析
- - meta
- name: description
content: 负载均衡指的是将用户请求分摊到不同的服务器上处理,以提高系统整体的并发处理能力。负载均衡可以简单分为服务端负载均衡和客户端负载均衡 这两种。服务端负载均衡涉及到的知识点更多,工作中遇到的也比较多,因为,我会花更多时间来介绍。
---

View File

@ -7,6 +7,7 @@ head:
- - meta
- name: keywords
content: RabbitMQ,AMQP,Broker,Exchange,优先级队列,延迟队列
- - meta
- name: description
content: RabbitMQ 是一个在 AMQPAdvanced Message Queuing Protocol 基础上实现的可复用的企业消息系统。它可以用于大型软件系统各个模块之间的高效通信支持高并发支持可扩展。它支持多种客户端如Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等支持AJAX持久化用于在分布式系统中存储转发消息在易用性、扩展性、高可用性等方面表现不俗。
---

View File

@ -1,5 +1,5 @@
---
title: 优质面经
title: 优质面经(付费)
category: 知识星球
---

View File

@ -1,5 +1,5 @@
---
title: 常见面试题自测
title: 常见面试题自测(付费)
category: 知识星球
---

View File

@ -1,5 +1,5 @@
---
title: 手把手教你如何准备面试
title: 手把手教你如何准备面试(付费)
category: 知识星球
---

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -1 +1 @@
<mxfile host="Electron" modified="2021-11-24T03:52:30.002Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="bvsmkjngkIq_COWrT3rC" version="13.4.5" type="device"><diagram id="KJUpccsywgqX94dHTPwA" name="Page-1">7VfbjpswEP0aS+1DKi6BhEcgJG2lqqtut7vtS+WAAygOzhrn1q/vGEy4ZndVKbsvVR7iOR57xnPOgEGmvzkuON4mX1hEKDK06IjMGTIMXdds+JPIqUQM3VZIzNNIedXAbfqHKFBT6C6NSN5yFIxRkW7bYMiyjISihWHO2aHttmK0HXWLY9IDbkNM++h9GomkRKfGpMY/kjROqsi67ZQzG1w5q5PkCY7YoQGZATJ9zpgoR5ujT6isXlWXct38wuw5MU4y8ZIFEfua5d/uKLF/Z/78093NXbgYqV32mO7UgVFgoamFXB8FY+T5yJkUyBx5MJgXiPsZ7/FCEoMMm0Jwb8nlAcVJVc1+3MlTeSuWiVFecOqCg25vQRhePQ+juPiXoWbIgZhTNNVk/CpUGQYycEzkWqqS50CGIEe5QSI2FAAdhrngbE18RhkHJGMZkYmklHYgTNM4AzOE8hHAvT3hIgXiXTWxSaNIhvEOSSrI7RaHMuYBdA4YZ7ssIrKyGlgULwn1cLiOC7wbHKqghK3byq5ckGF6xa/YU2CRMhl7ZMpt8zURYaLO1ae74g7yJscGpOhfELYhgp/ARc0aEyXFQ61kfaqwpKlihWHVPPF5q1pfMFASG5bbd5NnHv/z6zG4If6PKIzu3fVIt/t6UxKSVXmJiAwTRNRTULbbGO+kTNwAOYFS7NR4X+0O2ZYBlOC6Oqr51C8w3pBYTHGeV3KrulrvEtbUHNC8WhE7DHsChZlo4iw17ZxVj88B1i9S7KgWUY9bc4Bwe4Bw62qET69HuP6fcCBcaxEOHWb1KZ+8JuVy/QXK8y3O/p3yZcE3vCdcY5Dvcvc345vokUUmQ3w79sTE9lUaXH/zDjfGV+pw/Azdb9zer0R3p72H3uCv291Db/BO8UkWufLqLW9XsrRp+RTEXPThBg1QEn56UFeqwvgpjQ9WZc6OzcnZqbKOqXhojBurwKoXSeO8JoJbvsqYcZGwmGWYBjWqyJN+T1MHJ2c7HpIXPBGhAjERz/ZSXwwNtq0nyOaEwgVy3054SAEqwg1LiwZVWhu3tda9BJbHVGua3xmdbbqSHXf2KavQ2wekgU8Nt610yC9na3aytawnszK7WbXcYVDGrxvjXP6hXgGz/nAr3evvXzP4Cw==</diagram></mxfile>
<mxfile host="Electron" modified="2022-08-05T03:30:17.917Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="u1IF9P1wmilqNJ8enUse" version="13.4.5" type="device"><diagram id="KJUpccsywgqX94dHTPwA" name="Page-1">7VfJbtswEP0aAu3BgRZLto6SraRo0SJFisTpjZZoSTUtOhS95es7lKhdTt0CSS5FDibfkJzRvHkTEpmzzfGG4238lYWEIkMLj8icI8PQdc2GH4mcCsTQbYVEPAnVqhq4S56JAjWF7pKQZK2FgjEqkm0bDFiakkC0MMw5O7SXrRhte93iiPSAuwDTPvqQhCIu0KkxqfFPJIni0rNuO4Vlg8vF6kuyGIfs0IBMH5kzzpgoRpvjjFCZvTIvxb7rM9YqME5SccmGHyZPPf7888m/JbP7MAgf3PVIV2zsMd2pL0aGTeFAb8XgXAhbnFQu7KcdKw2jLGfKhQWGuT3WRhhF8jfdbYwPyLeQ6yPHl4PpNZoaH8vTIc7CQbFcpajyZXC2S0MiQ9fBfIgTQe62OJDWA5QaYLHYUGWOKM4yNa7SrMnJmoggVpZVQumMUcZzD+ZqRewgkIsEZ2vSsIQTZ6lpVVTNFKus7wkX5NiAVMpvCNsQwU+wRFkdq9ih6t9UxXCoa0m3FRY36shSGFblG1UH1wzDQJH8N4RPX49w/T/hQLjWIhwUZvUpn7wl5XL/GcqzLU7/nfJlzrc3R64xyHdx+vvxPQ3IMN/LqTW2Xkfg+rsr3Bi/ksLxH+h+b3m/Dd0deU8G6H5bdQ/9B+8kn6ShK+9CMAtkapMiTZiLPtygAbLETwuV+nzyKCdXVjmdH5vG+amcHROxaIwbu2BWb5KTak8I1y4VMeMiZhFLMfVrtM19qyrO0pqxHQ/IBa0RUhERcYGoZJgvlkmjDqwXyoATikWyb18yh2pDebhlSS5dVYXjdhXqneIqvlvtaV4JO8d0i3ncOadIS+8cKBp8aizbygXZ+WjNTrSW9WJUZjeq1nIYFP5ryVTpv0hF86W5wN+x/oUuzHv+TYtTB4/KPtAQkWxxcJuZIX+MvBlyJqrpeTC4zhH3M97jG/lAKbvgkl/SXnV7qL0ifyKbq6PLgeshx6n8/AI/+UPoKkh74obGJdq6bTfAlKWk0y0VhGkSpVL8oBsCuCfbYAJvH1cZNkkY5tob6td1R5capHhJqIeDdZTjXeeQAPW2gxdHMW80aC//y88UIAsmfY/MXsMfEH2vcQ/0gbO9vHqalZVmDDTz6YCKu3q7oJnDtH7qFVVbv5hN/zc=</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@ -1 +1 @@
<mxfile host="Electron" modified="2021-11-24T03:33:09.587Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="pc9xWnTv6V551KKFcp_6" version="13.4.5" type="device"><diagram id="KJUpccsywgqX94dHTPwA" name="Page-1">7Vlbc9o4FP41mtl9oGNZvj5iAnSzvWQ2k2mzLzvCFrY3xmJlEaC/fiVbvjuEUmiTTuEB69P9fN85OjIATVa7OcPr6D0NSAJ0LdgBdAV0HULNEj8S2ReIDi2FhCwOVKsauI2/EAVqCt3EAclaDTmlCY/XbdCnaUp83sIwY3TbbrakSXvWNQ5JD7j1cdJHP8UBjwrU0e0af0viMCpnhpZb1Kxw2VjtJItwQLcNCE0BmjBKefG02k1IIq1X2qXoN3uitloYIyk/psPi72gzG7OHT/q7PY4/hNtr+HGkF6M84mSjNqwWy/elBRjdpAGRg0CAvG0Uc3K7xr6s3QrSBRbxVaKqwwRnmXquNpwXHgj3I1VYxkkyoQll+QwowMRZ+rIRZ/SBNGos3yGLpajpb1bt/5EwTnYNSG1+TuiKcLYXTVQtKiWlpDjSNQVsa2J1S2FRg1RkKxArMYXV4LW9xYMy+deYv29uEgj9qSJlPKIhTXEyrVGvJkQTpbrNO0rXysD/Es73ypnwhtM2ScKGbP9Z9c8L97LwxiyLV7tm5dVelZ4kIaMb5pMD+zSU52IWEn6gHVRuJY1wkFNGEszjx7aTDtGTdx0zhveNBmsapzxrjHwjgVoqut2WimW2PeuZ5iNodpRRLKDWSbWT06VjDHiulXDpXTRfXK0p678NLStGWa6KsWigo/WurhRPofzFcqm/gakJvCsw1uWDMwOO/ns5vFhtMUPR/gfECwIDk9hD8cK1bISt88QLRXpJqtEPFnAoWJgXixVaz9Q/ZaywXnesGEHYOYdfQrSwLhct8lgxngJ3+hJjxXJJLH8wtwhsd6EdlOrJsWIEkdmPFvb3jBb2NxOOtCHCc7I14FjHUiwsyNs8tplIaUo6tCkIJ3GYiqIveCEC9yQfsUjNx6piFQdBHuWGhNOJfKIy5pIeQ1NbVSEPuueRgKF33drunxjGgAT0S0kADmng+yf3JnECY8gBHX2BrDMd1si129Z39Dd9F3RQ3/yVq36L/QP6Mc3+ukuI9U86mf1xd3Pnz0ewb34ZIIX/TMDUAN4EiEUXIdMTD7McGV/jRzyXl97SwRbsGG+FlvDW3E17DmvITM4VczrA0eT85VTFNGIFLgJj84V6boIXJPGw/xDmeHfyhiNbqtyQmJd/8zG5yAGonHuEtK5kzyBA6LbdX0cD+aIz4P7wDO6/0e4+/MnuJ/NJdm3S+/V79nZ/zNWepMFYviOp7RngLKrCQYN5id9gLthMc0QcD5UeyvciejNdrHLHIl3Un8sXyS7mnxvPzW6iWPeShbJT96w35XfwPUL+qZjupIwD1Pd5bvBoHjjIvy7Z7CWHvTjWGaHInlWnA0mm4XRPo/Y4RXbdG+eEdHNQe+jn195rUxLSzqOk7jgXVpL5S0kvTUmjc0mpN9CFtfTsJdivTow6i0LFZyCxUn2zNU5Pv0CDqQVcE3gamNrA84BjNC5WxdDt6V7HfeuJ9OkoVZdqgUZbLQP3KdPqq/6E+5Qo1v/DFGqr/85C0/8B</diagram></mxfile>
<mxfile host="Electron" modified="2022-08-05T03:33:34.511Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="atv9O4ftve5jkPghnfh8" version="13.4.5" type="device"><diagram id="KJUpccsywgqX94dHTPwA" name="Page-1">7VnZUuM4FP0aVc08mLIlb3mMg6Gb6YUqimnoN8VWbINjpRWFJPP1I9nybpIAyQxMDXlAurra7jk6WgzQZL65ZHgRf6UhSQHUww1A5wBCw9Bt8U9atoUFGrayRCwJlVdtuEn+IsqoK+sqCcmy5cgpTXmyaBsDmmUk4C0bZoyu224zmrZ7XeCI9Aw3AU771h9JyOPC6kKntn8iSRSXPRv2qCiZ49JZzWQZ45CuGybkAzRhlPIiNd9MSCqjV8alqHfxTGk1MEYyfkgF3eezX9P76cPNg/b59uftn9/dzxoqWnnC6UpNWA2Wb8sIMLrKQiIbMQDy1nHCyc0CB7J0LUAXtpjPU1UcpXi5VOlqwrrMPBIexKpklqTphKaU5T2gEBN3FkgnzugjaZTYgUumM1GihkkYJ5tn529UURV8JHROONsKF1UBOmZRRVFRg7pCZl0DK/hZ2OIGqMhRjliRKaoar+MtEirkLwi/1Ys2CQX9VJYyHtOIZjj1a6tX49GLa13hC6ULZXwgnG/VwsIrTtuAieCx7Z3IaPqZWKzKcC9bPzMtWBrON6q/Irdt5q4JS0Q4CCuH1MS9mKCc1W7YRBDoigXKa/ozXl2M2eMP+GWLk2/R+sr4rin4OGYR4Tv8DGeYB4ykmCdP7YEMQZpXHTOGtw2HBU0yvmy0fC0NDXrZeotettVejXvcNWh22FQMoOZWNZOD6LYrgq3VbqdcrkiaD64mov1rRcsCbZmzZywcIFps6kKRiuR/LIf6G/At4J2DMZQJ9wK48PeyeTHaoofC/1/QmJkbkGBQY6auZVo1VQeY2ZOdZzVGgV6CavYFxihxbwqMdSp9Md+XvphuV2BG70lg7A8lMJphdDb8vRKDTi8x9ukkJheYsQ9G/rsUmBmxhwUmdEZT/TQCoxnI6kuMcyKJGQTceTPgSB8CPAdbB659KMQigryNYxuJjGakA5sy4TSJMpENBC5SZDyJRyLuAGNVME/CMNfGIeK09ZKKwoRLeExdTVVpozHaSacjkANZ7lmXH05/CzIH6AFPRQ9jiB///A3DIm5oDi1OF06RbR8p/shpR9/tL04X9YNfLeK3RH+l3377g91PLifLK4veL76yT1sN7g8+ycKxvCfXyyHEy7hCoxF5ab/GXCyRLLeIlVtFtLwbVxeGu+b+X+z20Nq32W8SftdIN6uJbF1LZspKXRm25G/wLpn/vW4Ndo4XfQY0ELZ2qO/LTgi9Hb1HMHTWYU5xxlHVdpwNTKjvaak4BfVaesU5YZCZBzw8fHRm/nd4Bp1j8azf0ol5Zv3Ps4/DMw3B4wlar6kTM23vzSeo9qL6rIuKv4Hjr6q7XODs9bcm4NtgZAFPB74DPA+4ZuM0XTTd7u5jHLJPfIruvBM7FZOaq2LgnfgYh+jBZxyjTy15AxYXpAnwTeBNwMhRd2JPJC5yy/gKP+FL+fmkBHfKDmGSYQ8zyZGPeyNDJsYeGI2qfh5EP/lnmrMge6eESfGUpB4OHqPc3u28cUmzVb5xcPTyX94mF2pHZd8a6lFv4FXqTd8ruld9OPCY6A5os/FyFops/R2q0MP6cx7y/wY=</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

View File

@ -1 +1 @@
<mxfile host="Electron" modified="2021-11-24T07:29:28.493Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="YfKlEbrvOOY6jty1IFoX" version="13.4.5" type="device"><diagram id="KJUpccsywgqX94dHTPwA" name="Page-1">7Vlbd+I2EP41Pqd9IMeWfOPREMh2l223zTbZ5k3YwlYxFivErb++ki3jKw7pQki7PTygGY1u8818GtsaHC52dwwto480wLEG9GCnwVsNAMPQbfEnNftMAwxbaUJGAmVVKO7JX1gpdaVdkwCvKoac0piTZVXp0yTBPq/oEGN0WzWb0bi66hKFuKG491Hc1D6SgEeZ1gVOoX+HSRjlKxt2P+tZoNxYnWQVoYBuSyo40uCQUcqz1mI3xLH0Xu6XbNz4SO9hYwwn/JQB06doPfbY/BFM9oj8HG7fG7/0QDbLBsVrdWC1Wb7PPcDoOgmwnMTQ4GAbEY7vl8iXvVsButBFfBGr7jBGq5VqHw6cCnPM/UgJMxLHQxpTlq4AA4TdmS+NOKNzXOqxfRdPZ6KneVh1/g1mHO9KKnX4O0wXmLO9MFG9wDWzISoUe0BXyGwLYIGtdFEJVOgoJVLBFB4mL/wtGsrl7e7/6et+bD6YCXE/333++OCaH/xNz254Gwci/JRIGY9oSBMUjwrtoMBDF1JhM6F0qfz7J+Z8r3IJrTmtYiRcyPZf1PhU+EMKN1Yu3u7Knbd7JR3FYEXXzMcdUaYczxELMe9wh7KTPuhElOEYcbKppmgbOOlQjzG0LxksKUn4qjTzJ6koAgXm3KMCxbSqefWMuWvXwiJbvwiSw0FOipsuh1bS1o65TC2a7q2IKPvrmuYdvVUaE54wAHC5KzpFK5T/O4LohPygjSzNG2n9kWy4Y80FP+bzi+1mS2QDrsAWsxm2/Va2CJz+VO+M1NPZwrEqoIImVRhtVGFdiims74Mp7CZTtNoZzrmp4lR0urZ9kXx8ilAS/p+SxdWc399GnqPlpOxfKClbYXe+GXaot8Gegq1r8iY5DWLhQV7FsYpEQhNcg02pUEzCRIi+wAUL/UDiQUT966mOBQmClE3aAqfGMKKTcAmPqaujKmox+ucJARM0YqBJzGZLCIBLhYBxPAZWS5T889Rf8XWAU8jTyaaswQml4MiWulr+BxZ2A7Mt/10wheKJ7yzgQ9u9sWrwA3DTQgImbCGBg+XZ72b3P3c3tx4T/KureOO6ZXyXR8/OHUvMVjQBadEwuNU80Fo0XJk0sCFow2kjjb7tQHQm0nBqUXD1Qt75PsgCvvlCvmvbZy/ks4w0nsnIK5fxr5ORbq2Ec1sy0nnNjDz+MuWCFdyENPVvh5tfqaCrhYLdEgmvXMrlRUDXC3GcBJ78siAkXzqXZE++iPGm+gwcjHeEfym1S6OEVAySwmHMs1fK5ctAeCKxl9C2OtL+ZdVis7yrsUd2TGXUURbW2arOQpkbGvO8tFoFLytW64/BVsX8m4vVx99B4gS/7Z5+ZZ8eJpt36MN80cvTu5QZ8g6zNG+ojUxtMNT6jrrVBqIxTjXee7RBd/IbXpnunmdUwxaMmjJi49WIKS/QvljT1QQ4Yv18qWwZsYM+1Lzm+9K38Y4kRlMcD5A/D1N9ffHSKxNbySXyHaS/dE4ukoHKtXtQr5P5ER5uSffj1AyNxrN2Czu7LQlrvPyeFmLxHTSL1+JzMhz9DQ==</diagram></mxfile>
<mxfile host="Electron" modified="2022-08-05T03:32:30.115Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="WLd5agzo5INWRagEBRhn" version="13.4.5" type="device"><diagram id="KJUpccsywgqX94dHTPwA" name="Page-1">7Vpbc9o4FP41ntl9IGNLtrEfgZDstsnuziTTdvsmbGG7GIvKIkB//Uq2jC9SICk4ZKY7eUA6up/zne8cWTHgZLm9pWgV35MQpwYww60Brw0ALMt0+Y+Q7EoJsFwpiWgSyl614CH5gaXQlNJ1EuK81ZERkrJk1RYGJMtwwFoyRCnZtLvNSdpedYUirAgeApSq0s9JyOJS6oFhLf8DJ1FcrWy5ftmyRFVneZI8RiHZNERwasAJJYSVpeV2glOhvUov5bibZ1r3G6M4Yy8ZMPsar29GdPEZ3O1Q8le0+WD9PQDlLE8oXcsDy82yXaUBStZZiMUklgHHmzhh+GGFAtG64UbnspgtU9kcpSjPZXl/YFNUFpgFsWyZJ2k6ISmhxQowRNibB6ITo2SBGy1u4OHZnLeoh5Xnf8KU4W1DJA9/i8kSM7rjXWQrrLAooTgAprTMpjYsx2cpixtGhUPZEUkwRfvJa33zglS5Xv2Lx3Se2XDx8X69XXGwPubZ48BWtI1DDj9ZJZTFJCIZSqe1dFzbQ9FrPeCOkJUUfsOM7aRjoTUjbYNxfdLdFzlZUflXVK6cqnq9bTZe76p1m8YtTyG2/iw2pSgnaxrgA4CUGmGIRlgO/fP77sb+ZGeJ93j7eP/Jsz8GT2U/1dYUp4glT+196MxWDB1RinaNDiuSZCxvzPyPENQQAhUQJIRsp+1xR7r7dgcw5fo1fPYHeRGiDumv5dBuyoTTkWJvNdbc72tSNQzyAiAj3gHA1bZu5KVI/G4TRO6S34ypY4ymhj8VBe/G8MDv1fx8u+US5YAL8Mh8jt1AyyPh0J+Z5nl4xB1eOS2zApVGrKGj0ojTF4uonP2rs4irsoi2nzV8DzQysJxO6D42wPf7ZxK3Ryb5GqMs+p9MNEioiKNJJp7ZD5lozT482ezQ1Jm9MLZpeO5LTcw1yNp2bFsiIxnumE2KUJpEGa8G3C6Yy8fCHgnP6UeyYZmEYcGCOuC0mZHwxoQJ89imPKpkQcs/CKdzZKw+7IQajg812NgaeIC+4GE9j498hbKfp4WcrUNcwKGYbEYVvmgAp1zqYtwQOtgLbR03eGAG+Q33POZ3QMf4QEMONtSQA6hwc/Zcw/mVcw3tTQSoucb7vbFYl72yHFLg2RllhWlOMlCkGeNrYwS0acaFqWTuBVifZsw8x3b6STMs7ZWlpyxDSyPw4jTSFx3Ac7i54pfQ6SaKZsc0Jf/IYbV1Tr6bOK8jjP3GeqUMeHKSeogyrCOUcembySUoY+BpKKOvi8nz8fOtE8+7RJW/n+DxRnmo3c5D6w9gb5OHatFQvQ8dernAWTgST0C8Fgj1JqXTIMpU8RkSTLxN2JdGuTGK1+pBorIfczTq/cQN82wpK9TGRRUoDRw4Byjh1BjYYZbymErEU8Z53YjWU+R8ZaZ9MM6eHDa1WVfl+g2fEfHNMUYTY2ob44nhD2XEG/PCTSEZfUBP6FY8wzap8DjbWq7+M9BQRFbfEoXR2PD9/Trf+DrFc+9VkCm+/D6+BqVohtMxChZRIe8u3vg45Mp6g5PHxV8xJ+N+QMTaA6i4tSYxVbha4/bP0zewuxnayyK59fpIzqv1g3aJ2vr/AuD0Pw==</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -1 +1 @@
<mxfile host="Electron" modified="2021-11-23T09:52:42.543Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="1gWZy4iF8VxfL9PcQwjW" version="13.4.5" type="device"><diagram id="KJUpccsywgqX94dHTPwA" name="Page-1">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</diagram></mxfile>
<mxfile host="Electron" modified="2022-08-05T03:55:07.660Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="FRAlw8Cix2AlpgvyGKA0" version="13.4.5" type="device"><diagram id="KJUpccsywgqX94dHTPwA" name="Page-1">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</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -7,6 +7,7 @@ head:
- - meta
- name: keywords
content: JVM,JDK,JRE,字节码详解,Java 基本数据类型,装箱和拆箱
- - meta
- name: description
content: 全网质量最高的Java基础常见知识点和面试题总结希望对你有帮助
---

View File

@ -7,6 +7,7 @@ head:
- - meta
- name: keywords
content: 面向对象,构造方法,接口,抽象类,String,Object
- - meta
- name: description
content: 全网质量最高的Java基础常见知识点和面试题总结希望对你有帮助
---
@ -208,7 +209,7 @@ System.out.println(person1.getAddress() == person1Copy.getAddress());
我专门画了一张图来描述浅拷贝、深拷贝、引用拷贝:
![](./images/shallow&deep-copy.png)
![浅拷贝、深拷贝、引用拷贝示意图](./images/shallow&deep-copy.jpg)
## Java 常见类

View File

@ -7,6 +7,7 @@ head:
- - meta
- name: keywords
content: Java异常,泛型,反射,IO,注解
- - meta
- name: description
content: 全网质量最高的Java基础常见知识点和面试题总结希望对你有帮助
---
@ -377,7 +378,7 @@ public class DebugInvocationHandler implements InvocationHandler {
### 何谓注解?
`Annotation` (注解) 是 Java5 开始引入的新特性,可以看作是一种特殊的注释,主要用于修饰类、方法或者变量,提供某些
`Annotation` (注解) 是 Java5 开始引入的新特性,可以看作是一种特殊的注释,主要用于修饰类、方法或者变量,提供某些信息供程序在编译或者运行时使用。
注解本质是一个继承了`Annotation` 的特殊接口:

View File

@ -7,6 +7,7 @@ head:
- - meta
- name: keywords
content: Java SPI机制
- - meta
- name: description
content: SPI 即 Service Provider Interface 字面意思就是“服务提供者的接口”我的理解是专门提供给服务提供者或者扩展框架功能的开发者去使用的一个接口。SPI 将服务接口和具体的服务实现分离开来,将服务调用方和服务实现者解耦,能够提升程序的扩展性、可维护性。修改或者替换服务实现并不需要修改调用方。
---

View File

@ -75,7 +75,7 @@ num2 = 20
`swap()` 方法中,`a``b` 的值进行交换,并不会影响到 `num1``num2`。因为,`a``b` 的值,只是从 `num1``num2` 的复制过来的。也就是说a、b 相当于 `num1``num2` 的副本,副本的内容无论怎么修改,都不会影响到原件本身。
![基本数据类型参数](./images/java-value-passing-01.png)
![基本数据类型参数](./images/java-value-passing-01.jpg)
通过上面例子我们已经知道了一个方法不能修改一个基本数据类型的参数而对象引用作为参数就不一样请看案例2。
@ -106,7 +106,7 @@ num2 = 20
解析:
![引用数据类型参数1](./images/java-value-passing-02.png)
![引用数据类型参数1](./images/java-value-passing-02.jpg)
看了这个案例很多人肯定觉得 Java 对引用类型的参数采用的是引用传递。
@ -156,7 +156,7 @@ xiaoLi:小李
`swap` 方法的参数 `person1``person2` 只是拷贝的实参 `xiaoZhang``xiaoLi` 的地址。因此, `person1``person2` 的互换只是拷贝的两个地址的互换罢了,并不会影响到实参 `xiaoZhang``xiaoLi`
![引用数据类型参数2](./images/java-value-passing-03.png)
![引用数据类型参数2](./images/java-value-passing-03.jpg)
## 总结

View File

@ -6,7 +6,7 @@ tag:
---
## 1. ArrayList 简介
## ArrayList 简介
`ArrayList` 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用`ensureCapacity`操作来增加 `ArrayList` 实例的容量。这可以减少递增式再分配的数量。
@ -24,12 +24,12 @@ public class ArrayList<E> extends AbstractList<E>
- `ArrayList` 实现了 **`Cloneable` 接口** ,即覆盖了函数`clone()`,能被克隆。
- `ArrayList` 实现了 `java.io.Serializable`接口,这意味着`ArrayList`支持序列化,能通过序列化去传输。
### 1.1. Arraylist 和 Vector 的区别?
### Arraylist 和 Vector 的区别?
1. `ArrayList``List` 的主要实现类,底层使用 `Object[ ]`存储,适用于频繁的查找工作,线程不安全
2. `Vector``List` 的古老实现类,底层使用 `Object[ ]`存储,线程安全的。
### 1.2. Arraylist 与 LinkedList 区别?
### Arraylist 与 LinkedList 区别?
1. **是否保证线程安全:** `ArrayList``LinkedList` 都是不同步的,也就是不保证线程安全;
2. **底层数据结构:** `Arraylist` 底层使用的是 **`Object` 数组**`LinkedList` 底层使用的是 **双向链表** 数据结构JDK1.6 之前为循环链表JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!)
@ -37,7 +37,7 @@ public class ArrayList<E> extends AbstractList<E>
4. **是否支持快速随机访问:** `LinkedList` 不支持高效的随机元素访问,而 `ArrayList` 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于`get(int index)`方法)。
5. **内存空间占用:** `ArrayList` 的空 间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 `LinkedList` 的空间花费则体现在它的每一个元素都需要消耗比 `ArrayList` 更多的空间(因为要存放直接后继和直接前驱以及数据)。
## 2. ArrayList 核心源码解读
## ArrayList 核心源码解读
```java
package java.util;
@ -546,9 +546,9 @@ public class ArrayList<E> extends AbstractList<E>
```
## 3. ArrayList 扩容机制分析
## ArrayList 扩容机制分析
### 3.1. 先从 ArrayList 的构造函数说起
### 先从 ArrayList 的构造函数说起
**JDK8ArrayList 有三种方式来初始化,构造方法源码如下:**
@ -603,15 +603,15 @@ public class ArrayList<E> extends AbstractList<E>
```
细心的同学一定会发现 **以无参数构造方法创建 `ArrayList` 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为 10。** 下面在我们分析 ArrayList 扩容时会讲到这一点内容!
细心的同学一定会发现 **以无参数构造方法创建 ``ArrayList`` 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为 10。** 下面在我们分析 ArrayList 扩容时会讲到这一点内容!
> 补充JDK6 new 无参构造的 `ArrayList` 对象时,直接创建了长度是 10 的 `Object[]` 数组 elementData 。
### 3.2. 一步一步分析 ArrayList 扩容机制
### 一步一步分析 ArrayList 扩容机制
这里以无参构造函数创建的 ArrayList 为例分析
#### 3.2.1. 先来看 `add` 方法
#### 先来看 `add` 方法
```java
/**
@ -628,7 +628,7 @@ public class ArrayList<E> extends AbstractList<E>
> **注意** JDK11 移除了 `ensureCapacityInternal()``ensureExplicitCapacity()` 方法
#### 3.2.2. 再来看看 `ensureCapacityInternal()` 方法
#### 再来看看 `ensureCapacityInternal()` 方法
JDK7可以看到 `add` 方法 首先调用了`ensureCapacityInternal(size + 1)`
@ -648,7 +648,7 @@ public class ArrayList<E> extends AbstractList<E>
> 此处和后续 JDK8 代码格式化略有不同,核心代码基本一样。
#### 3.2.3. `ensureExplicitCapacity()` 方法
#### `ensureExplicitCapacity()` 方法
如果调用 `ensureCapacityInternal()` 方法就一定会进入(执行)这个方法,下面我们来研究一下这个方法的源码!
@ -673,7 +673,7 @@ public class ArrayList<E> extends AbstractList<E>
直到添加第 11 个元素minCapacity(为 11)比 elementData.length为 10要大。进入 grow 方法进行扩容。
#### 3.2.4. `grow()` 方法
#### `grow()` 方法
```java
/**
@ -718,7 +718,7 @@ public class ArrayList<E> extends AbstractList<E>
- java 中的 `length()` 方法是针对字符串说的,如果想看这个字符串的长度则用到 `length()` 这个方法.
- java 中的 `size()` 方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看!
#### 3.2.5. `hugeCapacity()` 方法。
#### `hugeCapacity()` 方法。
从上面 `grow()` 方法源码我们知道: 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE如果 minCapacity 大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`
@ -736,11 +736,11 @@ public class ArrayList<E> extends AbstractList<E>
}
```
### 3.3. `System.arraycopy()``Arrays.copyOf()`方法
### `System.arraycopy()``Arrays.copyOf()`方法
阅读源码的话,我们就会发现 ArrayList 中大量调用了这两个方法。比如:我们上面讲的扩容操作以及`add(int index, E element)``toArray()` 等方法中都用到了该方法!
#### 3.3.1. `System.arraycopy()` 方法
#### `System.arraycopy()` 方法
源码:
@ -807,7 +807,7 @@ public class ArraycopyTest {
0 1 99 2 3 0 0 0 0 0
```
#### 3.3.2. `Arrays.copyOf()`方法
#### `Arrays.copyOf()`方法
源码:
@ -856,7 +856,7 @@ public class ArrayscopyOfTest {
10
```
#### 3.3.3. 两者联系和区别
#### 两者联系和区别
**联系:**
@ -866,9 +866,9 @@ public class ArrayscopyOfTest {
`arraycopy()` 需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 `copyOf()` 是系统自动在内部新建一个数组,并返回该数组。
### 3.4. `ensureCapacity`方法
### `ensureCapacity`方法
ArrayList 源码中有一个 `ensureCapacity` 方法不知道大家注意到没有,这个方法 ArrayList 内部没有被调用过,所以很显然是提供给用户调用的,那么这个方法有什么作用呢?
`ArrayList` 源码中有一个 `ensureCapacity` 方法不知道大家注意到没有,这个方法 `ArrayList` 内部没有被调用过,所以很显然是提供给用户调用的,那么这个方法有什么作用呢?
```java
/**
@ -891,7 +891,7 @@ ArrayList 源码中有一个 `ensureCapacity` 方法不知道大家注意到没
```
**最好在 add 大量元素之前用 `ensureCapacity` 方法,以减少增量重新分配的次数**
理论上来说,最好在向 `ArrayList` 添加大量元素之前用 `ensureCapacity` 方法,以减少增量重新分配的次数
我们通过下面的代码实际测试以下这个方法的效果:
@ -939,4 +939,7 @@ public class EnsureCapacityTest {
使用ensureCapacity方法后1773
```
通过运行结果,我们可以看出向 ArrayList 添加大量元素之前最好先使用`ensureCapacity` 方法,以减少增量重新分配的次数。
通过运行结果,我们可以看出向 `ArrayList` 添加大量元素之前使用`ensureCapacity` 方法可以提升性能。不过,这个性能差距几乎可以忽略不计。而且,实际项目根本也不可能往 `ArrayList` 里面添加这么多元素。

View File

@ -6,7 +6,8 @@ tag:
head:
- - meta
- name: keywords
content: Collection,List,Set,Queue,Deque,PriorityQueue,
content: Collection,List,Set,Queue,Deque,PriorityQueue
- - meta
- name: description
content: Java集合常见知识点和面试题总结希望对你有帮助
---

View File

@ -7,6 +7,7 @@ head:
- - meta
- name: keywords
content: HashMap,ConcurrentHashMap,Hashtable,List,Set
- - meta
- name: description
content: Java集合常见知识点和面试题总结希望对你有帮助
---

View File

@ -1,5 +1,5 @@
---
title: AQS 原理&AQS 同步组件总结
title: AQS 详解
category: Java
tag:
- Java并发
@ -13,6 +13,8 @@ tag:
- 用过 `Semaphore` 吗?应用场景了解吗?
- ......
相关阅读:[从 ReentrantLock 的实现看AQS的原理及应用](./reentrantlock.md)
## AQS 简单介绍
AQS 的全称为 `AbstractQueuedSynchronizer` ,翻译过来的意思就是抽象队列同步器。这个类在 `java.util.concurrent.locks` 包下面。

View File

@ -1 +0,0 @@
<mxfile host="Electron" modified="2022-02-08T08:36:06.533Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="B4R4mgy3WOuTaF9DRYB6" version="13.4.5" type="device"><diagram id="O1q-3gN19tnn94MyZq-a" name="Page-1">7VnZcqM4FP0aHuMChFgevSaVmtSk2jPVPY8K3IAqGLmFHDv99S2BMKuzTJp2ajLJg9HRfs/RPSwGmm8Ol5xskxsWQWrYZnQw0MKwbcsyXfmjkKcSCQKvBGJOI92oBtb0B2jQ1OiORpC3GgrGUkG3bTBkWQahaGGEc7ZvN7tnaXvWLYmhB6xDkvbRrzQSSYn6tlfjV0DjpJrZcoOyZkOqxnoneUIitm9AaGmgOWdMlFebwxxSFbwqLmW/1Yna48I4ZOI1Hf76sb6Jrtj+Kvy+/jP6I/+Kr/0LuxzlkaQ7vWG9WPFURYCzXRaBGsQy0GyfUAHrLQlV7V5yLrFEbFJdfc8yoUmUy5FlmqZzljJejIXu/RDCUOK54OwBGjV3Pnaw6pE/gAgTPdwxZqpGLxW4gMPJGFjHyEpJAtuA4E+ySa3HsotW41Fm+5pb29FY0uQ10CDReoqPY9chlxc66m9gAPUYmN/+LYEvENNcAM/fSUibALAiDN4QAYHrIeJ2CWjwafljEILbhKABQix3gBBvLD4sr38kltjwsTGdG0vHmM0NmcIUsjJm8mJVINNr8kguVa4ybDeVa5zd8RZr7vedOuhFQC/yIqJT2cByt4ciklW9vIqLXzXVwgjknL7hm2r+aqpyGrmCABlT3JOHJEO0NdDmOmMZdIShIZLSOJPFUJIHEp8paqnMhVNdsaFRpKYZFF0tS6WNlNxBOiPhQ1zg3ckbsnJ1uaHFWfFfjCmIoEzNfYFGzw5HUWkxunhAjP5QdhhNjMFwdviQlHdz/7iZ3O9kcjTAlTnAFRqLq8rnP4uXOgh/NC+tNvFJzdTx2oyc30yrI/B//upyhc3O6Tl//jp1JzonYQLvOzi9A4JcFKCof6RWq+nCWT1zcEpmRn4scILXprLRnN92Pi0bXWP5CGzgl70dsmiqXjiobJSSPKelNxMu+vAzLg8HKr6pYE6wHejyP0XZNKvy4qCjXRSeGoVb4FRuWaXCxQluIOq98egwI7fFdjyEF5NFn8IGRXiAoQrjkMr7+sf2OoZY0zPcMipXWN+od84rNjvEl+vXvZrvRLoDIa8ttSorVwNJ+mIQvYEKER23/Q5duefQVVNVzypqNPU451QPxs7kRIZ5q36KoZyOhJyJaf1eFfVfWdwQmknkBjZMp78R3OL1rtDzlaXt41/l3W0uvYGbXjx00zuiW/Sf2sc/1bbZdIsLaRe293HswjurXdjBRFpE/YdainF9PAmCf2kgauh2AnC7ZjTy6UeveO/wH1Lb+VzDndgnUs3bbQNNOs/qrmlPqk9j7xaOLNbflcrm9dc5tPwJ</diagram></mxfile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

View File

@ -1 +1 @@
<mxfile host="Electron" modified="2022-01-24T01:30:38.715Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="l1tJc7jft47xw4GR_2dM" version="13.4.5" type="device"><diagram id="f1uOqe90rjhexKzs8PuL" name="Page-1">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</diagram></mxfile>
<mxfile host="Electron" modified="2022-08-04T12:45:10.835Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="WotcJiYPt2iKRm6Hc-5O" version="13.4.5" type="device"><diagram id="f1uOqe90rjhexKzs8PuL" name="Page-1">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</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@ -1 +1 @@
<mxfile host="Electron" modified="2022-01-24T01:31:02.213Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="tG8e46NqnXu62oEqxvIZ" version="13.4.5" type="device"><diagram id="l-JW3fph1e97gRfsgveQ" name="Page-1">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</diagram></mxfile>
<mxfile host="Electron" modified="2022-08-04T12:45:04.137Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="bDDGEj_wTsAIpHJuuoFW" version="13.4.5" type="device"><diagram id="l-JW3fph1e97gRfsgveQ" name="Page-1">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</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2022-08-04T12:39:26.711Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="owfxTT7KcwZ98QLN75Wc" version="13.4.5" type="device"><diagram id="XZHCjfYPnYWvLepVL7Sq" name="Page-1">7Vtdc6M2FP01PCbDhxH40XacdNpsZ9vstJu+ySAb1jJiQY7t/vpKQhjzZeME7DRhkhmjKyHQPedcXYRQjMlq+xDB0PtCXIQVXXW3inGn6LqmqYD9cMsusQyHVmJYRL4rG2WGJ/9fJI2qtK59F8W5hpQQTP0wb3RIECCH5mwwisgm32xOcP6qIVygkuHJgbhs/dt3qZeOCwyzil+Qv/DSSwNzkNSsYNpaDiX2oEs2ByZjqhiTiBCaHK22E4S591LHJOfd19Tu7yxCAW1ygr755+fz79HXUbzGy/lv85vJ+o8bO+nlBeK1HLG8WbpLXRCRdeAi3ommGOON51P0FEKH124Y6Mzm0RWW1fESUceThTkJqISU3RurTD3AC+XblyN6QRFF2wOTHM4DIitEox1rImuNgXStJJeumUl5k0Glp228A5QGtjRCSY/Fvu/MgexA+vAMfxplf05NxTaV0USZDpTxRGEC4JZ7ZcwO7oVl9Ct8gQ+c6YoOMLvH8SzKYQB+rjlLhENvYuHREWuggXCbVbKjhfidWsr4Thlq/GA0VobD/XV+sOsIRd06QQln5nRaAJNGZIkmBJOIWQISIH4PPsYFE8T+ImBFh0GJmH3MIfSZiEayYuW7Lr9MJXsyfnFaYDhDeAyd5ULYixfPGKUBWU6bKLoxFn+iTwqpT/i1bwy1yMu2eWjaeR4OVLXEQ82u4KHWFQ21Kl0n1OIua0Quu4pc37wIQTfti91b0p1k3vukVAsID4w8wns0DxAGFQDrXQEMwIUCt5BZCY1DxJjuXIjsuVOCl9UAx0azeTsgWGoh3KdT7alwb4OOULDUktORy/IHWSQR9ciCBBBPM2uBnFmbR0JC6f8fiNKdBACuKclDVUQnB12tn/mNHfVyhDCLmS/5xKfKZfLUr8QXkaQGHWAWuE9htEBUnnWYpxQ6MvUTHcVkHTmo1JFAcD+e14M6GJ6WFptBQn5I4UxgeoBOlc5iNvYUMg46y1Yp9AMuJXGOQzCGYeyLzpIWno/dR7gja5peJi2N87o+X4qv488ZwbKoU6tiOtSr0rLO5sOUQk0wZWOnPsR/sscJGCyawFuGz41I+E3wXRpCzlYUTV+YW2Npq5j2qAgBIg9C8/TcGaGUrGQhku7adypcZY7ZPxvjRL01FZONZsLKWlZm/7x5xJKlgBED+gJcBGO6QTGthP24Nk6T4RTYpt0V1loF1mfmPmZV7jMNxO01S33ewibxKAszNhHm7jkWGavH0h4UtEGehojvlfOeEdd7dV9Y3bs8pKegB50F9ooHbqnQOIRBe2JPevvoYtcbE+B6iA96sV9H7JrWUO3dBXqzV3uLah80ZsD1EG+wzvGp1V67LlZas+kuLljg2nHBKrHk9vb24woXNIboepA0eLP0qYXbmRx1o7wsemHsq1bQ+mn6tWq3GzPgaoiDfn3tWmof6s3U3tkDGahfb+vVfrbaQXvrbd0h3q+3XUnthtlQ7d1F+n7BrU21t7fg1hnillZy/7kvuXNvGF/xxhtxbnyXnYnCMy8wNcri3faw8m6XxoTCm84yKunYxCvlY0mY9DhNw1AtovLZrL3X7W8Dr0Go/uj7RIDacFvgsKsJ06oPmm1sx3okDsRfGFKffVvWHtVrbcuyWligPgU0b9N0nnwfSB+NGR0IXretcpZkXpQHVcvWrfMAwBV3djCLxU9PihwptMIsUBEbNOOipCivUpczq8Ad8c8WMp833fiHtj79nmVFrPR8UJMlSLyQ5ke1bj6ZvRw4sUpYqe2NewrtwgZ/MDTzXdRsBTzdkTrMd1SzObGtPYVW/X7st8WDv3iH/XSQU75tnZ4OqtK/7oTfwnp4Pfqstg/9eQKk+wSOhH7rkgSwq5ZI+8h/NPLnIdx/6nV+5M93tP9o4EKR3656AG/j0W+JGu9FfR+qbyOnKz7vGVd+3rNbWA2tRDfp8H8V1dsI3OBi+LJi9ultIvbsC2Zj+h8=</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2022-08-04T13:25:05.991Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="mjUzkXDFLRMI-iCStmDP" version="13.4.5" type="device"><diagram id="O1q-3gN19tnn94MyZq-a" name="Page-1">7Vlbc6s2EP41PMbDTQIefcHptMmcTDOdNk8dGWTgBCMfIcd2f30lEAYEdlwfk7g9nWQS9tMiof12V7ugWdPV7p6idfxIQpxqph7uNGummaZh6JD/E8i+RDzPKYGIJqFUqoHn5C8sQV2imyTEeUuREZKyZN0GA5JlOGAtDFFKtm21JUnbq65RhDvAc4DSLvp7ErK4RF3TqfGfcBLF1coG9MqRFaqU5U7yGIVk24AsX7OmlBBWXq12U5wK41V2Ke+bHxk9PBjFGTvnhgX49suX8cufFDz43uuLlzxkX+7kLG8o3cgNy4dl+8oClGyyEItJDM2abOOE4ec1CsTolnPOsZitUjm8JBmTJPLH4XKSplOSElrMZS3dAAcBx3NGyStujCxcYANxR/6KWRDL6Q42EyPdDVdPjynDuwYkDXCPyQozuucqchTokgzpjZYt5W3NrVnpxE1eK0Uk/Sk6zF2bnF9Iq/8DBswOA9On3zjwK46SnGGafychbQKwEQLs9BHgQcdCUCWgwafhDkGIqRDidQkxYA8hzlB8WP18KBzw7bG2odsGzUiGFetLCKVJlHEx4EbDHJ8IYyU84YzlwCoJQ7FML7M193pPtA0bO0ChCvRQ1Rc71lBU2R1acMiTtxQJZTGJSIZSv0YVA9Y6D4SspeG+Ysb20qxow0ibZ25Auv9D3D8yQSW/yPkKYbZrSXsp5QxRNhZHkmA/RXmeBBU8T9KDHzU5PEpbTjY0wCdsA+RZiWiE2Qk9eY4Jw510AopTxJK39ql4dUbBkWQ4RUGMvy8RdhIeDFy8WHZTZIiwuwxOJMIy1K4dXUY7ukDPyWQ4PdEFhoou+H5pgLPwiEN34RNFAt4lrIwoYHpSLiJqpOuVXAeVEPYN4QnThO9Y5NJZh5lGtLaC1XgvWD82Co0zo7DhDOCEL5wdrHKFJ5LwndW+6Cq+qPpYuW95V7PkVCaCutOayAagPVFpmM5Ehb8etn25CztdF/YdbTLVPEvzgTaeaZ6r+bY2cTW+ZZ//5aO25kMhjh2h4840dy6Gxr74vW4KCuECglM1VzfVqAX1cmkWBfUAKQj2HPCg74AfrBZze/jjlHDmpgVtnMiSpLk24RfzAhn/jN7QvWgbNROmokpb0BZp8NtG9FyFne/ywtBjrmDA9a4e5FdR8V84DPcTQ1yMJ5rnHdb5ytcp2tNRkN1ogZiiBU4nKHiNClxdvFHcQyk3fGtS/BRzMp5OiFj7zhq8zoRQyT5Gz0no9vVoQ7mh9/5J+F9qkl0TtAi4gSa5ovYH7ZJdW2Hk07tko/vi6P82ueTKUbj69DbZ6CncP7RPdm64Tzbk+55/WaNsHHlN9QN0yq59a52yYb9fINx+q3xeQN1Ii+qZ9giobjCylfby3D7VUyoe21VcZeA+1ei+a3lECW8q9Ee8ItJDL4/ny5tLDPsL0dDxFvpAb74c97PbTrNbbV4rni99H9WI+2bUn4z4a52Q8Mon5MdkCEf5XOF4irucmxxc5Zuho3Y5FycHLtaff0v1+iO65f8N</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2022-08-04T13:24:14.579Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="TtfzKUAGciy96luYy6S1" version="13.4.5" type="device"><diagram id="O1q-3gN19tnn94MyZq-a" name="Page-1">3VnbcuI4EP0aP0L5Jl8eMYGkpobd7JKt2Z2XLWELWxNjMbYIkK8fyRbYsgW5AAm7yUPcR7Jk9elu9alo1nCxuc3hMpmQCKWaqUcbzbrRTNMwdIf94ci2QnzfrYA4x5GYVANT/IwEqAt0hSNUSBMpISnFSxkMSZahkEoYzHOylqfNSSrvuoQx6gDTEKZd9BuOaFKhnunW+B3CcbLb2XD8amQBd5PFSYoERmTdgKyRZg1zQmj1tNgMUcqdt/NL9d74wOj+w3KU0de88PA8nUR3ZH0X/pz+Hn0tvoEvXs+sVnmC6UocWHws3e48kJNVFiG+iKFZwTrBFE2XMOSja8Y5wxK6SMXwnGRUkMg+h9k4TYckJXm5ljX3QhSGDC9oTh5RY2TmARvwN4pHRMNELLf3GR/pHlj44AnlFG0akHDALSILRPMtm7IbtQQZIhpNX9jrmltTF1jS5NUWIBTxFO/Xrl3OHoTX38CA1WFgeP8XA/5EMS4oyosTCZEJQEYEkKsiwHdcCzptAhp8Gt4lCAEyIZbdJcRwFIS4l+LD8NWEtEhg56Oyp2WPZiRDLfcLCKY4zpgZMq8hhgfcW5hVnIEYWOAo4tsoqa3J1xXpdtnk8Vpc6QquVMljnYGr517aM+L72eI5CaOHfx9+I9/dntOhBUWseguT5DQhMclgOqrRlgPrOV8JWQrH/UCUboVb4YoSmWfmwHz7N3+/b4Kd/Y9YrzRuNpK1FVZBYU4H/E7i7KewKHC4g8c43cdRk8PqgPxUB6u+gAqyykN0rNSLSsO2ixE9NtFVh0GOUkjxk/wlZ88/81BBHMIwQWcthhFE3jx8U8nrlE0n9NBsfqYEs+UEsxW3k+EqEgxcqhiyAH+xP0BZdCCou/CRTgFtMK2yCpi+sMus6uv6zq4TixvbhnGPcsyOzOtpxVOTtEbGSglrvJSw3Uw8JfE6WfxyJqrDpREO4Eg0vDpfxQ73BLOj7aNx34Jvd8u2oqw6uHir2Xi2F7JcOax1IC9U+aGzUBmx+2OfEMRuJ4gnEGcMmaAFEVny7ppyqHaYnTBs98Jz5Kh74cj1Z/q5Lu1WTXEUlzZQXdrnaLC8iRmsew90OQjmAfrj+wCPA5XkGAHNA9pgqI1sLRhqTCJyZKwF7GFcIoMv8Aneci2omU7KO69ZLpHm/FxxIVUy0CtKCgZsguEsN/Uge4rLvyNXC2403+APg0Dz/f0+P9g+pebsh9mVNn0pnKE0gOFjXOLtzRvXlyPsRmwF5W+5JmXVgfC9e9bFe0ez1TvaluJq81TC61K9o9ENwytXvh1vKzg5SIANgETAxypfJQPdQvAfU76nEeLKhHyo8lXycaDxvsoa+FbhexJVQG9R9enC1+3Q8rHC171S4at0lq1dme499pWXkL2HRevpgvgslfATZa+Si/+F6n1XOn2W2ATA7oN2FPRZX1T/tETja9UnaDU+drsov1t9MrP+7001vf4fmDX6BQ==</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View File

@ -7,6 +7,7 @@ head:
- - meta
- name: keywords
content: 线程和进程,并发和并行,多线程,死锁,线程的生命周期
- - meta
- name: description
content: Java并发常见知识点和面试题总结含详细解答希望对你有帮助
---
@ -131,7 +132,7 @@ Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种
由上图可以看出:线程创建之后它将处于 **NEW新建** 状态,调用 `start()` 方法后开始运行,线程这时候处于 **READY可运行** 状态。可运行状态的线程获得了 CPU 时间片timeslice后就处于 **RUNNING运行** 状态。
> 在操作系统中层面线程有 READY 和 RUNNING 状态,而在 JVM 层面只能看到 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运行中** 状态 。
> 在操作系统层面,线程有 READY 和 RUNNING 状态;而在 JVM 层面,只能看到 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运行中** 状态 。
>
> **为什么 JVM 没有区分这两种状态呢?** (摘自:[java线程运行怎么有第六种状态 - Dawell的回答](https://www.zhihu.com/question/56494969/answer/154053599) 现在的<b>时分</b>time-sharing<b>多任务</b>multi-task操作系统架构通常都是用所谓的“<b>时间分片</b>time quantum or time slice”方式进行<b>抢占式</b>preemptive轮转调度round-robin式。这个时间分片通常是很小的一个线程一次最多只能在 CPU 上运行比如 10-20ms 的时间(此时处于 running 状态),也即大概只有 0.01 秒这一量级,时间片用后就要被切换下来放入调度队列的末尾等待再次调度。(也即回到 ready 状态)。线程切换的如此之快,区分这两种状态就没什么意义了。

View File

@ -7,76 +7,30 @@ head:
- - meta
- name: keywords
content: 多线程,死锁,synchronized,ReentrantLock,volatile,ThreadLocal,线程池,CAS,AQS
- - meta
- name: description
content: Java并发常见知识点和面试题总结含详细解答希望对你有帮助
---
## JMM(Java Memory Model)
JMMJava 内存模型)相关的问题比较多,也比较重要,于是我单独抽了一篇文章来总结 JMM 相关的知识点和问题: [JMMJava 内存模型)详解](./jmm.md) 。
## volatile 关键字
要想理解透彻 volatile 关键字,我们先要从 **CPU 缓存模型** 说起!
### CPU 缓存模型了解吗?
**为什么要弄一个 CPU 高速缓存呢?** 类比我们开发网站后台系统使用的缓存(比如 Redis是为了解决程序处理速度和访问常规关系型数据库速度不对等的问题。 **CPU 缓存则是为了解决 CPU 处理速度和内存处理速度不对等的问题。**
我们甚至可以把 **内存可以看作外存的高速缓存**,程序运行的时候我们把外存的数据复制到内存,由于内存的处理速度远远高于外存,这样提高了处理速度。
总结:**CPU Cache 缓存的是内存数据用于解决 CPU 处理速度和内存不匹配的问题,内存缓存的是硬盘数据用于解决硬盘访问速度过慢的问题。**
为了更好地理解,我画了一个简单的 CPU Cache 示意图如下(实际上,现代的 CPU Cache 通常分为三层,分别叫 L1,L2,L3 Cache:
![cpu-cache](./images/interview-questions/cpu-cache.png)
**CPU Cache 的工作方式:**
先复制一份数据到 CPU Cache 中,当 CPU 需要用到的时候就可以直接从 CPU Cache 中读取数据,当运算完成后,再将运算得到的数据写回 Main Memory 中。但是,这样存在 **内存缓存不一致性的问题** !比如我执行一个 i++操作的话,如果两个线程同时执行的话,假设两个线程从 CPU Cache 中读取的 i=1两个线程做了 1++运算完之后再写回 Main Memory 之后 i=2而正确结果应该是 i=3。
**CPU 为了解决内存缓存不一致性问题可以通过制定缓存一致协议或者其他手段来解决。**
### 讲一下 JMM(Java 内存模型)
Java 内存模型抽象了线程和主内存之间的关系就比如说线程之间的共享变量必须存储在主内存中。Java 内存模型主要目的是为了屏蔽系统和硬件的差异,避免一套代码在不同的平台下产生的效果不一致。
在 JDK1.2 之前Java 的内存模型实现总是从**主存**(即共享内存)读取变量,是不需要进行特别的注意的。而在当前的 Java 内存模型下,线程可以把变量保存**本地内存**(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成**数据的不一致**。
> - **主内存** :所有线程创建的实例对象都存放在主内存中,不管该实例对象是成员变量还是方法中的本地变量(也称局部变量)
> - **本地内存** :每个线程都有一个私有的本地内存来存储共享变量的副本,并且,每个线程只能访问自己的本地内存,无法访问其他线程的本地内存。本地内存是 JMM 抽象出来的一个概念,存储了主内存中的共享变量副本。
![JMM(Java 内存模型)](./images/interview-questions/jmm.png)
### Java 内存区域和内存模型(JMM)有何区别?
**Java 内存区域和内存模型是完全不一样的两个东西!!!**
- Java 内存区域定义了JVM 在运行时如何分区存储程序数据,就比如说堆主要用于存放对象实例。
- Java 内存模型抽象了线程和主内存之间的关系,就比如说线程之间的共享变量必须存储在主内存中,其目的是为了屏蔽系统和硬件的差异,避免一套代码在不同的平台下产生的效果不一致。
### 如何保证变量的可见性?
`volatile` 关键字可以保证变量的可见性,如果我们将变量声明为 **`volatile`** ,这就指示 JVM这个变量是共享且不稳定的每次使用它都到主存中进行读取。
在 Java 中,`volatile` 关键字可以保证变量的可见性,如果我们将变量声明为 **`volatile`** ,这就指示 JVM这个变量是共享且不稳定的每次使用它都到主存中进行读取。
![volatile关键字可见性](./images/interview-questions/jmm2.png)
![JMM(Java 内存模型)](./images/jmm/jmm.png)
### 什么是指令重排序?如何禁止指令重排序?
`volatile` 关键字其实并非是 Java 语言特有的,在 C 语言里也有,它最原始的意义就是禁用 CPU 缓存。如果我们将一个变量使用 `volatile` 修饰,这就指示 编译器,这个变量是共享且不稳定的,每次使用它都到主存中进行读取。
为了提升执行速度/性能,计算机在执行程序代码的时候,会对指令就行重排序
`volatile` 关键字能保证数据的可见性,但不能保证数据的原子性。`synchronized` 关键字两者都能保证。
指令重排序简单来说就是系统在执行代码的时候并不一定是按照你写的代码的顺序依次执行。
### 如何禁止指令重排序?
常见的重排序有下面 2 种情况:
- **编译器优化重排** :编译器(包括 JVM、JIT 编译器等)在不改变单线程程序语义的前提下,重新安排语句的执行顺序。
- **指令并行重排** :和编译器优化重排类似,
另外,内存系统也会有“重排序”,但有不是真正意义上的重排序。在 JMM 里表现为主存和本地内存,而主存和本地内存的内容可能不一致,所以这也会导致程序表现出乱序的行为。
**指令重排序可以保证串行语义一致,但是没有义务保证多线程间的语义也一致**。所以在多线程下,指令重排序可能会导致一些问题。
**`volatile` 关键字除了可以保证变量的可见性,还有一个重要的作用就是防止 JVM 的指令重排序。**
如果我们将变量声明为 **`volatile`** ,在对这个变量进行读写操作的时候,编译器会通过 **内存屏障** 来禁止指令重排序。
内存屏障Memory Barrier或有时叫做内存栅栏Memory Fence是一种CPU指令用于控制特定条件下的重排序和内存可见性问题。Java编译器也会根据内存屏障的规则禁止重排序。
**在 Java 中,`volatile` 关键字除了可以保证变量的可见性,还有一个重要的作用就是防止 JVM 的指令重排序。** 如果我们将变量声明为 **`volatile`** ,在对这个变量进行读写操作的时候,会通过插入特定的 **内存屏障** 的方式来禁止指令重排序。
在 Java 中,`Unsafe` 类提供了三个开箱即用的内存屏障相关的方法,屏蔽了操作系统底层的差异:
@ -86,7 +40,135 @@ public native void storeFence();
public native void fullFence();
```
理论上来说,你通过这个三个方法也可以实现和`volatile`禁止重排序一样的效果。
理论上来说,你通过这个三个方法也可以实现和`volatile`禁止重排序一样的效果,只是会麻烦一些。
下面我以一个常见的面试题为例讲解一下 `volatile` 关键字禁止指令重排序的效果。
面试中面试官经常会说:“单例模式了解吗?来给我手写一下!给我解释一下双重检验锁方式实现单例模式的原理呗!”
**双重校验锁实现对象单例(线程安全)**
```java
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
//先判断对象是否已经实例过,没有实例化过才进入加锁代码
if (uniqueInstance == null) {
//类对象加锁
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
```
`uniqueInstance` 采用 `volatile` 关键字修饰也是很有必要的, `uniqueInstance = new Singleton();` 这段代码其实是分为三步执行:
1. 为 `uniqueInstance` 分配内存空间
2. 初始化 `uniqueInstance`
3. 将 `uniqueInstance` 指向分配的内存地址
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如线程 T1 执行了 1 和 3此时 T2 调用 `getUniqueInstance`() 后发现 `uniqueInstance` 不为空,因此返回 `uniqueInstance`,但此时 `uniqueInstance` 还未被初始化。
### volatile 可以保证原子性么?
**`volatile` 关键字能保证变量的可见性,但不能保证对变量的操作是原子性的。**
我们通过下面的代码即可证明:
```java
/**
* 微信搜 JavaGuide 回复"面试突击"即可免费领取个人原创的 Java 面试手册
*
* @author Guide哥
* @date 2022/08/03 13:40
**/
public class VolatoleAtomicityDemo {
public volatile static int inc = 0;
public void increase() {
inc++;
}
public static void main(String[] args) throws InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(5);
VolatoleAtomicityDemo volatoleAtomicityDemo = new VolatoleAtomicityDemo();
for (int i = 0; i < 5; i++) {
threadPool.execute(() -> {
for (int j = 0; j < 500; j++) {
volatoleAtomicityDemo.increase();
}
});
}
// 等待1.5秒,保证上面程序执行完成
Thread.sleep(1500);
System.out.println(inc);
threadPool.shutdown();
}
}
```
正常情况下,运行上面的代码理应输出 `2500`。但你真正运行了上面的代码之后,你会发现每次输出结果都小于 `2500`
为什么会出现这种情况呢?不是说好了,`volatile` 可以保证变量的可见性嘛!
也就是说,如果 `volatile` 能保证 `inc++` 操作的原子性的话。每个线程中对 `inc` 变量自增完之后其他线程可以立即看到修改后的值。5 个线程分别进行了 500 次操作,那么最终 inc 的值应该是 5\*500=2500。
很多人会误认为自增操作 `inc++` 是原子性的,实际上,`inc++` 其实是一个复合操作,包括三步:
1. 读取 inc 的值。
2. 对 inc 加 1。
3. 将 inc 的值写回内存。
`volatile` 是无法保证这三个操作是具有原子性的,有可能导致下面这种情况出现:
1. 线程 1 对 `inc` 进行读取操作之后,还未对其进行修改。线程 2 又读取了 `inc`的值并对其进行修改(+1再将`inc` 的值写回内存。
2. 线程 2 操作完毕后,线程 1 对 `inc`的值进行修改(+1再将`inc` 的值写回内存。
这也就导致两个线程分别对 `inc` 进行了一次自增操作后,`inc` 实际上只增加了 1。
其实,如果想要保证上面的代码运行正确也非常简单,利用 `synchronized``Lock`或者`AtomicInteger`都可以。
使用 `synchronized` 改进:
```java
public synchronized void increase() {
inc++;
}
```
使用 `AtomicInteger` 改进:
```java
public AtomicInteger inc = new AtomicInteger();
public void increase() {
inc.getAndIncrement();
}
```
使用 `ReentrantLock` 改进:
```java
Lock lock = new ReentrantLock();
public void increase() {
lock.lock();
try {
inc++;
} finally {
lock.unlock();
}
}
```
## synchronized 关键字
@ -149,47 +231,6 @@ synchronized(this) {
- `synchronized` 关键字加到实例方法上是给对象实例上锁;
- 尽量不要使用 `synchronized(String a)` 因为 JVM 中,字符串常量池具有缓存功能。
下面我以一个常见的面试题为例讲解一下 `synchronized` 关键字的具体使用。
面试中面试官经常会说:“单例模式了解吗?来给我手写一下!给我解释一下双重检验锁方式实现单例模式的原理呗!”
**双重校验锁实现对象单例(线程安全)**
```java
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
//先判断对象是否已经实例过,没有实例化过才进入加锁代码
if (uniqueInstance == null) {
//类对象加锁
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
```
另外,需要注意 `uniqueInstance` 采用 `volatile` 关键字修饰也是很有必要。
`uniqueInstance` 采用 `volatile` 关键字修饰也是很有必要的, `uniqueInstance = new Singleton();` 这段代码其实是分为三步执行:
1. 为 `uniqueInstance` 分配内存空间
2. 初始化 `uniqueInstance`
3. 将 `uniqueInstance` 指向分配的内存地址
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如线程 T1 执行了 1 和 3此时 T2 调用 `getUniqueInstance`() 后发现 `uniqueInstance` 不为空,因此返回 `uniqueInstance`,但此时 `uniqueInstance` 还未被初始化。
使用 `volatile` 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
### 构造方法可以使用 synchronized 关键字修饰么?
先说结论:**构造方法不能使用 synchronized 关键字修饰。**
@ -227,13 +268,11 @@ public class SynchronizedDemo {
在执行`monitorenter`时,会尝试获取对象的锁,如果锁的计数器为 0 则表示锁可以被获取,获取后将锁计数器设为 1 也就是加 1。
![执行 monitorenter 获取锁](./images/interview-questions/synchronized-get-lock-code-block.png)
![执行 monitorenter 获取锁](./images/interview-questions/synchronized-get-lock-code-block.jpg)
对象锁的的拥有者线程才可以执行 `monitorexit` 指令来释放锁。在执行 `monitorexit` 指令后,将锁计数器设为 0表明锁被释放其他线程可以尝试获取锁。
![执行 monitorexit 释放锁](./images/interview-questions/synchronized-release-lock-block.png)
![执行 monitorexit 释放锁](./images/interview-questions/synchronized-release-lock-block.jpg)
如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外一个线程释放为止。
@ -262,9 +301,9 @@ public class SynchronizedDemo2 {
**不过两者的本质都是对对象监视器 monitor 的获取。**
相关推荐:[Java锁与线程的那些事 - 有赞技术团队](https://tech.youzan.com/javasuo-yu-xian-cheng-de-na-xie-shi/) 。
相关推荐:[Java 锁与线程的那些事 - 有赞技术团队](https://tech.youzan.com/javasuo-yu-xian-cheng-de-na-xie-shi/) 。
🧗🏻进阶一下:学有余力的小伙伴可以抽时间详细研究一下对象监视器 `monitor`
🧗🏻 进阶一下:学有余力的小伙伴可以抽时间详细研究一下对象监视器 `monitor`
### JDK1.6 之后的 synchronized 关键字底层做了哪些优化?
@ -274,11 +313,11 @@ JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、
关于这几种优化的详细信息可以查看下面这篇文章:[Java6 及以上版本对 synchronized 的优化](https://www.cnblogs.com/wuqinglong/p/9945618.html)
### synchronized 和 volatile 的区别?
### synchronized 和 volatile 的区别?
`synchronized` 关键字和 `volatile` 关键字是两个互补的存在,而不是对立的存在!
- `volatile` 关键字是线程同步的轻量级实现,所以 `volatile `性能肯定比` synchronized `关键字要好 。但是 `volatile` 关键字只能用于变量而 `synchronized` 关键字可以修饰方法以及代码块 。
- `volatile` 关键字是线程同步的轻量级实现,所以 `volatile`性能肯定比`synchronized`关键字要好 。但是 `volatile` 关键字只能用于变量而 `synchronized` 关键字可以修饰方法以及代码块 。
- `volatile` 关键字能保证数据的可见性,但不能保证数据的原子性。`synchronized` 关键字两者都能保证。
- `volatile`关键字主要用于解决变量在多个线程之间的可见性,而 `synchronized` 关键字解决的是多个线程之间访问资源的同步性。
@ -304,27 +343,21 @@ JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、
**如果你想使用上述功能,那么选择 ReentrantLock 是一个不错的选择。性能已不是选择标准**
### 并发编程的三个重要特性
1. **原子性** : 一次操作或者多次操作,要么所有的操作全部都得到执行并且不会受到任何因素的干扰而中断,要么都不执行。`synchronized` 可以保证代码片段的原子性。
2. **可见性** :当一个线程对共享变量进行了修改,那么另外的线程都是立即可以看到修改后的最新值。`volatile` 关键字可以保证共享变量的可见性。
3. **有序性** 代码在执行的过程中的先后顺序Java 在编译器以及运行期间的优化,代码的执行顺序未必就是编写代码时候的顺序。`volatile` 关键字可以禁止指令进行重排序优化。
## ThreadLocal
### ThreadLocal 简介
### ThreadLocal 有什么用?
通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。**如果想实现每一个线程都有自己的专属本地变量该如何解决呢?** JDK 中提供的`ThreadLocal`类正是为了解决这样的问题。 **`ThreadLocal`类主要解决的就是让每个线程绑定自己的值,可以将`ThreadLocal`类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。**
通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。**如果想实现每一个线程都有自己的专属本地变量该如何解决呢?**
**如果你创建了一个`ThreadLocal`变量,那么访问这个变量的每个线程都会有这个变量的本地副本,这也是`ThreadLocal`变量名的由来。他们可以使用 `get``set` 方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题。**
JDK 中自带的`ThreadLocal`类正是为了解决这样的问题。 **`ThreadLocal`类主要解决的就是让每个线程绑定自己的值,可以将`ThreadLocal`类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。**
再举个简单的例子:
如果你创建了一个`ThreadLocal`变量,那么访问这个变量的每个线程都会有这个变量的本地副本,这也是`ThreadLocal`变量名的由来。他们可以使用 `get``set` 方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题。
比如有两个人去宝屋收集宝物,这两个共用一个袋子的话肯定会产生争执,但是给他们两个人每个人分配一个袋子的话就不会出现这样的问题。如果把这两个人比作线程的话,那么 ThreadLocal 就是用来避免这两个线程竞争的。
再举个简单的例子:两个人去宝屋收集宝物,这两个共用一个袋子的话肯定会产生争执,但是给他们两个人每个人分配一个袋子的话就不会出现这样的问题。如果把这两个人比作线程的话,那么 ThreadLocal 就是用来避免这两个线程竞争的。
### ThreadLocal 示例
### 如何使用 ThreadLocal
相信看了上面的解释,大家已经搞懂 ThreadLocal 类是个什么东西了。
相信看了上面的解释,大家已经搞懂 `ThreadLocal` 类是个什么东西了。下面简单演示一下如何在项目中实际使用 `ThreadLocal`
```java
import java.text.SimpleDateFormat;
@ -362,7 +395,7 @@ public class ThreadLocalExample implements Runnable{
```
Output:
输出结果 :
```
Thread Name= 0 default Formatter = yyyyMMdd HHmm
@ -387,7 +420,7 @@ Thread Name= 8 formatter = yy-M-d ah:mm
Thread Name= 9 formatter = yy-M-d ah:mm
```
从输出中可以看出,Thread-0 已经改变了 formatter 的值,但仍然是 thread-2 默认格式化程序与初始化值相同,其他线程也一样。
从输出中可以看出,虽然 `Thread-0` 已经改变了 `formatter` 的值,但 `Thread-1` 默认格式化值与初始化值相同,其他线程也一样。
上面有一段代码用到了创建 `ThreadLocal` 变量的那段代码用到了 Java8 的知识它等于下面这段代码如果你写了下面这段代码的话IDEA 会提示你转换为 Java8 的格式(IDEA 真的不错!)。因为 ThreadLocal 类在 Java 8 中扩展,使用一个新的方法`withInitial()`,将 Supplier 功能接口作为参数。
@ -400,7 +433,7 @@ private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<S
};
```
### ThreadLocal 原理
### ThreadLocal 原理了解吗?
`Thread`类源代码入手。
@ -446,15 +479,19 @@ ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
比如我们在同一个线程中声明了两个 `ThreadLocal` 对象的话, `Thread`内部都是使用仅有的那个`ThreadLocalMap` 存放数据的,`ThreadLocalMap`的 key 就是 `ThreadLocal`对象value 就是 `ThreadLocal` 对象调用`set`方法设置的值。
![ThreadLocal数据结构](./images/threadlocal数据结构.png)
`ThreadLocal` 数据结构如下图所示:
![threadlocal-data-structure](./images/interview-questions/threadlocal-data-structure.jpg)
`ThreadLocalMap``ThreadLocal`的静态内部类。
![ThreadLocal内部类](./images/ThreadLocal内部类.png)
![ThreadLocal内部类](./images/interview-questions/thread-local-inner-class.png)
### ThreadLocal 内存泄露问题
### ThreadLocal 内存泄露问题是怎么导致的?
`ThreadLocalMap` 中使用的 key 为 `ThreadLocal` 的弱引用,而 value 是强引用。所以,如果 `ThreadLocal` 没有被外部强引用的情况下在垃圾回收的时候key 会被清理掉,而 value 不会被清理掉。这样一来,`ThreadLocalMap` 中就会出现 key 为 null 的 Entry。假如我们不做任何措施的话value 永远无法被 GC 回收这个时候就可能会产生内存泄露。ThreadLocalMap 实现中已经考虑了这种情况,在调用 `set()``get()``remove()` 方法的时候,会清理掉 key 为 null 的记录。使用完 `ThreadLocal`方法后 最好手动调用`remove()`方法
`ThreadLocalMap` 中使用的 key 为 `ThreadLocal` 的弱引用,而 value 是强引用。所以,如果 `ThreadLocal` 没有被外部强引用的情况下在垃圾回收的时候key 会被清理掉,而 value 不会被清理掉。
这样一来,`ThreadLocalMap` 中就会出现 key 为 null 的 Entry。假如我们不做任何措施的话value 永远无法被 GC 回收,这个时候就可能会产生内存泄露。`ThreadLocalMap` 实现中已经考虑了这种情况,在调用 `set()``get()``remove()` 方法的时候,会清理掉 key 为 null 的记录。使用完 `ThreadLocal`方法后 最好手动调用`remove()`方法
```java
static class Entry extends WeakReference<ThreadLocal<?>> {
@ -478,9 +515,6 @@ static class Entry extends WeakReference<ThreadLocal<?>> {
- 《深入理解 Java 虚拟机》
- 《实战 Java 高并发程序设计》
- 《Java 并发编程的艺术》
- 《深入浅出Java多线程》http://concurrent.redspider.group/RedSpider.html
- Java内存访问重排序的研究https://tech.meituan.com/2014/09/23/java-memory-reordering.html
- 嘿,同学,你要的 Java 内存模型 (JMM) 来了https://xie.infoq.cn/article/739920a92d0d27e2053174ef2
- Java并发之AQS详解https://www.cnblogs.com/waterystone/p/4920797.html
- Java并发包基石-AQS详解https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html
- Guide to the Volatile Keyword in Java - Baeldunghttps://www.baeldung.com/java-volatile
- 理解 Java 中的 ThreadLocal - 技术小黑屋https://droidyue.com/blog/2016/03/13/learning-threadlocal-in-java/
- ThreadLocal (Java Platform SE 8 ) - Oracle Help Centerhttps://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html

View File

@ -7,6 +7,7 @@ head:
- - meta
- name: keywords
content: 多线程,死锁,线程池,CAS,AQS
- - meta
- name: description
content: Java并发常见知识点和面试题总结含详细解答希望对你有帮助
---
@ -249,7 +250,7 @@ public class ThreadPoolExecutorDemo {
new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 10; i++) {
//创建WorkerThread对象WorkerThread类实现了Runnable 接口)
//创建 MyRunnable 对象MyRunnable 类实现了Runnable 接口)
Runnable worker = new MyRunnable("" + i);
//执行Runnable
executor.execute(worker);
@ -630,4 +631,4 @@ CompletableFuture<Void> allFutures = CompletableFuture.allOf(
- 《深入理解 Java 虚拟机》
- 《实战 Java 高并发程序设计》
- Java并发之AQS详解https://www.cnblogs.com/waterystone/p/4920797.html
- Java并发包基石-AQS详解https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html
- Java并发包基石-AQS详解https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html

232
docs/java/concurrent/jmm.md Normal file
View File

@ -0,0 +1,232 @@
---
title: JMMJava 内存模型)详解
category: Java
tag:
- Java并发
head:
- - meta
- name: keywords
content: CPU 缓存模型,指令重排序,Java 内存模型JMM,happens-before
- - meta
- name: description
content: 对于 Java 来说,你可以把 JMM 看作是 Java 定义的并发编程相关的一组规范,除了抽象了线程和主内存之间的关系之外,其还规定了从 Java 源代码到 CPU 可执行指令的这个转化过程要遵守哪些和并发相关的原则和规范,其主要目的是为了简化多线程编程,增强程序可移植性的。
---
要想理解透彻 JMMJava 内存模型),我们先要从 **CPU 缓存模型和指令重排序** 说起!
## 从 CPU 缓存模型说起
**为什么要弄一个 CPU 高速缓存呢?** 类比我们开发网站后台系统使用的缓存(比如 Redis是为了解决程序处理速度和访问常规关系型数据库速度不对等的问题。 **CPU 缓存则是为了解决 CPU 处理速度和内存处理速度不对等的问题。**
我们甚至可以把 **内存可以看作外存的高速缓存**,程序运行的时候我们把外存的数据复制到内存,由于内存的处理速度远远高于外存,这样提高了处理速度。
总结:**CPU Cache 缓存的是内存数据用于解决 CPU 处理速度和内存不匹配的问题,内存缓存的是硬盘数据用于解决硬盘访问速度过慢的问题。**
为了更好地理解,我画了一个简单的 CPU Cache 示意图如下(实际上,现代的 CPU Cache 通常分为三层,分别叫 L1,L2,L3 Cache:
![CPU 缓存模型示意图](./images/jmm/cpu-cache.jpg)
**CPU Cache 的工作方式:** 先复制一份数据到 CPU Cache 中,当 CPU 需要用到的时候就可以直接从 CPU Cache 中读取数据,当运算完成后,再将运算得到的数据写回 Main Memory 中。但是,这样存在 **内存缓存不一致性的问题** !比如我执行一个 i++ 操作的话,如果两个线程同时执行的话,假设两个线程从 CPU Cache 中读取的 i=1两个线程做了 1++ 运算完之后再写回 Main Memory 之后 i=2而正确结果应该是 i=3。
**CPU 为了解决内存缓存不一致性问题可以通过制定缓存一致协议(比如 [MESI 协议](https://zh.wikipedia.org/wiki/MESI%E5%8D%8F%E8%AE%AE))或者其他手段来解决。** 这个缓存缓存一致性协议指的是在 CPU 高速缓存与主内存交互的时候需要准守的原则和规范。不同的 CPU 中,使用的缓存一致性协议通常也会有所不同。
![缓存一致性协议](./images/jmm/cpu-cache-protocol.jpg)
我们的程序运行在操作系统之上,操作系统屏蔽了底层硬件的操作细节,将各种硬件资源虚拟化。于是,操作系统也就同样需要解决内存缓存不一致性问题。
操作系统通过 **内存模型Memory Model** 定义一系列规范来解决这个问题。无论是 Windows 系统,还是 Linux 系统,它们都有特定的内存模型。
## 指令重排序
说完了 CPU 缓存模型,我们再来看看另外一个比较重要的概念 **指令重排序**
为了提升执行速度/性能,计算机在执行程序代码的时候,会对指令进行重排序。
**什么是指令重排序?** 简单来说就是系统在执行代码的时候并不一定是按照你写的代码的顺序依次执行。
常见的指令重排序有下面 2 种情况:
- **编译器优化重排** :编译器(包括 JVM、JIT 编译器等)在不改变单线程程序语义的前提下,重新安排语句的执行顺序。
- **指令并行重排** :现代处理器采用了指令级并行技术(Instruction-Level ParallelismILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
另外,内存系统也会有“重排序”,但有不是真正意义上的重排序。在 JMM 里表现为主存和本地内存的内容可能不一致,进而导致程序在多线程下执行可能出现问题。
Java 源代码会经历 **编译器优化重排 —> 指令并行重排 —> 内存系统重排** 的过程,最终才变成操作系统可执行的指令序列。
**指令重排序可以保证串行语义一致,但是没有义务保证多线程间的语义也一致** ,所以在多线程下,指令重排序可能会导致一些问题。
编译器和处理器的指令重排序的处理方式不一样。对于编译器通过禁止特定类型的编译器的当时来禁止重排序。对于处理器通过插入内存屏障Memory Barrier或有时叫做内存栅栏Memory Fence的方式来禁止特定类型的处理器重排序。指令并行重排和内存系统重排都属于是处理器级别的指令重排序。
> 内存屏障Memory Barrier或有时叫做内存栅栏Memory Fence是一种 CPU 指令,用来禁止处理器指令发生重排序(像屏障一样),从而保障指令执行的有序性。另外,为了达到屏障的效果,它也会使处理器写入、读取值之前,将主内存的值写入高速缓存,清空无效队列,从而保障变量的可见性。
## JMM(Java Memory Model)
### 什么是 JMM为什么需要 JMM
Java 是最早尝试提供内存模型的编程语言。由于早期内存模型存在一些缺陷(比如非常容易削弱编译器的优化能力),从 Java5 开始Java 开始使用新的内存模型 [《JSR-133Java Memory Model and Thread Specification》](http://www.cs.umd.edu/~pugh/java/memoryModel/CommunityReview.pdf) 。
一般来说编程语言也可以直接复用操作系统层面的内存模型。不过不同的操作系统内存模型不同。如果直接复用操作系统层面的内存模型就可能会导致同样一套代码换了一个操作系统就无法执行了。Java 语言是跨平台的,它需要自己提供一套内存模型以屏蔽系统差异。
这只是 JMM 存在的其中一个原因。实际上,对于 Java 来说,你可以把 JMM 看作是 Java 定义的并发编程相关的一组规范,除了抽象了线程和主内存之间的关系之外,其还规定了从 Java 源代码到 CPU 可执行指令的这个转化过程要遵守哪些和并发相关的原则和规范,其主要目的是为了简化多线程编程,增强程序可移植性的。
**为什么要遵守这些并发相关的原则和规范呢?** 这是因为并发编程下,像 CPU 多级缓存和指令重排这类设计可能会导致程序运行出现一些问题。就比如说我们上面提到的指令重排序就可能会让多线程程序的执行出现问题为此JMM 抽象了 happens-before 原则(后文会详细介绍到)来解决这个指令重排序问题。
JMM 说白了就是定义了一些规范来解决这些问题,开发发者可以利用这些规范更方便地开发多线程程序。对于 Java 开发者说,你不需要了解底层原理,直接使用并发相关的一些关键字和类(比如 `volatile``synchronized`、各种 `Lock`)即可开发出并发安全的程序。
### JMM 是如何抽象线程和主内存之间的关系?
**Java 内存模型JMM** 抽象了线程和主内存之间的关系,就比如说线程之间的共享变量必须存储在主内存中。
在 JDK1.2 之前Java 的内存模型实现总是从 **主存** (即共享内存)读取变量,是不需要进行特别的注意的。而在当前的 Java 内存模型下,线程可以把变量保存 **本地内存** (比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。
这和我们上面讲到的 CPU 缓存模型非常相似。
**什么是主内存?什么是本地内存?**
- **主内存** :所有线程创建的实例对象都存放在主内存中,不管该实例对象是成员变量还是方法中的本地变量(也称局部变量)
- **本地内存** :每个线程都有一个私有的本地内存来存储共享变量的副本,并且,每个线程只能访问自己的本地内存,无法访问其他线程的本地内存。本地内存是 JMM 抽象出来的一个概念,存储了主内存中的共享变量副本。
Java 内存模型的抽象示意图如下:
![JMM(Java 内存模型)](./images/jmm/jmm.png)
从上图来看,线程 1 与线程 2 之间如果要进行通信的话,必须要经历下面 2 个步骤:
1. 线程 1 把本地内存中修改过的共享变量副本的值同步到主内存中去。
2. 线程 2 到主存中读取对应的共享变量的值。
也就是说JMM 为共享变量提供了可见性的保障。
不过,多线程下,对主内存中的一个共享变量进行操作有可能诱发线程安全问题。举个例子:
1. 线程 1 和线程 2 分别对同一个共享变量进行操作,一个执行修改,一个执行读取。
2. 线程 2 读取到的是线程 1 修改之前的值还是修改后的值并不确定,都有可能,因为线程 1 和线程 2 都是先将共享变量从主内存拷贝到对应线程的工作内存中。
关于主内存与工作内存直接的具体交互协议即一个变量如何从主内存拷贝到工作内存如何从工作内存同步到主内存之间的实现细节Java 内存模型定义来以下八种同步操作(了解即可,无需死记硬背):
- **锁定lock**: 作用于主内存中的变量,将他标记为一个线程独享变量。
- **解锁unlock**: 作用于主内存中的变量,解除变量的锁定状态,被解除锁定状态的变量才能被其他线程锁定。
- **read读取**:作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的 load 动作使用。
- **load(载入)**:把 read 操作从主内存中得到的变量值放入工作内存的变量的副本中。
- **use(使用)**:把工作内存中的一个变量的值传给执行引擎,每当虚拟机遇到一个使用到变量的指令时都会使用该指令。
- **assign赋值**:作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
- **store存储**:作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的 write 操作使用。
- **write写入**:作用于主内存的变量,它把 store 操作从工作内存中得到的变量的值放入主内存的变量中。
除了这 8 种同步操作之外,还规定了下面这些同步规则来保证这些同步操作的正确执行(了解即可,无需死记硬背):
- 不允许一个线程无原因地(没有发生过任何 assign 操作)把数据从线程的工作内存同步回主内存中。
- 一个新的变量只能在主内存中 “诞生”不允许在工作内存中直接使用一个未被初始化load 或 assign的变量换句话说就是对一个变量实施 use 和 store 操作之前,必须先执行过了 assign 和 load 操作。
- 一个变量在同一个时刻只允许一条线程对其进行 lock 操作,但 lock 操作可以被同一条线程重复执行多次,多次执行 lock 后,只有执行相同次数的 unlock 操作,变量才会被解锁。
- 如果对一个变量执行 lock 操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行 load 或 assign 操作初始化变量的值。
- 如果一个变量事先没有被 lock 操作锁定,则不允许对它执行 unlock 操作,也不允许去 unlock 一个被其他线程锁定住的变量。
- ......
### Java 内存区域和 JMM 有何区别?
这是一个比较常见的问题,很多初学者非常容易搞混。 **Java 内存区域和内存模型是完全不一样的两个东西**
- JVM 内存结构和 Java 虚拟机的运行时区域相关,定义了 JVM 在运行时如何分区存储程序数据,就比如说堆主要用于存放对象实例。
- Java 内存模型和 Java 的并发编程相关,抽象了线程和主内存之间的关系就比如说线程之间的共享变量必须存储在主内存中,规定了从 Java 源代码到 CPU 可执行指令的这个转化过程要遵守哪些和并发相关的原则和规范,其主要目的是为了简化多线程编程,增强程序可移植性的。
### happens-before 原则是什么?
happens-before 这个概念最早诞生于 Leslie Lamport 于 1978 年发表的论文[《TimeClocks and the Ordering of Events in a Distributed System》](https://lamport.azurewebsites.net/pubs/time-clocks.pdf)。在这篇论文中Leslie Lamport 提出了[逻辑时钟](https://writings.sh/post/logical-clocks)的概念,这也成了第一个逻辑时钟算法 。在分布式环境中,通过一系列规则来定义逻辑时钟的变化,从而能通过逻辑时钟来对分布式系统中的事件的先后顺序进行判断。**逻辑时钟并不度量时间本身,仅区分事件发生的前后顺序,其本质就是定义了一种 happens-before 关系。**
上面提到的 happens-before 这个概念诞生的背景并不是重点,简单了解即可。
JSR 133 引入了 happens-before 这个概念来描述两个操作之间的内存可见性。
**为什么需要 happens-before 原则?** happens-before 原则的诞生是为了程序员和编译器、处理器之间的平衡。程序员追求的是易于理解和编程的强内存模型遵守既定规则编码即可。编译器和处理器追求的是较少约束的弱内存模型让它们尽己所能地去优化性能让性能最大化。happens-before 原则的设计思想其实非常简单:
- 为了对编译器和处理器的约束尽可能少,只要不改变程序的执行结果(单线程程序和正确执行的多线程程序),编译器和处理器怎么进行重排序优化都行。
- 对于会改变程序执行结果的重排序JMM 要求编译器和处理器必须禁止这种重排序。
下面这张是 《Java 并发编程的艺术》这本书中的一张 JMM 设计思想的示意图,非常清晰。
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/java/concurrent/image-20220731155332375.png)
了解了 happens-before 原则的设计思想,我们再来看看 JSR-133 对 happens-before 原则的定义:
- 如果一个操作 happens-before 另一个操作,那么第一个操作的执行结果将对第二个操作可见,并且第一个操作的执行顺序排在第二个操作之前。
- 两个操作之间存在 happens-before 关系,并不意味着 Java 平台的具体实现必须要按照 happens-before 关系指定的顺序来执行。如果重排序之后的执行结果,与按 happens-before 关系来执行的结果一致,那么 JMM 也允许这样的重排序。
我们看下面这段代码:
```java
int userNum = getUserNum(); // 1
int teacherNum = getTeacherNum(); // 2
int totalNum = userNum + teacherNum; // 3
```
- 1 happens-before 2
- 2 happens-before 3
- 1 happens-before 3
虽然 1 happens-before 2但对 1 和 2 进行重排序不会影响代码的执行结果,所以 JMM 是允许编译器和处理器执行这种重排序的。但 1 和 2 必须是在 3 执行之前,也就是说 1,2 happens-before 3 。
**happens-before 原则表达的意义其实并不是一个操作发生在另外一个操作的前面,虽然这从程序员的角度上来说也并无大碍。更准确地来说,它更想表达的意义是前一个操作的结果对于后一个操作是可见的,无论这两个操作是否在同一个线程里。**
举个例子:操作 1 happens-before 操作 2即使操作 1 和操作 2 不在同一个线程内JMM 也会保证操作 1 的结果对操作 2 是可见的。
### happens-before 常见规则有哪些?谈谈你的理解?
happens-before 的规则就 8 条,说多不多,重点了解下面列举的 5 条即可。全记是不可能的,很快就忘记了,意义不大,随时查阅即可。
1. **程序顺序规则** :一个线程内,按照代码顺序,书写在前面的操作 happens-before 于书写在后面的操作;
2. **解锁规则** :解锁 happens-before 于加锁;
3. **volatile 变量规则** :对一个 volatile 变量的写操作 happens-before 于后面对这个 volatile 变量的读操作。说白了就是对 volatile 变量的写操作的结果对于发生于其后的任何操作都是可见的。
4. **传递规则** :如果 A happens-before B且 B happens-before C那么 A happens-before C
5. **线程启动规则** Thread 对象的 `start`方法 happens-before 于此线程的每一个动作。
如果两个操作不满足上述任意一个 happens-before 规则那么这两个操作就没有顺序的保障JVM 可以对这两个操作进行重排序。
### happens-before 和 JMM 什么关系?
happens-before 与 JMM 的关系用《Java 并发编程的艺术》这本书中的一张图就可以非常好的解释清楚。
![happens-before 与 JMM 的关系](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/java/concurrent/image-20220731084604667.png)
## 再看并发编程三个重要特性
### 原子性
一次操作或者多次操作,要么所有的操作全部都得到执行并且不会受到任何因素的干扰而中断,要么都不执行。
在 Java 中,可以借助`synchronized` 、各种 `Lock` 以及各种原子类实现原子性。
`synchronized` 和各种 `Lock` 可以保证任一时刻只有一个线程访问该代码块,因此可以保障原子性。各种原子类是利用 CAS (compare and swap) 操作(可能也会用到 `volatile`或者`final`关键字)来保证原子操作。
### 可见性
当一个线程对共享变量进行了修改,那么另外的线程都是立即可以看到修改后的最新值。
在 Java 中,可以借助`synchronized``volatile` 以及各种 `Lock` 实现可见性。
如果我们将变量声明为 `volatile` ,这就指示 JVM这个变量是共享且不稳定的每次使用它都到主存中进行读取。
### 有序性
由于指令重排序问题,代码的执行顺序未必就是编写代码时候的顺序。
我们上面讲重排序的时候也提到过:
> **指令重排序可以保证串行语义一致,但是没有义务保证多线程间的语义也一致** ,所以在多线程下,指令重排序可能会导致一些问题。
在 Java 中,`volatile` 关键字可以禁止指令进行重排序优化。
## 总结
- Java 是最早尝试提供内存模型的语言,其主要目的是为了简化多线程编程,增强程序可移植性的。
- CPU 可以通过制定缓存一致协议(比如 [MESI 协议](https://zh.wikipedia.org/wiki/MESI%E5%8D%8F%E8%AE%AE))来解决内存缓存不一致性问题。
- 为了提升执行速度/性能,计算机在执行程序代码的时候,会对指令进行重排序。 简单来说就是系统在执行代码的时候并不一定是按照你写的代码的顺序依次执行。**指令重排序可以保证串行语义一致,但是没有义务保证多线程间的语义也一致** ,所以在多线程下,指令重排序可能会导致一些问题。
- 你可以把 JMM 看作是 Java 定义的并发编程相关的一组规范,除了抽象了线程和主内存之间的关系之外,其还规定了从 Java 源代码到 CPU 可执行指令的这个转化过程要遵守哪些和并发相关的原则和规范,其主要目的是为了简化多线程编程,增强程序可移植性的。
- JSR 133 引入了 happens-before 这个概念来描述两个操作之间的内存可见性。
## 参考
- 《Java 并发编程的艺术》第三章 Java 内存模型
- 《深入浅出 Java 多线程》http://concurrent.redspider.group/RedSpider.html
- Java 内存访问重排序的研究https://tech.meituan.com/2014/09/23/java-memory-reordering.html
- 嘿,同学,你要的 Java 内存模型 (JMM) 来了https://xie.infoq.cn/article/739920a92d0d27e2053174ef2
- JSR 133 (Java Memory Model) FAQhttps://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html

View File

@ -1,5 +1,5 @@
---
title: ThreadLocal 关键字详解
title: ThreadLocal 详解
category: Java
tag:
- Java并发

View File

@ -30,25 +30,6 @@ icon: "xitongsheji"
- **[Caffeine](https://github.com/ben-manes/caffeine)** : 一款强大的本地缓存解决方案,性能非常 🐂。
- **[Redis](https://github.com/redis/redis)** :一个使用 C 语言开发的内存数据库,分布式缓存首选。
### 数据库建模
- **[CHINER](https://gitee.com/robergroup/chiner)** :开源免费的国产数据库建模工具。目标是做一款丰富数据库生态,独立于具体数据库之外的,数据库关系模型设计平台。前生是 [PDMan](https://gitee.com/robergroup/pdman),定位为 PowerDesigner 的免费替代方案。
开源的数据库建模工具比较少,以下是一些非开源的数据库建模工具(部分需要付费才能使用) :
- **[MySQL Workbench](https://www.mysql.com/products/workbench/)** : MySQL 官方为数据库架构师、开发人员和 DBA 提供的一个可视化工具。 MySQL Workbench 支持数据建模SQL 开发以及服务器配置、用户管理、性能优化、数据库备份以及迁移等功能,支持 Windows、Linux 和 Mac OS X 平台。
- **[Navicat Data Modeler](https://www.navicat.com.cn/products/navicat-data-modeler)** : 一款强大的和符合成本效益的数据库设计工具,它能帮助用户创建高质素的概念、逻辑和物理数据模型。让你可视化地设计数据库结构、执行逆向或正向工程程序、从 ODBC 数据源导入模型、生成复杂的 SQL/DDL 和打印模型到文件等。付费。
- **[DbSchema](https://dbschema.com/)** : 一款功能强大的数据库设计和管理的可视化工具,支持几乎所有的关系型和 NoSQL 数据库。付费。
- **[dbdiagram.io](https://dbdiagram.io/home)** : 是一款简单免费的在线 ER 图绘制工具,通过编写代码创建模型,专为开发人员和数据分析师而设计。它通过一个简单的自定义语言来生成数据模型,支持 MySQL、PostgreSQL、SQL Server 数据库 DDL 文件的正向工程和逆向工程、版本历史、在线共享、导出图片或者 PDF 等功能。dbdiagram.io 提供了免费版。
### 数据库管理
- **[Beekeeper Studio](https://github.com/beekeeper-studio/beekeeper-studio)** :跨平台数据库管理工具,颜值高,支持 SQLite、MySQL、MariaDB、Postgres、CockroachDB、SQL Server、Amazon Redshift。
- **[Sequel Pro](https://github.com/sequelpro/sequelpro)** :适用于 macOS 的 MySQL/MariaDB 数据库管理工具。
- **[DBeaver](https://github.com/dbeaver/dbeaver)** :一个基于 Java 开发 并且支持几乎所有的数据库产品的开源数据库管理工具。DBeaver 社区版不光支持关系型数据库比如 MySQL、PostgreSQL、MariaDB、SQLite、Oracle、Db2、SQL Server还比如 SQLite、H2 这些内嵌数据库。还支持常见的全文搜索引擎比如 Elasticsearch 和 Solr、大数据相关的工具比如 Hive 和 Spark。
- **[Kangaroo](https://gitee.com/dbkangaroo/kangaroo)** :袋鼠 是一款为热门数据库系统打造的管理客户端(SQLite / MySQL / PostgreSQL / ...) ,支持建表、查询、模型、同步、导入导出等功能,支持 Windows / Mac / Linux 等操作系统,力求打造成好用、好玩、开发友好的 SQL 工具。
- **[Mongood](https://github.com/RenzHoly/Mongood)** : MongoDB 图形化的管理工具。基于微软 Fluent UI支持自动黑暗模式。
### 数据同步
- **[Canal](https://github.com/alibaba/canal "canal")** [kə'næl] : Canal 译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费。

View File

@ -4,26 +4,47 @@ category: 开源项目
icon: tool
---
## Java 相关
## Java
- **[JADX](https://github.com/skylot/jadx)** :一款功能强大的反编译工具。
- **[Recaf](https://github.com/Col-E/Recaf)** Java 字节码编辑器,基于 ASMJava 字节码操作框架) 来修改字节码,可简化编辑已编译 Java 应用程序的过程。
## Redis 可视化管理
## 数据库
### 数据库建模
- **[CHINER](https://gitee.com/robergroup/chiner)** :开源免费的国产数据库建模工具。目标是做一款丰富数据库生态,独立于具体数据库之外的,数据库关系模型设计平台。前生是 [PDMan](https://gitee.com/robergroup/pdman),定位为 PowerDesigner 的免费替代方案。
开源的数据库建模工具比较少,以下是一些非开源的数据库建模工具(部分需要付费才能使用) :
- **[MySQL Workbench](https://www.mysql.com/products/workbench/)** : MySQL 官方为数据库架构师、开发人员和 DBA 提供的一个可视化工具。 MySQL Workbench 支持数据建模SQL 开发以及服务器配置、用户管理、性能优化、数据库备份以及迁移等功能,支持 Windows、Linux 和 Mac OS X 平台。
- **[Navicat Data Modeler](https://www.navicat.com.cn/products/navicat-data-modeler)** : 一款强大的和符合成本效益的数据库设计工具,它能帮助用户创建高质素的概念、逻辑和物理数据模型。让你可视化地设计数据库结构、执行逆向或正向工程程序、从 ODBC 数据源导入模型、生成复杂的 SQL/DDL 和打印模型到文件等。付费。
- **[DbSchema](https://dbschema.com/)** : 一款功能强大的数据库设计和管理的可视化工具,支持几乎所有的关系型和 NoSQL 数据库。付费。
- **[dbdiagram.io](https://dbdiagram.io/home)** : 是一款简单免费的在线 ER 图绘制工具,通过编写代码创建模型,专为开发人员和数据分析师而设计。它通过一个简单的自定义语言来生成数据模型,支持 MySQL、PostgreSQL、SQL Server 数据库 DDL 文件的正向工程和逆向工程、版本历史、在线共享、导出图片或者 PDF 等功能。dbdiagram.io 提供了免费版。
### 数据库管理
- **[Beekeeper Studio](https://github.com/beekeeper-studio/beekeeper-studio)** :跨平台数据库管理工具,颜值高,支持 SQLite、MySQL、MariaDB、Postgres、CockroachDB、SQL Server、Amazon Redshift。
- **[Sequel Pro](https://github.com/sequelpro/sequelpro)** :适用于 macOS 的 MySQL/MariaDB 数据库管理工具。
- **[DBeaver](https://github.com/dbeaver/dbeaver)** :一个基于 Java 开发 并且支持几乎所有的数据库产品的开源数据库管理工具。DBeaver 社区版不光支持关系型数据库比如 MySQL、PostgreSQL、MariaDB、SQLite、Oracle、Db2、SQL Server还比如 SQLite、H2 这些内嵌数据库。还支持常见的全文搜索引擎比如 Elasticsearch 和 Solr、大数据相关的工具比如 Hive 和 Spark。
- **[Kangaroo](https://gitee.com/dbkangaroo/kangaroo)** :袋鼠是一款为热门数据库系统打造的管理客户端(SQLite / MySQL / PostgreSQL / ...) ,支持建表、查询、模型、同步、导入导出等功能,支持 Windows / Mac / Linux 等操作系统,力求打造成好用、好玩、开发友好的 SQL 工具。
- **[Arctype](https://arctype.com/)** :一个桌面的数据库查询工具,可以连接各种数据库,在其中执行 SQL 语句,以可视化形式展示数据。
- **[Mongood](https://github.com/RenzHoly/Mongood)** : MongoDB 图形化的管理工具。基于微软 Fluent UI支持自动黑暗模式。
### Redis
- **[Another Redis Desktop Manager](https://github.com/qishibo/AnotherRedisDesktopManager/blob/master/README.zh-CN.md)** :更快、更好、更稳定的 Redis 桌面(GUI)管理客户端。
- **[Redis Manager](https://github.com/ngbdf/redis-manager)** Redis 一站式管理平台支持集群cluster、master-replica、sentinel的监控、安装除 sentinel、管理、告警以及基本的数据操作功能。
## Docker & Kubernetes 可视化管理
## Devops
- **[Portainer](https://github.com/portainer/portainer)** :可视化管理 Docker 和 Kubernetes。相关阅读[《吊炸天的 Docker 图形化工具 Portainer必须推荐给你](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247504221&idx=1&sn=85a3c69d64fba1b0d6d8485ab28ab4c4&chksm=cea19e96f9d617802920d5769bafc824b3b80afdfb6166a00532f0caa3b6f5bdac930e4e89de&token=693114125&lang=zh_CN#rd)。
## ZooKeeper 可视化管理
## ZooKeeper
- **[PrettyZoo](https://github.com/vran-dev/PrettyZoo)** 一个基于 Apache Curator 和 JavaFX 实现的 ZooKeeper 图形化管理客户端,颜值非常高,支持 Mac / Windows / Linux 。你可以使用 PrettyZoo 来实现对 ZooKeeper 的可视化增删改查。
- **[zktools](https://zktools.readthedocs.io/en/latest/#installing)** 一个低延迟的 ZooKeeper 图形化管理客户端,颜值非常高,支持 Mac / Windows / Linux 。你可以使用 zktools 来实现对 ZooKeeper 的可视化增删改查。
## Markdown
- **[MarkText](https://github.com/marktext/marktext)** :一个简单而优雅的开源 Markdown 编辑器专注于速度和可用性。Linux、macOS 和 Windows 均适用。

View File

@ -1,5 +1,5 @@
---
title: Netty常见面试题总结
title: Netty常见面试题总结(付费)
category: 框架
---

View File

@ -1,5 +1,5 @@
---
title: SpringBoot常见面试题总结
title: SpringBoot常见面试题总结(付费)
category: 框架
tag:
- Spring

View File

@ -6,13 +6,12 @@ tag:
---
> 本文授权转载自 https://ken.io/note/sso-design-implement 作者ken.io
>
## 一、前言
## SSO 介绍
### 1、SSO说明
### 什么是 SSO
SSO英文全称Single Sign On单点登录。SSO是在多个应用系统中用户只需要登录一次就可以访问所有相互信任的应用系统。https://baike.baidu.com/item/SSO/3451380
SSO 英文全称 Single Sign On单点登录。SSO 是在多个应用系统中用户只需要登录一次就可以访问所有相互信任的应用系统。https://baike.baidu.com/item/SSO/3451380
例如访问在网易账号中心https://reg.163.com/ )登录之后
访问以下站点都是登录状态
@ -21,17 +20,17 @@ SSO英文全称Single Sign On单点登录。SSO是在多个应用系统中
- 网易博客 [https://blog.163.com](https://blog.163.com/)
- 网易花田 [https://love.163.com](https://love.163.com/)
- 网易考拉 [https://www.kaola.com](https://www.kaola.com/)
- 网易Lofter [http://www.lofter.com](http://www.lofter.com/)
- 网易 Lofter [http://www.lofter.com](http://www.lofter.com/)
### 2、单点登录系统的好处
### SSO 有什么好处?
1. **用户角度** :用户能够做到一次登录多次使用,无需记录多套用户名和密码,省心。
2. **系统管理员角度** : 管理员只需维护好一个统一的账号中心就可以了,方便。
3. **新系统开发角度:** 新系统开发时只需直接对接统一的账号中心即可,简化开发流程,省时。
### 3、设计目标
## SSO 设计与实现
本篇文章也主要是为了探讨如何设计&实现一个SSO系统
本篇文章也主要是为了探讨如何设计&实现一个 SSO 系统
以下为需要实现的核心功能:
@ -40,88 +39,85 @@ SSO英文全称Single Sign On单点登录。SSO是在多个应用系统中
- 支持跨域单点登录
- 支持跨域单点登出
## 二、SSO设计与实现
### 核心应用与依赖
### 1、核心应用与依赖
![单点登录SSO设计](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/security/sso/sso-system.png-kblb.png)
![单点登录SSO设计](https://img.ken.io/blog/sso/sso-system.png-kblb.png)
| 应用/模块/对象 | 说明 |
| ----------------- | ----------------------------------- |
| 前台站点 | 需要登录的站点 |
| SSO 站点-登录 | 提供登录的页面 |
| SSO 站点-登出 | 提供注销登录的入口 |
| SSO 服务-登录 | 提供登录服务 |
| SSO 服务-登录状态 | 提供登录状态校验/登录信息查询的服务 |
| SSO 服务-登出 | 提供用户注销登录的服务 |
| 数据库 | 存储用户账户信息 |
| 缓存 | 存储用户的登录信息,通常使用 Redis |
| 应用/模块/对象 | 说明 |
| ---------------- | ----------------------------------- |
| 前台站点 | 需要登录的站点 |
| SSO站点-登录 | 提供登录的页面 |
| SSO站点-登出 | 提供注销登录的入口 |
| SSO服务-登录 | 提供登录服务 |
| SSO服务-登录状态 | 提供登录状态校验/登录信息查询的服务 |
| SSO服务-登出 | 提供用户注销登录的服务 |
| 数据库 | 存储用户账户信息 |
| 缓存 | 存储用户的登录信息通常使用Redis |
### 用户登录状态的存储与校验
### 2、用户登录状态的存储与校验
常见的 Web 框架对于 Session 的实现都是生成一个 SessionId 存储在浏览器 Cookie 中。然后将 Session 内容存储在服务器端内存中,这个 [ken.io](https://ken.io/) 在之前[Session 工作原理](https://ken.io/note/session-principle-skill)中也提到过。整体也是借鉴这个思路。
常见的Web框架对于[Session](https://ken.io/note/session-principle-skill)的实现都是生成一个SessionId存储在浏览器Cookie中。然后将Session内容存储在服务器端内存中这个 ken.io 在之前[Session工作原理](https://ken.io/note/session-principle-skill)中也提到过。整体也是借鉴这个思路。
用户登录成功之后生成AuthToken交给客户端保存。如果是浏览器就保存在Cookie中。如果是手机App就保存在App本地缓存中。本篇主要探讨基于Web站点的SSO。
用户在浏览需要登录的页面时客户端将AuthToken提交给SSO服务校验登录状态/获取用户登录信息
用户登录成功之后,生成 AuthToken 交给客户端保存。如果是浏览器,就保存在 Cookie 中。如果是手机 App 就保存在 App 本地缓存中。本篇主要探讨基于 Web 站点的 SSO。
对于登录信息的存储建议采用Redis使用Redis集群来存储登录信息既可以保证高可用又可以线性扩充。同时也可以让SSO服务满足负载均衡/可伸缩的需求。
用户在浏览需要登录的页面时,客户端将 AuthToken 提交给 SSO 服务校验登录状态/获取用户登录信息
对于登录信息的存储,建议采用 Redis使用 Redis 集群来存储登录信息,既可以保证高可用,又可以线性扩充。同时也可以让 SSO 服务满足负载均衡/可伸缩的需求。
| 对象 | 说明 |
| --------- | ------------------------------------------------------------ |
| AuthToken | 直接使用UUID/GUID即可如果有验证AuthToken合法性需求可以将UserName+时间戳加密生成,服务端解密之后验证合法性 |
| 登录信息 | 通常是将UserIdUserName缓存起来 |
| AuthToken | 直接使用 UUID/GUID 即可,如果有验证 AuthToken 合法性需求,可以将 UserName+时间戳加密生成,服务端解密之后验证合法性 |
| 登录信息 | 通常是将 UserIdUserName 缓存起来 |
### 3、用户登录/登录校验
### 用户登录/登录校验
- 登录时序图
**登录时序图**
![SSO系统设计-登录时序图](https://img.ken.io/blog/sso/sso-login-sequence.png-kbrb.png)
![SSO系统设计-登录时序图](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/security/sso/sso-login-sequence.png-kbrb.png)
按照上图用户登录后AuthToken保存在Cookie中。 domain=test.com
浏览器会将domain设置成 .test.com
这样访问所有*.test.com的web站点都会将AuthToken携带到服务器端。
然后通过SSO服务完成对用户状态的校验/用户登录信息的获取
按照上图,用户登录后 AuthToken 保存在 Cookie 中。 domain=test.com
浏览器会将 domain 设置成 .test.com
- 登录信息获取/登录状态校验
这样访问所有 \*.test.com 的 web 站点,都会将 AuthToken 携带到服务器端。
然后通过 SSO 服务,完成对用户状态的校验/用户登录信息的获取
![SSO系统设计-登录信息获取/登录状态校验](https://img.ken.io/blog/sso/sso-logincheck-sequence.png-kbrb.png)
**登录信息获取/登录状态校验**
### 4、用户登出
![SSO系统设计-登录信息获取/登录状态校验](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/security/sso/sso-logincheck-sequence.png-kbrb.png)
### 用户登出
用户登出时要做的事情很简单:
1. 服务端清除缓存Redis中的登录状态
2. 客户端清除存储的AuthToken
2. 客户端清除存储的 AuthToken
- 登出时序图
**登出时序图**
![SSO系统设计-用户登出](https://img.ken.io/blog/sso/sso-logout-sequence.png-kbrb.png)
![SSO系统设计-用户登出](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/security/sso/sso-logout-sequence.png-kbrb.png)
### 5、跨域登录、登出
### 跨域登录、登出
前面提到过核心思路是客户端存储AuthToken服务器端通过Redis存储登录信息。由于客户端是将AuthToken存储在Cookie中的。所以跨域要解决的问题就是如何解决Cookie的跨域读写问题。
前面提到过,核心思路是客户端存储 AuthToken服务器端通过 Redis 存储登录信息。由于客户端是将 AuthToken 存储在 Cookie 中的。所以跨域要解决的问题,就是如何解决 Cookie 的跨域读写问题。
解决跨域的核心思路就是:
- 登录完成之后通过回调的方式将AuthToken传递给主域名之外的站点该站点自行将AuthToken保存在当前域下的Cookie中。
- 登出完成之后通过回调的方式调用非主域名站点的登出页面完成设置Cookie中的AuthToken过期的操作。
- 跨域登录(主域名已登录)
- 登录完成之后通过回调的方式,将 AuthToken 传递给主域名之外的站点,该站点自行将 AuthToken 保存在当前域下的 Cookie 中。
- 登出完成之后通过回调的方式,调用非主域名站点的登出页面,完成设置 Cookie 中的 AuthToken 过期的操作。
![SSO系统设计-跨域登录(主域名已登录)](https://img.ken.io/blog/sso/sso-crossdomain-login-loggedin-sequence.png-kbrb.png)
**跨域登录(主域名已登录)**
- 跨域登录(主域名未登录)
![SSO系统设计-跨域登录(主域名已登录)](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/security/sso/sso-crossdomain-login-loggedin-sequence.png-kbrb.png)
![SSO系统设计-跨域登录(主域名未登录)](https://img.ken.io/blog/sso/sso-crossdomain-login-unlogin-sequence.png-kbrb.png)
**跨域登录(主域名未登录)**
- 跨域登出
![SSO系统设计-跨域登录(主域名未登录)](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/security/sso/sso-crossdomain-login-unlogin-sequence.png-kbrb.png)
![SSO系统设计-跨域登出](https://img.ken.io/blog/sso/sso-crossdomain-logout-sequence.png-kbrb.png)
**跨域登出**
## 三、备注
![SSO系统设计-跨域登出](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/security/sso/sso-crossdomain-logout-sequence.png-kbrb.png)
- 关于方案
## 说明
这次设计方案更多是提供实现思路。如果涉及到APP用户登录等情况在访问SSO服务时增加对APP的签名验证就好了。当然如果有无线网关验证签名不是问题。
- 关于时序图
时序图中并没有包含所有场景ken.io只列举了核心/主要场景,另外对于一些不影响理解思路的消息能省就省了。
- 关于方案 :这次设计方案更多是提供实现思路。如果涉及到 APP 用户登录等情况,在访问 SSO 服务时,增加对 APP 的签名验证就好了。当然,如果有无线网关,验证签名不是问题。
- 关于时序图:时序图中并没有包含所有场景,只列举了核心/主要场景,另外对于一些不影响理解思路的消息能省就省了。

View File

@ -1,5 +1,5 @@
---
title: 系统设计常见面试题总结
title: 系统设计常见面试题总结(付费)
category: Java面试指北
---

View File

@ -1,5 +1,5 @@
---
title: 《手写 RPC 框架》
title: 《手写 RPC 框架》(付费)
category: 知识星球
---
@ -39,6 +39,10 @@ category: 知识星球
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/IMG_3007.jpg)
或者你也可以直接使用下面这张 **20** 元的优惠券,**139/年** 加入。
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/xingqiu/xingqiuyouhuijuan-20.png)
进入星球之后,你可以为自己制定一个目标,比如自己想要进入某某还不错的公司或者达成什么成就(一定要是还算有点挑战的目标)。待你完成目标在星球分享之后,我会将星球的门票费退还给你。
**真诚欢迎准备面试的小伙伴加入星球一起交流!真心希望能够帮助到更多小伙伴!**

View File

@ -1,5 +1,5 @@
---
title: 《Java 面试指北》
title: 《Java 面试指北》(付费)
category: 知识星球
star: 5
---
@ -88,6 +88,10 @@ star: 5
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/IMG_3007.jpg)
或者你也可以直接使用下面这张 **20** 元的优惠券,**139/年** 加入。
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/xingqiu/xingqiuyouhuijuan-20.png)
进入星球之后,你可以为自己制定一个目标,比如自己想要进入某某还不错的公司或者达成什么成就(一定要是还算有点挑战的目标)。待你完成目标在星球分享之后,我会将星球的门票费退还给你。
**真诚欢迎准备面试的小伙伴加入星球一起交流!真心希望能够帮助到更多小伙伴!**

View File

@ -38,6 +38,10 @@ category: 知识星球
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/IMG_3007.jpg)
或者你也可以直接使用下面这张 **20** 元的优惠券,**139/年** 加入。
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/xingqiu/xingqiuyouhuijuan-20.png)
进入星球之后,你可以为自己制定一个目标,比如自己想要进入某某还不错的公司或者达成什么成就(一定要是还算有点挑战的目标)。待你完成目标在星球分享之后,我会将星球的门票费退还给你。
**真诚欢迎准备面试的小伙伴加入星球一起交流!真心希望能够帮助到更多小伙伴!**

View File

@ -1,5 +1,5 @@
---
title: 《Java 必读源码系列》
title: 《Java 必读源码系列》(付费)
category: 知识星球
star: true
---
@ -46,6 +46,10 @@ star: true
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/IMG_3007.jpg)
或者你也可以直接使用下面这张 **20** 元的优惠券,**139/年** 加入。
![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/xingqiu/xingqiuyouhuijuan-20.png)
进入星球之后,你可以为自己制定一个目标,比如自己想要进入某某还不错的公司或者达成什么成就(一定要是还算有点挑战的目标)。待你完成目标在星球分享之后,我会将星球的门票费退还给你。
**真诚欢迎准备面试的小伙伴加入星球一起交流!真心希望能够帮助到更多小伙伴!**