[
  {
    "path": "Kafka.md",
    "content": "# **Kafka知识总结**\r\n\r\n## **一、讲讲acks参数对消息持久化的影响**\r\n\r\n### **目录**\r\n\r\n1. 写在前面\r\n2. 如何保证宕机时数据不丢失？\r\n3. 多副本之间数据如何同步？\r\n4. ISR到底指的是什么东西？\r\n5. acks参数的含义？\r\n6. 最后的思考\r\n\r\n### **1.写在前面**\r\n\r\n面试大厂时，一旦简历上写了Kafka，几乎必然会被问到一个问题：说说acks参数对消息持久化的影响？\r\n\r\n这个acks参数在kafka的使用中，是非常核心以及关键的一个参数，决定了很多东西。\r\n\r\n所以无论是为了面试还是实际项目使用，大家都值得看一下这篇文章对Kafka的acks参数的分析，以及背后的原理。\r\n\r\n### **2.如何保证宕机的时候数据不丢失？（或者kafka如何保证高可用、或者Kafka如何保证高可用）**\r\n\r\n- Kafka 一个最基本的架构认识：由多个 broker 组成，每个 broker 是一个节点；创建一个 topic，这个 topic 可以划分为多个 partition，每个 partition 可以存在于不同的 broker 上，每个 partition 就放一部分数据。\r\n\r\n  这就是**天然的分布式消息队列**，就是说一个 topic 的数据，是**分散放在多个机器上的，每个机器就放一部分数据**。\r\n\r\n- 而且Kafka还提供replica**副本机制**，每个partition的数据都会同步到其他机器上，形成自己的多个replica副本。所有replica会选举出来一个leader出来，那么**生产和消费都跟这个leader打交道**，然后其他replica就是follower。写的时候，leader会负责把数据同步到所有follower上去，读的时候就直接读leader上的数据即可。\r\n\r\n如果某个broker宕机了，那个broker上的partition在其他机器上都有副本。如果这个宕机的broker上面有某个partition的leader，那么从follower中重新选举一个新的leader出来，然后继续读写新的leader即可，这就是所谓的高可用。\r\n\r\n![](<https://github.com/XU-ZHOU/Java/blob/master/pictures/1.jpg>)\r\n\r\n### 3.**多副本之间数据如何保证同步**\r\n\r\n其实任何一个Partition，只有Leader是对外提供读写服务的，也就是说，如果有一个客户端往一个Partition写入数据，此时一般就是写入这个Partition的Leader副本。\r\n\r\n然后Leader副本接收到数据之后，Follower副本会不停的给他发送请求尝试去拉取最新的数据，拉取到自己本地后，写入磁盘中。如下图所示：\r\n\r\n![](<https://github.com/XU-ZHOU/Java/blob/master/pictures/2.jpg>)\r\n\r\n### **4.ISR到底指的是什么东西？**\r\n\r\nISR全称是“In-Sync Replicas”，也就是**保持同步的副本**，他的含义就是，跟Leader始终保持同步的Follower有哪些。\r\n\r\n大家可以想一下 ，如果说某个Follower所在的Broker因为JVM FullGC之类的问题，导致自己卡顿了，无法及时从Leader拉取同步数据，那么是不是会导致Follower的数据比Leader要落后很多？\r\n\r\n所以这个时候，就意味着Follower已经跟Leader不再处于同步的关系了。但是只要Follower一直及时从Leader同步数据，就可以保证他们是处于同步的关系的。\r\n\r\n所以每个Partition都有一个ISR，这个ISR里一定会有Leader自己，因为Leader肯定数据是最新的，然后就是那些跟Leader保持同步的Follower，也会在ISR里。\r\n\r\n### **5.acks参数的含义**\r\n\r\n首先这个acks参数，是在KafkaProducer，也就是生产者客户端里设置的\r\n\r\n也就是说，你往kafka写数据的时候，就可以来设置这个acks参数。然后这个参数实际上有三种常见的值可以设置，分别是：**0、1 和 all**。\r\n\r\n**第一种选择是把acks参数设置为0**，意思就是我的KafkaProducer在客户端，只要把消息发送出去，不管那条数据有没有在哪怕Partition Leader上落到磁盘，我就不管他了，直接就认为这个消息发送成功了。\r\n\r\n如果你采用这种设置的话，那么你必须注意的一点是，可能你发送出去的消息还在半路。结果呢，Partition Leader所在Broker就直接挂了，然后结果你的客户端还认为消息发送成功了，此时就会**导致这条消息就丢失了**。\r\n\r\n![](<https://github.com/XU-ZHOU/Java/blob/master/pictures/3.jpg>)\r\n\r\n**第二种选择是设置 acks = 1**，意思就是说只要Partition Leader接收到消息而且写入本地磁盘了，就认为成功了，不管他其他的Follower有没有同步过去这条消息了。\r\n\r\n这种设置其实是**kafka默认的设置**\r\n\r\n也就是说，默认情况下，你要是不管acks这个参数，只要Partition Leader写成功就算成功。\r\n\r\n但是这里有一个问题，万一Partition Leader刚刚接收到消息，Follower还没来得及同步过去，结果Leader所在的broker宕机了，此时也会导致这条消息丢失，因为人家客户端已经认为发送成功了。\r\n\r\n![](<https://github.com/XU-ZHOU/Java/blob/master/pictures/4.jpg>)\r\n\r\n**最后一种情况，就是设置acks=all**，这个意思就是说，**Partition Leader接收到消息之后，还必须要求ISR列表里跟Leader保持同步的那些Follower都要把消息同步过去**，才能认为这条消息是写入成功了。\r\n\r\n如果说Partition Leader刚接收到了消息，但是结果Follower没有收到消息，此时Leader宕机了，那么客户端会感知到这个消息没发送成功，他会重试再次发送消息过去。\r\n\r\n此时可能Partition 2的Follower变成Leader了，此时ISR列表里只有最新的这个Follower转变成的Leader了，那么只要这个新的Leader接收消息就算成功了。\r\n\r\n![](<https://github.com/XU-ZHOU/Java/blob/master/pictures/5.jpg>)\r\n\r\n### **6.最后的思考**\r\n\r\nacks=all 就可以代表数据一定不会丢失了吗？\r\n\r\n当然不是，如果你的Partition只有一个副本，也就是一个Leader，任何Follower都没有，你认为acks=all有用吗？\r\n\r\n当然没用了，因为ISR里就一个Leader，他接收完消息后宕机，也会导致数据丢失。\r\n\r\n所以说，**这个acks=all，必须跟ISR列表里至少有2个以上的副本配合使用**，起码是有一个Leader和一个Follower才可以。\r\n\r\n这样才能保证说写一条数据过去，一定是2个以上的副本都收到了才算是成功，此时任何一个副本宕机，不会导致数据丢失。\r\n\r\n**参考**：https://mp.weixin.qq.com/s/IxS46JAr7D9sBtCDr8pd7A\r\n\r\n## 二、Kafka参数调优实战\r\n\r\n### 目录\r\n\r\n1. 背景引入：很多同学看不懂的Kafka参数\r\n2. 一段Kafka生产端的示例代码\r\n3. 内存缓冲的大小\r\n4. 多少数据打包为一个Batch合适？\r\n5. 要是一个Batch迟迟无法凑满怎么办？\r\n6. 最大请求大小\r\n7. 重试机制\r\n8. 持久化机制\r\n\r\n#### 1、背景引入：很多同学看不懂的kafka参数\r\n\r\n在使用Kafka的客户端编写代码与服务器交互的时候，是需要对客户端设置很多的参数的。\r\n\r\n#### 2、一段Kafka生产端的示例代码\r\n\r\n```scala\r\nProperties props = new Properties();\r\nprops.put(\"bootstrap.servers\", \"localhost:9092\"); \r\nprops.put(\"key.serializer\", \"org.apache.kafka.common.serialization.StringSerializer\");\r\nprops.put(\"value.serializer\", \"org.apache.kafka.common.serialization.StringSerializer\");\r\nprops.put(\"buffer.memory\", 67108864); \r\nprops.put(\"batch.size\", 131072); \r\nprops.put(\"linger.ms\", 100); \r\nprops.put(\"max.request.size\", 10485760); \r\nprops.put(\"acks\", \"1\"); \r\nprops.put(\"retries\", 10); \r\nprops.put(\"retry.backoff.ms\", 500);\r\n\r\nKafkaProducer<String, String> producer = new KafkaProducer<String, String>(props);\r\n```\r\n\r\n#### 3、内存缓冲的大小\r\n\r\n首先看看“**buffer.memory**”这个参数是什么意思？\r\n\r\nKafka的客户端发送数据到服务器，一般都是要经过**缓冲**的，也就是说，**通过KafkaProducer发送出去的消息都是先进入到客户端本地的内存缓冲里，然后把很多消息收集成一个一个的Batch，再发送到Broker上去的**。\r\n\r\n![](<https://github.com/XU-ZHOU/Java/blob/master/pictures/6.jpg>)\r\n\r\n所以这个“**buffer.memory”的本质就是用来约束KafkaProducer能够使用的内存缓冲的大小的，他的默认值是32MB**。\r\n\r\n你可以先想一下，如果这个内存缓冲设置的过小的话，可能会导致一个什么问题？\r\n\r\n首先要明确一点，那就是在内存缓冲里大量的消息会缓冲在里面，形成一个一个的Batch，每个Batch里包含多条消息。\r\n\r\n然后KafkaProducer有一个Sender线程会把多个Batch打包成一个Request发送到Kafka服务器上去。\r\n\r\n![](<https://github.com/XU-ZHOU/Java/blob/master/pictures/7.jpg>)\r\n\r\n那么如果要是**内存设置的太小**，可能**导致一个问题**：消息快速的写入内存缓冲里面，但是Sender线程来不及把Request发送到Kafka服务器。\r\n\r\n这样是不是会造成内存缓冲很快就被写满？一旦被写满，就会阻塞用户线程，不让继续往Kafka写消息了。\r\n\r\n所以对于“buffer.memory”这个参数应该结合自己的实际情况来进行压测，你需要测算一下在生产环境，你的用户线程会以每秒多少消息的频率来写入内存缓冲。\r\n\r\n比如说每秒300条消息，那么你就需要压测一下，假设内存缓冲就32MB，每秒写300条消息到内存缓冲，是否会经常把内存缓冲写满？经过这样的压测，你可以调试出来一个合理的内存大小。\r\n\r\n#### 4、多少数据打包为一个Batch合适？\r\n\r\n接着你需要思考第二个问题，就是你的“**batch.size**”应该如何设置？**这决定了你的每个Batch要存放多少数据就可以发送出去了**。\r\n\r\n比如说你要是给一个Batch设置成是16KB的大小，那么里面凑够16KB的数据就可以发送了。\r\n\r\n这个**参数的默认值是16KB**，一般可以尝试把这个参数调节大一些，然后利用自己的生产环境发消息的负载来测试一下。\r\n\r\n比如说发送消息的频率就是每秒300条，那么如果比如“batch.size”调节到了32KB，或者64KB，是否可以提升发送消息的整体吞吐量。\r\n\r\n因为理论上来说，提升batch的大小，可以允许更多的数据缓冲在里面，那么一次Request发送出去的数据量就更多了，这样吞吐量可能会有所提升。\r\n\r\n但是**不能无限的大**，过于大了之后，要是数据老是缓冲在Batch里迟迟不发送出去，那么岂不是你发送消息的延迟就会很高，**导致高延迟问题**。\r\n\r\n比如说，一条消息进入了Batch，但是要等待5秒钟Batch才凑满了64KB，才能发送出去。那这条消息的延迟就是5秒钟。\r\n\r\n所以需要在这里按照生产环境的发消息的速率，调节不同的Batch大小自己测试一下最终出去的吞吐量以及消息的 延迟，设置一个最合理的参数。\r\n\r\n#### 5、要是一个Batch迟迟无法凑满怎么办？\r\n\r\n要是一个Batch迟迟无法凑满，此时就需要引入另外一个参数了，“**linger.ms**”\r\n\r\n**含义是一个Batch被创建之后，最多过多久，不管这个Batch有没有写满，都必须发送出去了**。\r\n\r\n给大家举个例子，比如说batch.size是16kb，但是现在某个低峰时间段，发送消息很慢。\r\n\r\n这就导致可能Batch被创建之后，陆陆续续有消息进来，但是迟迟无法凑够16KB，难道此时就一直等着吗？\r\n\r\n当然不是，假设你现在设置“linger.ms”是50ms，那么只要这个Batch从创建开始到现在已经过了50ms了，哪怕他还没满16KB，也要发送他出去了。\r\n\r\n所以“linger.ms”决定了你的消息一旦写入一个Batch，最多等待这么多时间，他一定会跟着Batch一起发送出去。\r\n\r\n避免一个Batch迟迟凑不满，导致消息一直积压在内存里发送不出去的情况。**这是一个很关键的参数。**\r\n\r\n这个参数一般要非常慎重的来设置，要配合batch.size一起来设置。\r\n\r\n举个例子，首先假设你的Batch是32KB，那么你得估算一下，正常情况下，一般多久会凑够一个Batch，比如正常来说可能20ms就会凑够一个Batch。\r\n\r\n那么你的linger.ms就可以设置为25ms，也就是说，正常来说，大部分的Batch在20ms内都会凑满，但是你的linger.ms可以保证，哪怕遇到低峰时期，20ms凑不满一个Batch，还是会在25ms之后强制Batch发送出去。\r\n\r\n如果要是你把linger.ms设置的太小了，比如说默认就是0ms，或者你设置个5ms，那可能导致你的Batch虽然设置了32KB，但是经常是还没凑够32KB的数据，5ms之后就直接强制Batch发送出去，这样也不太好其实，会导致你的Batch形同虚设，一直凑不满数据。\r\n\r\n#### 6、最大请求大小\r\n\r\n**“max.request.size”这个参数决定了每次发送给Kafka服务器请求的最大大小**，同时也会限制你一条消息的最大大小也不能超过这个参数设置的值，这个其实可以根据你自己的消息的大小来灵活的调整。\r\n\r\n给大家举个例子，你们公司发送的消息都是那种大的报文消息，每条消息都是很多的数据，一条消息可能都要20KB。\r\n\r\n此时你的batch.size是不是就需要调节大一些？比如设置个512KB？然后你的buffer.memory是不是要给的大一些？比如设置个128MB？\r\n\r\n只有这样，才能让你在大消息的场景下，还能使用Batch打包多条消息的机制。但是此时“max.request.size”是不是也得同步增加？\r\n\r\n因为可能你的一个请求是很大的，默认他是1MB，你是不是可以适当调大一些，比如调节到5MB？\r\n\r\n#### 7、重试机制\r\n\r\n**“retries”和“retries.backoff.ms”决定了重试机制，也就是如果一个请求失败了可以重试几次，每次重试的间隔是多少毫秒**。\r\n\r\n这个大家适当设置几次重试的机会，给一定的重试间隔即可，比如给100ms的重试间隔。\r\n\r\n#### 8、持久化机制\r\n\r\n“acks”参数决定了发送出去的消息要采用什么样的持久化策略，这个涉及到了很多其他的概念，大家可以参考之前专门为“acks”写过的一篇文章。\r\n\r\n**参考**：[](https://mp.weixin.qq.com/s/YLrGg-jx5ddmHECmdccppw)\r\n\r\n## 三、消息中间件消费到的消息处理失败怎么办？\r\n\r\n消息中间件最核心的作用是：解耦、异步、削峰。\r\n\r\n假如有如下的系统：\r\n\r\n![](<https://github.com/XU-ZHOU/Java/blob/master/pictures/8.jpg>)\r\n\r\n生产中存在这种情况：如果独立仓库系统或者第三方物流系统故障了，导致仓储系统消费到一条订单消息之后，尝试进行发货失败，也就是对这条消费到的消息处理失败。这种情况，怎么处理？\r\n\r\n#### 死信队列的使用：处理失败的消息\r\n\r\n一般生产环境中，如果你有丰富的架构设计经验，都会在使用MQ的时候设计两个队列：一个是**核心业务队列**，一个是**死信队列**。\r\n\r\n核心业务队列，就是比如上面专门用来让订单系统发送订单消息的，然后另外一个死信队列就是用来处理异常情况的。\r\n\r\n面试被问到这个问题时，必须要结合你自己的业务实践经验来说。\r\n\r\n比如说要是第三方物流系统故障了，此时无法请求，那么仓储系统每次消费到一条订单消息，尝试通知发货和配送，都会遇到对方的接口报错。\r\n\r\n此时仓储系统就可以把这条消息拒绝访问，或者标志位处理失败！**注意，这个步骤很重要。**\r\n\r\n一旦标志这条消息处理失败了之后，MQ就会把这条消息转入提前设置好的一个死信队列中。\r\n\r\n然后你会看到的就是，在第三方物流系统故障期间，所有订单消息全部处理失败，全部会转入死信队列。\r\n\r\n然后你的仓储系统得专门有一个后台线程，监控第三方物流系统是否正常，能否请求的，不停的监视。\r\n\r\n一旦发现对方恢复正常，这个后台线程就从死信队列消费出来处理失败的订单，重新执行发货和配送的通知逻辑。\r\n\r\n**死信队列的使用，其实就是MQ在生产实践中非常重要的一环，也就是架构设计必须要考虑的**。\r\n\r\n整个过程，如下图所示：\r\n\r\n![](<https://github.com/XU-ZHOU/Java/blob/master/pictures/9.jpg>)\r\n\r\n## 四、Kafka选举\r\n\r\nKafka中的选举大致可以分为三大类：\r\n\r\n- 控制器的选举\r\n- 分区leader的选举\r\n- 消费者相关的选举\r\n\r\n#### 1、控制器选举\r\n\r\n在Kafka集群中会有一个或多个broker，其中有一个broker会被选举为控制器（Kafka Controller），它负责管理整个集群中所有分区和副本的状态等工作。\r\n\r\n比如**当某个分区的leader副本出现故障时，由控制器负责为该分区选举新的leader副本**。再比如当检测到某个分区的ISR集合发生变化时，由控制器负责通知所有broker更新其元数据信息。\r\n\r\nKafka Controller的选举是依赖Zookeeper来实现的，在Kafka集群中那个broker能够成功创建/controller这个临时（Ephemeral）节点他就可以成为Kafka Controller。\r\n\r\n这里需要说明一下的是Kafka Controller的实现还是相当复杂的，涉及到各个方面的内容，如果你掌握了Kafka Controller，你就掌握了Kafka的“半壁江山”。\r\n\r\n#### 2、分区leader的选举\r\n\r\n分区leader副本的选举**由Kafka Controller 负责具体实施**。\r\n\r\n当创建分区（创建主题或增加分区都有创建分区的动作）或分区上线（比如分区中原先的leader副本下线，此时分区需要选举一个新的leader上线来对外提供服务）的时候都需要执行leader的选举动作。\r\n\r\n基本思路是按照AR集合中副本的顺序查找第一个存活的副本，并且这个副本在ISR集合中。\r\n\r\n一个分区的AR集合在分配的时候就被指定，并且只要不发生重分配的情况，集合内部副本的顺序是保持不变的，而分区的ISR集合中副本的顺序可能会改变。\r\n\r\n注意：这里是根据AR的顺序而不是ISR的顺序进行选举的。这个说起来比较抽象，有兴趣的读者可以手动关闭/开启某个集群中的broker来观察一下具体的变化。\r\n\r\n还有一些情况也会发生分区leader的选举，比如当分区进行重分配（reassign）的时候也需要执行leader的选举动作。\r\n\r\n这个思路比较简单：从重分配的AR列表中找到第一个存活的副本，且这个副本在目前的ISR列表中。\r\n\r\n再比如当发生优先副本（preferred replica partition leader election）的选举时，直接将优先副本设置为leader即可，AR集合中的第一个副本即为优先副本。\r\n\r\n还有一种情况就是当某节点被优雅地关闭（也就是执行ControlledShutdown）时，位于这个节点上的leader副本都会下线，所以与此对应的分区需要执行leader的选举。\r\n\r\n这里的具体思路为：从AR列表中找到第一个存活的副本，且这个副本在目前的ISR列表中，与此同时还要确保这个副本不处于正在被关闭的节点上。\r\n\r\n#### 3、消费者相关的选择\r\n\r\n组协调器GroupCoordinator需要为消费组内的消费者选举出一个消费组的leader，这个选举的算法也很简单，分两种情况分析。\r\n\r\n- **如果消费组内还没有leader，那么第一个加入消费组的消费者即为消费组的leader**。\r\n\r\n- **如果某一时刻leader消费者由于某些原因退出了消费组，那么会重新选举一个新的leader，这个重新选举leader的过程又更“随意”了，相关代码如下**：\r\n\r\n```scala\r\n//scala code.\r\nprivate val members = new mutable.HashMap[String, MemberMetadata]\r\nvar leaderId = members.keys.head\r\n```\r\n\r\n解释一下这2行代码：在GroupCoordinator中消费者的信息是以HashMap的形式存储的，其中key为消费者的member_id，而value是消费者相关的元数据信息。\r\n\r\nleaderId表示leader消费者的member_id，它的取值为HashMap中的第一个键值对的key，这种选举的方式基本上和随机无异。\r\n\r\n总体上来说，消费组的leader选举过程是很随意的。\r\n\r\n到这里就结束了吗？还有分区分配策略的选举呢。\r\n\r\n或许你对此有点陌生，但是用过Kafka的同学或许对partition.assignment.strategy（取值为RangeAssignor、RoundRobinAssignor、StickyAssignor等）这个参数并不陌生。\r\n\r\n每个消费者都可以设置自己的分区分配策略，对消费组而言需要从各个消费者呈报上来的各个分配策略中选举一个彼此都“信服”的策略来进行整体上的分区分配。\r\n\r\n这个分区分配的选举并非由leader消费者决定，而是根据消费组内的各个消费者投票来决定的。\r\n\r\n**参考**：[](https://mp.weixin.qq.com/s/XvDpq1xxXPzRoRKMO-MxeQ)\r\n\r\n## 五、如何保证消息不被重复消费？（如何保证消息消费的幂等性）\r\n\r\n### 面试官心理分析\r\n\r\n其实这是很常见的一个问题，这俩问题基本可以连起来问。既然是消费消息，那肯定要考虑会不会重复消费？能不能避免重复消费？或者重复消费了也别造成系统异常可以吗？这个是 MQ 领域的基本问题，其实本质上还是问你**使用消息队列如何保证幂等性**，这个是你架构里要考虑的一个问题。\r\n\r\n### 面试题剖析\r\n\r\n回答这个问题，首先大概说一说可能会有哪些重复消费的问题。\r\n\r\n首先，比如 RabbitMQ、RocketMQ、Kafka，都有可能会出现消息重复消费的问题，挑 Kafka 来举个例子，说说怎么重复消费吧。\r\n\r\nKafka 实际上有个 offset 的概念，就是每个消息写进去，都有一个 offset，代表消息的序号，然后 consumer 消费了数据之后，**每隔一段时间**（**定时定期**），会把自己消费过的消息的 offset 提交一下，表示“我已经消费过了，下次我要是重启啥的，你就让我继续从上次消费到的 offset 来继续消费吧”。\r\n\r\n但是，你有时候重启系统，看你怎么重启了，如果碰到点着急的，直接 kill 进程了，再重启。这会导致 consumer 有些消息处理了，但是**没来得及提交 offset，重启之后，少数消息会再次消费一次**。\r\n\r\n例如，数据 1/2/3 依次进入 kafka，kafka 会给这三条数据每条分配一个 offset，代表这条数据的序号，我们就假设分配的 offset 依次是 152/153/154。消费者从 kafka 去消费的时候，也是按照这个顺序去消费。假如当消费者消费了 `offset=153` 的这条数据，刚准备去提交 offset 到 zookeeper，此时消费者进程被重启了。那么此时消费过的数据 1/2 的 offset 并没有提交，kafka 也就不知道你已经消费了 `offset=153` 这条数据。那么重启之后，消费者会找 kafka 说，嘿，哥儿们，你给我接着把上次我消费到的那个地方后面的数据继续给我传递过来。由于之前的 offset 没有提交成功，那么数据 1/2 会再次传过来，如果此时消费者没有去重的话，那么就会导致重复消费。\r\n\r\n![](<https://github.com/XU-ZHOU/Java/blob/master/pictures/10.png>)\r\n\r\n**如何保证消息队列消费的幂等性**？\r\n\r\n回答这个问题需要结合业务思考，有如下几个思路：\r\n\r\n- 比如数据要写库，先根据主键查一下，如果这数据都有了，就别插入了，update 一下。\r\n- 比如是写 Redis，那没问题了，因为每次都是 set，天然幂等性。\r\n- 比如不是上面两个场景，那做的稍微复杂一点，你需要让生产者发送每条数据的时候，里面**加一个全局唯一的 id**，类似订单 id 之类的东西，然后你这里消费到了之后，先根据这个 id 去比如 Redis 里查一下，之前消费过吗？如果没有消费过，你就处理，然后这个 id 写 Redis。如果消费过了，就别处理，保证别重复处理相同的消息即可。\r\n- 比如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了，重复数据插入只会报错，不会导致数据库中出现脏数据。\r\n\r\n![](<https://github.com/XU-ZHOU/Java/blob/master/pictures/11.png>)\r\n\r\n## 六、如何保证消息的可靠性传输？（如何处理消息丢失的问题？）\r\n\r\n## 面试官心理分析\r\n\r\n这个是肯定的，用 MQ 有个基本原则，就是**数据不能多一条，也不能少一条**，不能多，就是前面说的[重复消费和幂等性问题。不能少，就是说这数据别搞丢了。那这个问题你必须得考虑一下。\r\n\r\n如果说你这个是用 MQ 来传递非常核心的消息，比如说计费、扣费的一些消息，那必须确保这个 MQ 传递过程中**绝对不会把计费消息给弄丢**。\r\n\r\n## 面试题剖析\r\n\r\n数据的丢失问题，可能出现在**生产者、MQ、消费者**中，从 Kafka 来分析一下。\r\n\r\n### Kafka\r\n\r\n### 1、消费者丢失数据\r\n\r\n唯一可能导致消费者弄丢数据的情况，是消费到了这个消息，然后消费者那边**自动提交了 offset**，让 Kafka 以为你已经消费好了这个消息，但其实你才刚准备处理这个消息，你还没处理，你自己就挂了，此时这条消息就丢咯。\r\n\r\n由于 Kafka 会自动提交 offset，那么只要**关闭自动提交** offset，在处理完之后自己手动提交 offset，就可以保证数据不会丢。但是此时确实还是**可能会有重复消费**，比如你刚处理完，还没提交 offset，结果自己挂了，此时肯定会重复消费一次，自己保证幂等性就好了。\r\n\r\n生产环境碰到的一个问题是Kafka 消费者消费到了数据之后是写到一个内存的 queue 里先缓冲一下，结果有的时候，你刚把消息写入内存 queue，然后消费者会自动提交 offset。然后此时我们重启了系统，就会导致内存 queue 里还没来得及处理的数据就丢失了。\r\n\r\n### 2、Kafka弄丢数据\r\n\r\n这块比较常见的一个场景，就是 Kafka 某个 broker 宕机，然后重新选举 partition 的 leader。如果此时其他的 follower 刚好还有些数据没有同步，结果此时 leader 挂了，然后选举某个 follower 成 leader 之后，不就少了一些数据？这就丢了一些数据啊。\r\n\r\n所以此时一般是要求起码设置如下 4 个参数：\r\n\r\n- 给 topic 设置 `replication.factor` 参数：这个值必须大于 1，要求每个 partition 必须有至少 2 个副本。\r\n- 在 Kafka 服务端设置 `min.insync.replicas` 参数：这个值必须大于 1，这个是要求一个 leader 至少感知到有至少一个 follower 还跟自己保持联系，没掉队，这样才能确保 leader 挂了还有一个 follower 吧。\r\n- 在 producer 端设置 `acks=all`：这个是要求每条数据，必须是**写入所有 replica 之后，才能认为是写成功了**。\r\n- 在 producer 端设置 `retries=MAX`（很大很大很大的一个值，无限次重试的意思）：这个是**要求一旦写入失败，就无限重试**，卡在这里了。\r\n\r\n我们生产环境就是按照上述要求配置的，这样配置之后，至少在 Kafka broker 端就可以保证在 leader 所在 broker 发生故障，进行 leader 切换时，数据不会丢失。\r\n\r\n### 3、生产者会不会弄丢数据？\r\n\r\n如果按照上述的思路设置了 `acks=all`，一定不会丢，要求是，你的 leader 接收到消息，所有的 follower 都同步到了消息之后，才认为本次写成功了。如果没满足这个条件，生产者会自动不断的重试，重试无限次。\r\n\r\n## 七、如何保证消息的顺序性？\r\n\r\nKafka：比如说我们建了一个 topic，有三个 partition。生产者在写的时候，其实可以指定一个 key，比如说我们指定了某个订单 id 作为 key，那么这个订单相关的数据，一定会被分发到同一个 partition 中去，而且这个 partition 中的数据一定是有顺序的。\r\n消费者从 partition 中取出来数据的时候，也一定是有顺序的。到这里，顺序还是 ok 的，没有错乱。接着，我们在消费者里可能会搞**多个线程来并发处理消息**。因为如果消费者是单线程消费处理，而处理比较耗时的话，比如处理一条消息耗时几十 ms，那么 1 秒钟只能处理几十条消息，这吞吐量太低了。而多个线程并发跑的话，顺序可能就乱掉了。\r\n\r\n![](<https://github.com/XU-ZHOU/Java/blob/master/pictures/12.png>)\r\n\r\n#### 解决方案：\r\n\r\n- 一个 topic，一个 partition，一个 consumer，内部单线程消费，单线程吞吐量太低，一般不会用这个。\r\n- 写 N 个**内存 queue**，具有相同 key 的数据都到同一个内存 queue；然后对于 N 个线程，每个线程分别消费一个内存 queue 即可，这样就能保证顺序性。\r\n\r\n![](<https://github.com/XU-ZHOU/Java/blob/master/pictures/13.png>)\r\n\r\n\r\n\r\n"
  },
  {
    "path": "pictures/1.txt",
    "content": "as"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/ABADemo.java",
    "content": "import java.util.concurrent.TimeUnit;\r\nimport java.util.concurrent.atomic.AtomicReference;\r\nimport java.util.concurrent.atomic.AtomicStampedReference;\r\n\r\n/*\r\n* ABA问题的解决  AtomicStampedReference\r\n* */\r\npublic class ABADemo {\r\n    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);\r\n    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);\r\n\r\n\r\n    public static void main(String[] args){\r\n        new Thread(()->{\r\n            atomicReference.compareAndSet(100,101);\r\n            atomicReference.compareAndSet(101,100);\r\n        },\"t1\").start();\r\n\r\n        new Thread(()->{\r\n//            暂停1秒钟线程2，保证上面t1线程完成一次ABA操作\r\n            try{ TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {e.printStackTrace();}\r\n            System.out.println(atomicReference.compareAndSet(100,2019)+\"\\t\"+atomicReference.get());\r\n        },\"t2\").start();\r\n\r\n        try{TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}\r\n\r\n        System.out.println(\"======以下是ABA问题的解决=====\");\r\n        new Thread(()->{\r\n            int stamp = atomicStampedReference.getStamp();\r\n            System.out.println(Thread.currentThread().getName()+\"\\t第1次版本号：\"+stamp);\r\n\r\n//            暂停1秒钟t3线程\r\n            try{TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}\r\n            atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);\r\n            System.out.println(Thread.currentThread().getName()+\"\\t第2次版本号：\"+atomicStampedReference.getStamp());\r\n            atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);\r\n            System.out.println(Thread.currentThread().getName()+\"\\t第3次版本号：\"+atomicStampedReference.getStamp());\r\n        },\"t3\").start();\r\n\r\n        new Thread(()->{\r\n            int stamp = atomicStampedReference.getStamp();\r\n            System.out.println(Thread.currentThread().getName()+\"\\t第1次版本号：\"+stamp);\r\n\r\n//            暂停1秒钟t4线程\r\n            try{TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}\r\n            boolean result = atomicStampedReference.compareAndSet(100,2019,stamp,atomicStampedReference.getStamp()+1);\r\n\r\n            System.out.println(Thread.currentThread().getName()+\"\\t修改成功否： \"+result+\"\\t当前最新实际版本号：\"+atomicStampedReference.getStamp());\r\n\r\n        },\"t4\").start();\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/AtomicReferenceDemo.java",
    "content": "import javax.jws.soap.SOAPBinding;\r\nimport java.util.concurrent.atomic.AtomicInteger;\r\nimport java.util.concurrent.atomic.AtomicReference;\r\n\r\nclass User{\r\n    String userName;\r\n    int age;\r\n}\r\npublic class AtomicReferenceDemo {\r\n    public static void main(String[] args){\r\n        AtomicReference<User> atomicReference = new AtomicReference<>();\r\n\r\n//        User z3 = new User(\"z3\",22);\r\n//        User li4 = new User(\"li4\",25);\r\n\r\n//        atomicReference.set(z3);\r\n//        System.out.println(atomicReference.compareAndSet(z3,li4)+\"\\t\"+atomicReference.get().toString());\r\n\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/BlockingQueueDemo.java",
    "content": "import java.util.List;\r\nimport java.util.concurrent.ArrayBlockingQueue;\r\nimport java.util.concurrent.BlockingQueue;\r\n\r\n/*\r\n* ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列，此队列按FIFO原则对元素进行排序\r\n* LinkedBlockingQueue:是一个基于链表结构的阻塞队列，此队列按FIFO排序元素，吞吐量高于ArrayBlockingQueue\r\n* SynchronousQueue：一个不存储元素的阻塞队列，每个插入操作必须等到另一个线程调用移出操作，否则插入操作一直处于\r\n* 阻塞状态，吞吐量通常要高\r\n*\r\n*\r\n*\r\n* 2.阻塞队列\r\n*   2.1阻塞队列有没有好的一面\r\n*   2.2不得不阻塞，你如何管理\r\n* */\r\npublic class BlockingQueueDemo {\r\n    public static void main(String[] args) throws Exception{\r\n//        List list = null;\r\n        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);\r\n//        往阻塞队列添加元素\r\n        System.out.println(blockingQueue.add(\"a\"));\r\n        System.out.println(blockingQueue.add(\"b\"));\r\n        System.out.println(blockingQueue.add(\"c\"));\r\n\r\n//        从阻塞队列取元素\r\n        System.out.println(blockingQueue.element());\r\n\r\n        System.out.println(blockingQueue.remove());\r\n        System.out.println(blockingQueue.remove());\r\n\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/CASDemo.java",
    "content": "import java.util.concurrent.atomic.AtomicInteger;\r\n\r\n/*\r\n* 1、CAS是什么？ ==>compareAndSet\r\n*    比较并交换\r\n* */\r\npublic class CASDemo {\r\n    public static void main(String[] args){\r\n        AtomicInteger atomicInteger = new AtomicInteger(5);\r\n        System.out.println(atomicInteger.compareAndSet(5,2019)+\"\\t current data: \"+atomicInteger.get());\r\n        System.out.println(atomicInteger.compareAndSet(5,1024)+\"\\t current data: \"+atomicInteger.get());\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/CallableDemo.java",
    "content": "import java.util.concurrent.Callable;\r\nimport java.util.concurrent.ExecutionException;\r\nimport java.util.concurrent.FutureTask;\r\n\r\n/*class MyThread implements Runnable{\r\n    public void run(){\r\n\r\n    }\r\n}*/\r\n\r\nclass MyThread implements Callable<Integer> {\r\n    public Integer call() throws Exception{\r\n        System.out.println(\".........come in callable\");\r\n        return 1024;\r\n    }\r\n}\r\n\r\n/*\r\n*\r\n* */\r\npublic class CallableDemo {\r\n    public static void main(String[] args) throws InterruptedException, ExecutionException {\r\n        FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());\r\n        Thread t1 = new Thread(futureTask,\"AA\");\r\n        t1.start();\r\n        int r1 = 100;\r\n        int r2 = futureTask.get();//要求获得Callable线程的计算结果，如果没有计算完成就要强求，会导致阻塞，知道计算完成，建议放在最后\r\n\r\n        System.out.println(\".....result\"+r1+r2);\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/ContainerNotSafeDemo.java",
    "content": "import java.util.*;\r\nimport java.util.concurrent.CopyOnWriteArrayList;\r\n\r\n/*\r\n* 集合类不安全问题:原因是为了保证并发性，add操作没有加锁\r\n* ArrayList\r\n* */\r\npublic class ContainerNotSafeDemo {\r\n    public static void main(String[] args){\r\n//        List<String> list = new ArrayList<>();\r\n//        List<String> list = Collections.synchronizedList(new ArrayList<>());\r\n        List<String> list = new CopyOnWriteArrayList<>();\r\n        for(int i=1;i<=3;i++){\r\n            new Thread(()->{\r\n                list.add(UUID.randomUUID().toString().substring(0,8));\r\n                System.out.println(list);\r\n            },String.valueOf(i)).start();\r\n        }\r\n//        java.util.ConcurrentModificationException\r\n        /*\r\n        * 1 故障现象\r\n        *   java.util.ConcurrentModificationException\r\n        *\r\n        * 2 导致原因\r\n        *   并发争抢修改导致，参考我们的花名册签名情况。\r\n        *   一个人正在写入，另一个同学过来抢夺，导致数据不一致异常。并发修改异常。\r\n        * 3 解决方案\r\n        *   3.1 new Vector<>();\r\n        *   3.2 Collections.synchronizedList(new ArrayList<>());\r\n        *   3.3 new CopyOnWriteArrayList<>()\r\n        *       写时复制\r\n        *       CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候，不直接往当前容器Object[]添加，\r\n        *       而是先将当前object[]进行Copy，复制出一个新的容器Object[] newElements，然后新的容器Object[] newElements\r\n        *       里添加元素，添加完元素之后，再将原容器的引用指向新的容器setArray（newElements）;这样做的好处是可以对\r\n        *       copyonwrite容器进行并发的读，而不需要加锁，因为当前容器不会添加任何元素。所以copyonwrite容器也是一种\r\n        *       读写分离的思想，读和写不同的容器。\r\n        *\r\n        * 4 优化建议\r\n        * */\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/CountDownLatch.java",
    "content": "/*\r\n*\r\n* */\r\npublic class CountDownLatch {\r\n    public static void main(String[] args) throws Exception{\r\n        java.util.concurrent.CountDownLatch countDownLatch = new java.util.concurrent.CountDownLatch(6);\r\n\r\n        for(int i=1;i<=6;i++){\r\n            new Thread(()->{\r\n                System.out.println(Thread.currentThread().getName()+\"\\t 上完自习，离开教室\");\r\n                countDownLatch.countDown();\r\n            },String.valueOf(i)).start();\r\n        }\r\n\r\n        countDownLatch.await();\r\n        System.out.println(Thread.currentThread().getName()+\"\\t *****班长最后关门走人\");\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/CyclicBarrierDemo.java",
    "content": "import java.util.concurrent.BrokenBarrierException;\r\nimport java.util.concurrent.CyclicBarrier;\r\n\r\n/*\r\n*\r\n* */\r\npublic class CyclicBarrierDemo {\r\n    public static void main(String[] args){\r\n        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{System.out.println(\"召唤神龙\");});\r\n\r\n        for(int i=1;i<=7;i++){\r\n            final int tempInt = i;\r\n            new Thread(()->{\r\n                System.out.println(Thread.currentThread().getName()+\"\\t 收集到第：\"+tempInt+\"龙珠\");\r\n                try{\r\n                    cyclicBarrier.await();\r\n                } catch (InterruptedException e){\r\n                    e.printStackTrace();\r\n                } catch (BrokenBarrierException e){\r\n                    e.printStackTrace();\r\n                }\r\n\r\n            },String.valueOf(i)).start();\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/DeadLockDemo.java",
    "content": "/*\r\n* 死锁是指两个或者两个以上的进程在执行过程中，因抢夺资源而造成的一种互相等待的现象，\r\n* 若无外力干涉它们将都无法推进下去，如果系统资源充足，进程的资源请求都能够得到满足，\r\n* 死锁出现的可能性也就很低，否则就会因争夺有限的资源而陷入死锁。\r\n* */\r\n\r\nimport java.util.concurrent.TimeUnit;\r\n\r\nclass HoldLockThread implements Runnable{\r\n    private String lockA;\r\n    private String lockB;\r\n\r\n    public HoldLockThread(String lockA,String lockB){\r\n        this.lockA = lockA;\r\n        this.lockB = lockB;\r\n    }\r\n\r\n    public void run(){\r\n        synchronized (lockA){\r\n            System.out.println(Thread.currentThread().getName()+\"\\t自己持有：\"+lockA+\"\\t尝试获得：\"+lockB);\r\n            //暂停一下\r\n            try{ TimeUnit.SECONDS.sleep(2); }catch (InterruptedException e){e.printStackTrace();}\r\n\r\n            synchronized (lockB){\r\n                System.out.println(Thread.currentThread().getName()+\"\\t自己持有：\"+lockB+\"\\t尝试获得：\"+lockA);\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\npublic class DeadLockDemo {\r\n    public static void main(String[] args){\r\n        String lockA = \"lockA\";\r\n        String lockB = \"lockB\";\r\n\r\n        new Thread(new HoldLockThread(lockA,lockB),\"ThreadAAA\").start();\r\n        new Thread(new HoldLockThread(lockB,lockA),\"ThreadBBB\").start();\r\n\r\n        /*\r\n        * linux  ps -ef|grep xxxx    ls -l查看当前进程的命令\r\n        * windows下的java运行程序，也有类似ps的查看进程的命令，但是目前我们需要查看的只是java\r\n        *           jps = java ps      jps -l\r\n        *           jstack\r\n        * */\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/GCOverheadDemo.java",
    "content": "import java.util.ArrayList;\r\nimport java.util.List;\r\n\r\n/*\r\n* GC回收时间长时会抛出OutOfMemoryError。过长的定义是，超过98%的时间用来做GC并且回收了\r\n* 不到2%的堆内存，连续多次GC都只回收了不到2%的极端情况下才会抛出。\r\n\r\n假设不抛出GC overhead limit错误会发生什么情况呢？\r\n那就是GC清理的这么点内存很快会再次填满，迫使GC再次执行，这样就形成恶性循环，CPU使用率一直\r\n是100%，而GC缺没有任何成果。\r\n* */\r\n\r\npublic class GCOverheadDemo {\r\n    public static void main(String[] args){\r\n        int i = 0;\r\n        List<String> list = new ArrayList<>();\r\n\r\n        try{\r\n            while(true){\r\n                list.add(String.valueOf(++i).intern());\r\n            }\r\n        }catch (Throwable e){\r\n            System.out.println(\"***************i:\"+i);\r\n            e.printStackTrace();\r\n            throw e;\r\n        }\r\n\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/GCRootDemo.java",
    "content": "/*\r\n* 在java中可作为GC Roots的对象有：\r\n* 1.虚拟机栈（栈帧中的局部变量区，也叫做局部变量表）中引用的对象。\r\n* 2.方法区中的类静态属性引用的对象。\r\n* 3.方法区中常量引用的对象\r\n* 4.本地方法栈中JNI（Native方法）引用的对象。\r\n* */\r\npublic class GCRootDemo {\r\n    private byte[] byteArray = new byte[100*1024*1024];\r\n\r\n    public static void m1(){\r\n        GCRootDemo t1 = new GCRootDemo();\r\n        System.gc();\r\n        System.out.println(\"第一次GC完成\");\r\n    }\r\n\r\n    public static void main(String[] args){\r\n        m1();\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/HelloGC.java",
    "content": "public class HelloGC {\r\n    public static int oneAddone(int x,int y){\r\n        return x+y;\r\n    }\r\n    public static void main(String[] args) throws InterruptedException {\r\n        int res = oneAddone(1,1);\r\n        System.out.println(res);\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/JavaHeapSpaceDemo.java",
    "content": "import java.util.Random;\r\n\r\npublic class JavaHeapSpaceDemo {\r\n    public static void main(String[] args){\r\n        String str = \"seu\";\r\n\r\n        while(true){\r\n            str += str + new Random().nextInt(11111111)+new Random().nextInt(22222222);\r\n            str.intern();\r\n        }\r\n\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/MetaspaceOOMT.java",
    "content": "\r\n/*\r\n* Java8之后的版本使用Metaspace来替代永久代\r\n* Metaspace是方法区在HotSpot中的实现，它与持久带最大的区别在于：Metaspace并不在虚拟机内存中而是使用\r\n* 本地内存，也即在java8中，class metaspace（the virtual machines internal presentation of java class）\r\n* ,被存储在叫做Metaspace的native memory\r\n*\r\n* 永久代（Metaspace）存放以下信息：\r\n* 虚拟机加载的类信息\r\n* 常量池\r\n* 静态变量\r\n* 即时编译后的代码\r\n* */\r\n\r\npublic class MetaspaceOOMT {\r\n    static class OOMTest{\r\n\r\n    }\r\n    public static void main(String[] args){\r\n        int i=0;//模拟多少次后发生异常\r\n\r\n        try{\r\n            while (true){\r\n                i++;\r\n                \r\n            }\r\n        }catch (Throwable e){\r\n            System.out.println(\"********多少次后发生了异常：\"+i);\r\n            e.printStackTrace();\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/MyThreadPoolDemo.java",
    "content": "/*\r\n*第4种获得/使用java多线程的方式，通过线程池\r\n* （其他三种是：继承Thread类；实现Runnable接口，但是Runnable没有返回值，不抛异常；\r\n* 实现Callable接口，有返回值，会跑出异常）\r\n* */\r\n\r\nimport java.util.concurrent.*;\r\n\r\n//System.out.println(Runtime.getRuntime().availableProcessors());\r\n//Array Arrays  辅助工具类\r\n//Collection Collections\r\n//Executor Executors\r\npublic class MyThreadPoolDemo {\r\n    public static void main(String[] args){\r\n        ExecutorService threadPool = new ThreadPoolExecutor(2,\r\n                5,\r\n                1L,\r\n                TimeUnit.SECONDS,\r\n                new LinkedBlockingQueue<>(3),\r\n                Executors.defaultThreadFactory(),\r\n                new ThreadPoolExecutor.DiscardPolicy());\r\n\r\n        try{\r\n            for(int i=1;i<=11;i++){\r\n                threadPool.execute(()->{\r\n                    System.out.println(Thread.currentThread().getName()+\"\\t 办理业务\");\r\n                });\r\n            }\r\n        }catch (Exception e){\r\n            e.printStackTrace();\r\n        }finally {\r\n            threadPool.shutdown();\r\n        }\r\n    }\r\n\r\n    private static void threadPoolInit() {\r\n        //ExecutorService threadPool = Executors.newFixedThreadPool(5);//一池5个处理线程\r\n        //ExecutorService threadPool = Executors.newFixedThreadPool(1);//一池1个线程\r\n        ExecutorService threadPool = Executors.newCachedThreadPool();//一池N个线程\r\n\r\n        //模拟10个用户来办理业务，每个用户就是一个来自外部的请求线程\r\n        try{\r\n            for(int i=1;i<=10;i++){\r\n                threadPool.execute(()->{\r\n                    System.out.println(Thread.currentThread().getName()+\"\\t 办理业务\");\r\n                });\r\n            }\r\n        }catch (Exception e){\r\n            e.printStackTrace();\r\n        }finally {\r\n            threadPool.shutdown();\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/PhantomReferenceDemo.java",
    "content": "import java.lang.ref.PhantomReference;\r\nimport java.lang.ref.ReferenceQueue;\r\n\r\npublic class PhantomReferenceDemo {\r\n    public static void main(String[] args) throws InterruptedException {\r\n        Object o1 = new Object();\r\n        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();\r\n        PhantomReference<Object> phantomReference = new PhantomReference<>(o1,referenceQueue);\r\n\r\n        System.out.println(o1);\r\n        System.out.println(phantomReference.get());\r\n        System.out.println(referenceQueue.poll());\r\n\r\n        System.out.println(\"=================\");\r\n        o1 = null;\r\n        System.gc();\r\n        Thread.sleep(500);\r\n\r\n        System.out.println(o1);\r\n        System.out.println(phantomReference.get());\r\n        System.out.println(referenceQueue.poll());\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/ProdConsumer_BlockQueueDemo.java",
    "content": "import java.util.concurrent.ArrayBlockingQueue;\r\nimport java.util.concurrent.BlockingQueue;\r\nimport java.util.concurrent.TimeUnit;\r\nimport java.util.concurrent.atomic.AtomicInteger;\r\n\r\nclass MyResource{\r\n    private volatile boolean FLAG = true;//默认开启，进行生产+消费\r\n    private AtomicInteger atomicInteger = new AtomicInteger();\r\n\r\n    BlockingQueue<String> blockingQueue = null;\r\n    public MyResource(BlockingQueue<String> blockingQueue) {\r\n        this.blockingQueue = blockingQueue;\r\n        System.out.println(blockingQueue.getClass().getName());\r\n    }\r\n\r\n    public void myProd() throws Exception{\r\n        String data = null;\r\n        boolean retValue;\r\n        while(FLAG){\r\n            data = atomicInteger.incrementAndGet()+\"\";\r\n            retValue = blockingQueue.offer(data,2L, TimeUnit.SECONDS);\r\n            if(retValue){\r\n                System.out.println(Thread.currentThread().getName()+\"\\t插入队列\"+data+\"成功\");\r\n            }else{\r\n                System.out.println(Thread.currentThread().getName()+\"\\t插入队列\"+data+\"失败\");\r\n            }\r\n            TimeUnit.SECONDS.sleep(1);\r\n        }\r\n        System.out.println(Thread.currentThread().getName()+\"\\t生产停止\");\r\n    }\r\n\r\n    public void myConsumer() throws Exception{\r\n        String result = null;\r\n        while(FLAG){\r\n            result = blockingQueue.poll(2L,TimeUnit.SECONDS);\r\n            if(null==result || result.equalsIgnoreCase(\"\")){\r\n                FLAG = false;\r\n                System.out.println(Thread.currentThread().getName()+\"\\t 超过2秒，消费退出\");\r\n                System.out.println();\r\n                System.out.println();\r\n                return;\r\n            }\r\n            System.out.println(Thread.currentThread().getName()+\"\\t消费队列\"+result+\"成功\");\r\n        }\r\n    }\r\n\r\n    public void stop() throws Exception{\r\n        this.FLAG = false;\r\n    }\r\n}\r\n\r\n/*\r\n* volatile/CAS/atomicInteger/BlockQueue/线程交互/原子引用\r\n* */\r\n\r\npublic class ProdConsumer_BlockQueueDemo {\r\n    public static void main(String[] args) throws Exception{\r\n        MyResource myResource = new MyResource(new ArrayBlockingQueue<>(10));\r\n\r\n        new Thread(()->{\r\n            System.out.println(Thread.currentThread().getName()+\"\\t 生产线程启动\");\r\n            System.out.println();\r\n            System.out.println();\r\n            try{\r\n                myResource.myProd();\r\n            }catch (Exception e){\r\n                e.printStackTrace();\r\n            }\r\n        },\"Prod\").start();\r\n\r\n        new Thread(()->{\r\n            System.out.println(Thread.currentThread().getName()+\"\\t 消费线程启动\");\r\n            try{\r\n                myResource.myConsumer();\r\n            }catch (Exception e){\r\n                e.printStackTrace();\r\n            }\r\n        },\"Consumer\").start();\r\n\r\n        try{TimeUnit.SECONDS.sleep(5);}catch (InterruptedException e){e.printStackTrace();}\r\n\r\n        System.out.println();\r\n        System.out.println();\r\n        System.out.println();\r\n\r\n        System.out.println(\"5秒钟到，main停止\");\r\n        myResource.stop();\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/ProdConsumer_TraditionDemo.java",
    "content": "import java.util.concurrent.locks.Condition;\r\nimport java.util.concurrent.locks.Lock;\r\nimport java.util.concurrent.locks.ReentrantLock;\r\n\r\nclass ShareData{  //资源类\r\n    private int number = 0;\r\n    private Lock lock = new ReentrantLock();\r\n    private Condition condition = lock.newCondition();\r\n\r\n    public void increment() throws Exception{\r\n        lock.lock();\r\n        try{\r\n            //        1.判断\r\n            while(number !=0){\r\n//            等待，不能生产\r\n                condition.await();\r\n            }\r\n            //2.干活\r\n            number++;\r\n            System.out.println(Thread.currentThread().getName()+\"\\t\"+number);\r\n            //3.通知唤醒\r\n            condition.signalAll();\r\n        } catch (Exception e){\r\n            e.printStackTrace();\r\n        }finally {\r\n            lock.unlock();\r\n        }\r\n\r\n    }\r\n\r\n    public void decrement() throws Exception{\r\n        lock.lock();\r\n        try{\r\n            //        1.判断\r\n            while(number ==0){\r\n//            等待，不能生产\r\n                condition.await();\r\n            }\r\n            //2.干活\r\n            number--;\r\n            System.out.println(Thread.currentThread().getName()+\"\\t\"+number);\r\n            //3.通知唤醒\r\n            condition.signalAll();\r\n        } catch (Exception e){\r\n            e.printStackTrace();\r\n        }finally {\r\n            lock.unlock();\r\n        }\r\n\r\n    }\r\n\r\n}\r\n\r\n/*\r\n* 题目：一个初始值为0的变量，两个线程对其交替操作，一个加1，一个减1，来5轮\r\n*\r\n* 1.线程操作资源类\r\n* 2.判断 干活 通知\r\n* 3.防止虚假唤醒机制\r\n* */\r\npublic class ProdConsumer_TraditionDemo {\r\n    public static void main(String[] args){\r\n        ShareData shareData = new ShareData();\r\n\r\n        new Thread(()->{\r\n            for(int i=1;i<=5;i++){\r\n                try {\r\n                    shareData.increment();\r\n                }catch (Exception e){\r\n                    e.printStackTrace();\r\n                }\r\n            }\r\n        },\"AAA\").start();\r\n\r\n        new Thread(()->{\r\n            for(int i=1;i<=5;i++){\r\n                try {\r\n                    shareData.decrement();\r\n                }catch (Exception e){\r\n                    e.printStackTrace();\r\n                }\r\n            }\r\n        },\"BBB\").start();\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/ReadWriteLockDemo.java",
    "content": "/*\r\n* 多个线程同时读一个资源类没有问题，所以为了满足并发量，读取共享资源应该可以同时进行。但是写共享\r\n* 资源只能有一个线程。\r\n*\r\n* 写操作：原子+独占，整个过程必须是一个完整的统一体，中间不许被分割，被打断。\r\n* */\r\n\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\nimport java.util.concurrent.TimeUnit;\r\nimport java.util.concurrent.locks.Lock;\r\nimport java.util.concurrent.locks.ReentrantLock;\r\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\r\n\r\nclass MyCache{\r\n    private volatile Map<String,Object> map = new HashMap<>();\r\n//    private Lock lock = new ReentrantLock();\r\n    private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();\r\n    public void put(String key,Object value){\r\n\r\n        reentrantReadWriteLock.writeLock().lock();\r\n        try{\r\n            System.out.println(Thread.currentThread().getName()+\"\\t 正在写入：\"+key);\r\n            try{\r\n                TimeUnit.MILLISECONDS.sleep(300);\r\n            } catch (InterruptedException e) {e.printStackTrace();}\r\n            map.put(key,value);\r\n            System.out.println(Thread.currentThread().getName()+\"\\t 写入完成\");\r\n        }catch (Exception e){\r\n            e.printStackTrace();\r\n        }finally {\r\n            reentrantReadWriteLock.writeLock().unlock();\r\n        }\r\n\r\n    }\r\n\r\n    public void get(String key){\r\n        reentrantReadWriteLock.readLock().lock();\r\n        try {\r\n            System.out.println(Thread.currentThread().getName()+\"\\t 正在读取：\"+key);\r\n            try{\r\n                TimeUnit.MILLISECONDS.sleep(300);\r\n            } catch (InterruptedException e) {e.printStackTrace();}\r\n            Object result = map.get(key);\r\n            System.out.println(Thread.currentThread().getName()+\"\\t 读取完成\"+result);\r\n        }catch (Exception e){\r\n            e.printStackTrace();\r\n        }finally {\r\n            reentrantReadWriteLock.readLock().unlock();\r\n        }\r\n\r\n    }\r\n\r\n}\r\n\r\npublic class ReadWriteLockDemo {\r\n    public static void main(String[] args){\r\n        MyCache myCache = new MyCache();\r\n        for(int i=1;i<=5;i++){\r\n            final int tempInt = i;\r\n            new Thread(()->{\r\n                myCache.put(tempInt+\"\",tempInt+\"\");\r\n            },String.valueOf(i)).start();\r\n        }\r\n\r\n        for(int i=1;i<=5;i++){\r\n            final int tempInt = i;\r\n            new Thread(()->{\r\n                myCache.get(tempInt+\"\");\r\n            },String.valueOf(i)).start();\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/ReferenceQueueDemo.java",
    "content": "import java.lang.ref.ReferenceQueue;\r\nimport java.lang.ref.WeakReference;\r\n\r\npublic class ReferenceQueueDemo {\r\n    public static void main(String[] args) throws InterruptedException{\r\n        Object o1 = new Object();\r\n        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();\r\n        WeakReference<Object> weakReference = new WeakReference<>(o1,referenceQueue);\r\n        System.out.println(o1);\r\n        System.out.println(weakReference.get());\r\n        System.out.println(referenceQueue.poll());\r\n\r\n        System.out.println(\"=============\");\r\n        o1 = null;\r\n        System.gc();\r\n        Thread.sleep(500);\r\n\r\n        System.out.println(o1);\r\n        System.out.println(weakReference.get());\r\n        System.out.println(referenceQueue.poll());\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/RenenterLockDemo.java",
    "content": "/*\r\n* 可重入锁（也就是递归锁）：指的是同一个线程外层函数获得锁之后，内层递归函数仍然能获取该锁的代码，\r\n* 在同一线程在外层方法获取锁的时候，在进入内层方法会自动获取锁。也就是说，线程可以进入任何一个\r\n* 它已经拥有的锁所有同步着的代码块。\r\n* */\r\nclass Phone{\r\n    public synchronized void sendSMS() throws Exception{\r\n        System.out.println(Thread.currentThread().getId()+\"\\t invoked sendSMS()\");\r\n        sendEmail();\r\n    }\r\n\r\n    public synchronized void sendEmail() throws Exception{\r\n        System.out.println(Thread.currentThread().getId()+\"\\t invoked sendEmail()\");\r\n    }\r\n}\r\n\r\npublic class RenenterLockDemo {\r\n    public static void main(String[] args){\r\n        Phone phone = new Phone();\r\n        new Thread(()->{\r\n            try {\r\n                phone.sendSMS();\r\n            } catch (Exception e){\r\n                e.printStackTrace();\r\n            }\r\n        },\"t1\").start();\r\n\r\n        new Thread(()->{\r\n            try {\r\n                phone.sendSMS();\r\n            } catch (Exception e){\r\n                e.printStackTrace();\r\n            }\r\n        },\"t2\").start();\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/SemaphoreDemo.java",
    "content": "import java.util.concurrent.Semaphore;\r\nimport java.util.concurrent.TimeUnit;\r\n\r\n/*\r\n*\r\n* */\r\npublic class SemaphoreDemo {\r\n    public static void main(String[] args){\r\n        Semaphore semaphore = new Semaphore(3);  //模拟3个车位\r\n        for(int i=1;i<=6;i++){  //模拟6部车\r\n            new Thread(()->{\r\n                try{\r\n                    semaphore.acquire();\r\n                    System.out.println(Thread.currentThread().getName()+\"\\t抢到车位\");\r\n                    try{\r\n                        TimeUnit.SECONDS.sleep(3);\r\n                    } catch (InterruptedException e) {e.printStackTrace();}\r\n                    System.out.println(Thread.currentThread().getName()+\"\\t停车3秒后离开车位\");\r\n                } catch (InterruptedException e){\r\n                    e.printStackTrace();\r\n                } finally {\r\n                    semaphore.release();\r\n                }\r\n            },String.valueOf(i)).start();\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/SingletonDemo.java",
    "content": "\r\npublic class SingletonDemo {\r\n    private static SingletonDemo instance = null;\r\n    private SingletonDemo(){\r\n        System.out.println(Thread.currentThread().getName()+\"\\t 我是构造函数SingletonDemo（）\");\r\n    }\r\n//    DCL(Double Check Lock双端检锁机制)\r\n    public static SingletonDemo getInstance(){\r\n        if(instance==null){\r\n            synchronized (SingletonDemo.class){\r\n                if(instance==null){\r\n                    instance = new SingletonDemo();\r\n                }\r\n            }\r\n\r\n        }\r\n        return instance;\r\n    }\r\n\r\n    public static void main(String[] args){\r\n//        单线程（main线程的操作）\r\n//        System.out.println(SingletonDemo.getInstance()==SingletonDemo.getInstance());\r\n//        System.out.println(SingletonDemo.getInstance()==SingletonDemo.getInstance());\r\n//        System.out.println(SingletonDemo.getInstance()==SingletonDemo.getInstance());\r\n//\r\n//        System.out.println(\",,,,,\");\r\n\r\n//        并发多线程后，情况发生了很大的变化\r\n        for(int i=1;i<=10;i++){\r\n            new Thread(()->{\r\n                SingletonDemo.getInstance();\r\n            },String.valueOf(i)).start();\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/SoftReferenceDemo.java",
    "content": "import java.lang.ref.SoftReference;\r\n\r\npublic class SoftReferenceDemo {\r\n    /*\r\n    * 内存够用的时候就保留，不够用就回收\r\n    * */\r\n    public static void softRef_Memory_Enough(){\r\n        Object o1 = new Object();\r\n        SoftReference<Object> softReference = new SoftReference<>(o1);\r\n        System.out.println(o1);\r\n        System.out.println(softReference.get());\r\n\r\n        o1 = null;\r\n        System.gc();\r\n        System.out.println(o1);\r\n        System.out.println(softReference.get());\r\n    }\r\n\r\n    public static void softRef_Memory_NotEnough(){\r\n        Object o1 = new Object();\r\n        SoftReference<Object> softReference = new SoftReference<>(o1);\r\n        System.out.println(o1);\r\n        System.out.println(softReference.get());\r\n\r\n        o1 = null;\r\n\r\n        try{\r\n            byte[] bytes = new byte[30*1024*1024];\r\n        }catch (Throwable e){\r\n            e.printStackTrace();\r\n        }finally {\r\n            System.out.println(o1);\r\n            System.out.println(softReference.get());\r\n        }\r\n    }\r\n\r\n    public static void main(String[] args){\r\n        //softRef_Memory_Enough();\r\n        softRef_Memory_NotEnough();\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/SpinLockDemo.java",
    "content": "import java.util.concurrent.TimeUnit;\r\nimport java.util.concurrent.atomic.AtomicInteger;\r\nimport java.util.concurrent.atomic.AtomicReference;\r\n\r\n/*\r\n* 写一个自旋锁\r\n* 自旋锁的好处：循环比较获取直到成功为止，没有类似wait的阻塞。\r\n* */\r\npublic class SpinLockDemo {\r\n    AtomicReference<Thread> atomicReference = new AtomicReference<>();\r\n\r\n    public void myLock(){\r\n        Thread thread = Thread.currentThread();\r\n        System.out.println(Thread.currentThread().getName()+\"\\t come in \");\r\n        while(!atomicReference.compareAndSet(null,thread)){\r\n\r\n        }\r\n    }\r\n\r\n    public void myUnLock(){\r\n        Thread thread = Thread.currentThread();\r\n        atomicReference.compareAndSet(thread,null);\r\n        System.out.println(Thread.currentThread().getName()+\"\\t invoked myUnLock()\");\r\n    }\r\n\r\n    public static void main(String[] args){\r\n//        原子引用线程\r\n        SpinLockDemo spinLockDemo = new SpinLockDemo();\r\n        new Thread(()->{\r\n            spinLockDemo.myLock();\r\n            try{ TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) {e.printStackTrace();}\r\n        },\"AA\").start();\r\n\r\n        try{ TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {e.printStackTrace();}\r\n\r\n        new Thread(()->{\r\n            spinLockDemo.myLock();\r\n            try{ TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) {e.printStackTrace();}\r\n        },\"BB\").start();\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/StackOverflowErrorDemo.java",
    "content": "public class StackOverflowErrorDemo {\r\n    public static void main(String[] args){\r\n        stackOverflowError();\r\n    }\r\n\r\n    private static void stackOverflowError() {\r\n        stackOverflowError();\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/StrongReferenceDemo.java",
    "content": "public class StrongReferenceDemo {\r\n    public static void main(String[] args){\r\n        Object obj1 = new Object();//这样定义默认的就是强引用\r\n        Object obj2 = obj1;//obj2引用赋值\r\n        obj1 = null;\r\n        System.gc();\r\n        System.out.println(obj2);\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/SyncAndReentrantLockDemo.java",
    "content": "import java.util.concurrent.locks.Condition;\r\nimport java.util.concurrent.locks.Lock;\r\nimport java.util.concurrent.locks.ReentrantLock;\r\n\r\n/*\r\n* 题目：多线程之间按顺序调用，实现A->B->C三个线程启动，要求如下：\r\n* A打印5次，B打印10次，C打印15次\r\n* 紧接着\r\n* A打印5次，B打印10次，C打印15次\r\n* 。。。。。\r\n* 打印10轮\r\n* */\r\nclass ShareResource{\r\n    private int number = 1;//A:1.B:2,C:3\r\n    private Lock lock = new ReentrantLock();\r\n    private Condition c1 = lock.newCondition();\r\n    private Condition c2 = lock.newCondition();\r\n    private Condition c3 = lock.newCondition();\r\n\r\n    public void print5(){\r\n        lock.lock();\r\n        try{\r\n            //1判断\r\n            while(number != 1){\r\n                c1.await();\r\n            }\r\n            //2干活\r\n            for(int i=1;i<=5;i++){\r\n                System.out.println(Thread.currentThread().getName()+\"\\t\"+i);\r\n            }\r\n            //3通知\r\n            number = 2;\r\n            c2.signal();\r\n        }catch (Exception e){\r\n            e.printStackTrace();\r\n        }finally {\r\n            lock.unlock();\r\n        }\r\n    }\r\n\r\n    public void print10(){\r\n        lock.lock();\r\n        try{\r\n            //1判断\r\n            while(number != 2){\r\n                c2.await();\r\n            }\r\n            //2干活\r\n            for(int i=1;i<=10;i++){\r\n                System.out.println(Thread.currentThread().getName()+\"\\t\"+i);\r\n            }\r\n            //3通知\r\n            number = 3;\r\n            c3.signal();\r\n        }catch (Exception e){\r\n            e.printStackTrace();\r\n        }finally {\r\n            lock.unlock();\r\n        }\r\n    }\r\n\r\n    public void print15(){\r\n        lock.lock();\r\n        try{\r\n            //1判断\r\n            while(number != 3){\r\n                c3.await();\r\n            }\r\n            //2干活\r\n            for(int i=1;i<=15;i++){\r\n                System.out.println(Thread.currentThread().getName()+\"\\t\"+i);\r\n            }\r\n            //3通知\r\n            number = 1;\r\n            c1.signal();\r\n        }catch (Exception e){\r\n            e.printStackTrace();\r\n        }finally {\r\n            lock.unlock();\r\n        }\r\n    }\r\n\r\n\r\n\r\n\r\n}\r\n\r\npublic class SyncAndReentrantLockDemo {\r\n    public static void main(String[] args){\r\n        ShareResource shareResource = new ShareResource();\r\n\r\n        new Thread(()->{\r\n            for(int i=1;i<=10;i++){\r\n                shareResource.print5();\r\n            }\r\n        },\"A\").start();\r\n        new Thread(()->{\r\n            for(int i=1;i<=10;i++){\r\n                shareResource.print10();\r\n            }\r\n        },\"B\").start();\r\n        new Thread(()->{\r\n            for(int i=1;i<=10;i++){\r\n                shareResource.print15();\r\n            }\r\n        },\"C\").start();\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/SynchronousQueueDemo.java",
    "content": "import java.util.concurrent.BlockingQueue;\r\nimport java.util.concurrent.SynchronousQueue;\r\nimport java.util.concurrent.TimeUnit;\r\n\r\n/*\r\n* 阻塞队列SynchronousQueue演示\r\n* */\r\npublic class SynchronousQueueDemo {\r\n    public static void main(String[] args){\r\n        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();\r\n\r\n        new Thread(()->{\r\n            try{\r\n                System.out.println(Thread.currentThread().getName()+\"\\t put 1\");\r\n                blockingQueue.put(\"1\");\r\n                System.out.println(Thread.currentThread().getName()+\"\\t put 2\");\r\n                blockingQueue.put(\"2\");\r\n                System.out.println(Thread.currentThread().getName()+\"\\t put 3\");\r\n                blockingQueue.put(\"3\");\r\n            } catch (InterruptedException e){\r\n                e.printStackTrace();\r\n            }\r\n        },\"AAA\").start();\r\n\r\n        new Thread(()->{\r\n            try{\r\n                try{ TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e){ e.printStackTrace(); }\r\n                System.out.println(Thread.currentThread().getName()+\"\\t\"+blockingQueue.take());\r\n\r\n                try{ TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e){ e.printStackTrace(); }\r\n                System.out.println(Thread.currentThread().getName()+\"\\t\"+blockingQueue.take());\r\n\r\n                try{ TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e){ e.printStackTrace(); }\r\n                System.out.println(Thread.currentThread().getName()+\"\\t\"+blockingQueue.take());\r\n            } catch (InterruptedException e){\r\n                e.printStackTrace();\r\n            }\r\n        },\"BBB\").start();\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/VolatileDemo.java",
    "content": "import java.util.concurrent.TimeUnit;\r\nimport java.util.concurrent.atomic.AtomicInteger;\r\n\r\nclass MyData{\r\n    volatile int number = 0;\r\n    public void addTo60(){\r\n        this.number = 60;\r\n    }\r\n    public void addPlusPlus(){\r\n        number++;\r\n    }\r\n\r\n    AtomicInteger atomicInteger = new AtomicInteger();\r\n    public void addMyAtommic(){\r\n        atomicInteger.getAndIncrement();\r\n    }\r\n}\r\n/*\r\n1 验证volatile的可见性\r\n    1.1 加入int number=0，number变量之前根本没有添加volatile关键字修饰,没有可见性\r\n    1.2 添加了volatile，可以解决可见性问题\r\n2 验证volatile不保证原子性\r\n\r\n    2.1 原子性是不可分割，完整性，也即某个线程正在做某个具体业务时，中间不可以被加塞或者分割。\r\n    需要整体完成，要么同时成功，要么同时失败。\r\n\r\n    2.2 volatile不可以保证原子性演示\r\n\r\n    2.3 如何解决原子性\r\n        *加sync\r\n        *使用我们的JUC下AtomicInteger\r\n\r\n* */\r\npublic class VolatileDemo {\r\n    public static void main(String[] args){\r\n        MyData myData = new MyData();\r\n        for (int i = 1; i <= 20 ; i++) {\r\n            new Thread(()->{\r\n                for (int j = 1; j <= 1000 ; j++) {\r\n                    myData.addPlusPlus();\r\n                    myData.addMyAtommic();\r\n                }\r\n            },String.valueOf(i)).start();\r\n        }\r\n\r\n        //需要等待上述20个线程都计算完成后，再用main线程去的最终的结果是多少？\r\n//        try{TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}\r\n        while(Thread.activeCount() > 2){\r\n            Thread.yield();\r\n        }\r\n        System.out.println(Thread.currentThread().getName()+\"\\t finnally number value: \"+myData.number);\r\n        System.out.println(Thread.currentThread().getName()+\"\\t finnally number value: \"+myData.atomicInteger);\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/WeakHashMapDemo.java",
    "content": "import java.lang.ref.WeakReference;\r\nimport java.util.HashMap;\r\nimport java.util.WeakHashMap;\r\n\r\npublic class WeakHashMapDemo {\r\n    public static void main(String[] args){\r\n        myHashMap();\r\n        System.out.println(\"========\");\r\n        myWeakHashMap();\r\n    }\r\n\r\n    private static void myHashMap(){\r\n        HashMap<Integer,String> map = new HashMap<>();\r\n        Integer key = new Integer(1);\r\n        String value = \"HashMap\";\r\n\r\n        map.put(key,value);\r\n        System.out.println(map);\r\n\r\n        key = null;\r\n        System.out.println(map);\r\n\r\n        System.gc();\r\n        System.out.println(map);\r\n    }\r\n\r\n    private static void myWeakHashMap(){\r\n        WeakHashMap<Integer,String> map = new WeakHashMap<>();\r\n        Integer key = new Integer(2);\r\n        String value = \"WeakHashMap\";\r\n\r\n        map.put(key,value);\r\n        System.out.println(map);\r\n\r\n        key = null;\r\n        System.out.println(map);\r\n\r\n        System.gc();\r\n        System.out.println(map);\r\n    }\r\n}\r\n"
  },
  {
    "path": "尚硅谷-互联网大厂高频重点面试题第2季(上)-思维导图+Demo代码/Demo代码/WeakReferenceDemo.java",
    "content": "import java.lang.ref.WeakReference;\r\n\r\npublic class WeakReferenceDemo {\r\n    public static void main(String[] args){\r\n        Object o1 = new Object();\r\n        WeakReference<Object> weakReference = new WeakReference<>(o1);\r\n        System.out.println(o1);\r\n        System.out.println(weakReference.get());\r\n\r\n        o1 = null;\r\n        System.gc();\r\n        System.out.println(\"...............\");\r\n\r\n        System.out.println(o1);\r\n        System.out.println(weakReference.get());\r\n    }\r\n}\r\n"
  }
]