├── .gitignore ├── src └── main │ └── java │ └── com │ └── interview │ ├── base │ ├── package-info.java │ ├── OOMExample.java │ ├── MetaspaceOOMExample.java │ ├── QuickSort.java │ ├── SynchronizeQ.java │ ├── Bitmap.java │ └── LockLevel.java │ ├── distributed │ ├── package-info.java │ └── TCC.java │ ├── redis │ ├── package-info.java │ ├── Q1.java │ ├── Q3.java │ └── Q2.java │ ├── algorithm │ ├── KSolution.java │ ├── AddString.java │ ├── MD5Example.java │ ├── LongestSubstring.java │ └── KMPSearch.java │ └── leetcode │ ├── Q1768.java │ ├── Q1071.java │ ├── Q55.java │ ├── Q1431.java │ └── Q328.java ├── pom.xml ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | 4 | target 5 | *.class 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/com/interview/base/package-info.java: -------------------------------------------------------------------------------- 1 | package com.interview.base; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description Java 基础面试 6 | * @Date 5:25 PM 5/15/2023 7 | * 8 | * @Q1:请谈一下对Java中synchronized的理解 9 | * 10 | * @Q2:锁升级过程 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | */ 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/com/interview/distributed/package-info.java: -------------------------------------------------------------------------------- 1 | package com.interview.distributed; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description 6 | * @Date 9:32 AM 5/19/2023 7 | * 8 | * @分布式相关: 9 | * 10 | * @Q1:为什么用TCC?(为什么不用Seata、2PC?) 11 | * @Q2:TCC原理 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * 27 | * 28 | * 29 | * 30 | * 31 | */ 32 | -------------------------------------------------------------------------------- /src/main/java/com/interview/redis/package-info.java: -------------------------------------------------------------------------------- 1 | package com.interview.redis; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description redis 相关面试题 6 | * @Date 5:25 PM 5/15/2023 7 | * 8 | * @question1:介绍一下redis的架构 9 | * 10 | * @question2:redis的线程模型 11 | * 12 | * @question3:请详细讲一下redis的非阻塞式IO和多路复用,并讲出它的实现原理 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * 27 | * 28 | * 29 | * 30 | * 31 | * 32 | * 33 | * 34 | * 35 | * 36 | * 37 | * 38 | * 39 | * 40 | * 41 | */ 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/com/interview/base/OOMExample.java: -------------------------------------------------------------------------------- 1 | package com.interview.base; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * @Author qcl 8 | * @Description 9 | * @Date 10:13 AM 5/29/2023 10 | */ 11 | public class OOMExample { 12 | public static void main(String[] args) { 13 | List list = new ArrayList<>(); 14 | 15 | try { 16 | while (true) { 17 | byte[] arr = new byte[1024 * 1024]; // 分配1MB的字节数组 18 | list.add(arr); 19 | } 20 | } catch (OutOfMemoryError e) { 21 | System.out.println("OOM error occurred!"); 22 | // OOM error occurred! 23 | } 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/main/java/com/interview/base/MetaspaceOOMExample.java: -------------------------------------------------------------------------------- 1 | package com.interview.base; 2 | 3 | import javassist.CannotCompileException; 4 | import javassist.ClassPool; 5 | 6 | /** 7 | * @Author qcl 8 | * @Description 9 | * @Date 10:23 AM 5/29/2023 10 | */ 11 | public class MetaspaceOOMExample { 12 | public static void main(String[] args) { 13 | try { 14 | ClassPool classPool = ClassPool.getDefault(); 15 | int count = 0; 16 | 17 | while (true) { 18 | String className = "Class" + count++; 19 | try { 20 | classPool.makeClass(className).toClass(); 21 | } catch (CannotCompileException e) { 22 | e.printStackTrace(); 23 | } 24 | } 25 | } catch (OutOfMemoryError e) { 26 | System.out.println("Metaspace OOM error occurred!" + e.getMessage()); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.interview.all 8 | java-interview-all 9 | 1.0-SNAPSHOT 10 | Java相关面试题合集(每日更新八股文教程) ✨✨✨✨✨ https://www.qiuchenglei.top 11 | 12 | 13 | 8 14 | 8 15 | 16 | 17 | 18 | 19 | javassist 20 | javassist 21 | 3.12.1.GA 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/com/interview/algorithm/KSolution.java: -------------------------------------------------------------------------------- 1 | package com.interview.algorithm; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * @Author qcl 7 | * @Description 8 | * @Date 10:57 AM 6/21/2023 9 | */ 10 | public class KSolution { 11 | public static int maxOperations(int[] nums, int k) { 12 | Arrays.sort(nums); 13 | int l = 0, r = nums.length - 1; 14 | int ans = 0; 15 | while (l < r) { 16 | int res = nums[l] + nums[r]; 17 | if (res > k) { 18 | r--; 19 | } else if (res < k) { 20 | l++; 21 | } else { 22 | ans++; 23 | l++; 24 | r--; 25 | } 26 | } 27 | return ans; 28 | } 29 | 30 | public static void main(String[] args) { 31 | int[] nums1 = new int[]{1,2,3,4}; 32 | System.out.println( maxOperations(nums1, 5)); // 2 33 | 34 | int[] nums2 = new int[]{3,1,3,4,3}; 35 | System.out.println( maxOperations(nums2, 6)); // 1 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 程序员皮卡秋 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/main/java/com/interview/leetcode/Q1768.java: -------------------------------------------------------------------------------- 1 | package com.interview.leetcode; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description 1768: 交替合并字符串 : https://leetcode.cn/problems/merge-strings-alternately/ 6 | * @Date 2:10 PM 6/21/2023 7 | */ 8 | public class Q1768 { 9 | public static void main(String[] args) { 10 | System.out.println(mergeAlternately("abc", "pqrss")); // apbqcrss 11 | } 12 | 13 | /** 14 | * 双指针解法 15 | * 空间复杂度: O(1) 16 | * 时间复杂度: O(m+n) 17 | * @param word1 18 | * @param word2 19 | * @return 20 | */ 21 | public static String mergeAlternately(String word1, String word2) { 22 | int m = word1.length(), n = word2.length(); 23 | int i=0, j =0; 24 | 25 | StringBuilder ans = new StringBuilder(); 26 | while (i < m || j < n) { 27 | if(i < m) { 28 | ans.append(word1.charAt(i)); 29 | i ++; 30 | } 31 | 32 | if(j < n) { 33 | ans.append(word2.charAt(j)); 34 | j++; 35 | } 36 | } 37 | 38 | return ans.toString(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/interview/redis/Q1.java: -------------------------------------------------------------------------------- 1 | package com.interview.redis; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description 6 | * @Date 2:41 PM 5/16/2023 7 | * 8 | * @介绍一下redis的架构? 9 | * 10 | * Redis是一种开源的高性能键值存储系统,通常被用作内存数据库、缓存和消息中间件。它具有简单、灵活、高效的特点,以及丰富的数据结构和功能,使其成为许多应用程序的首选。 11 | * 12 | * Redis的架构主要由以下几个关键组件组成: 13 | * 14 | * 1. 客户端:应用程序通过Redis客户端与Redis服务器进行通信。客户端可以使用多种编程语言和协议与Redis进行交互,如Redis的官方客户端库、REST API或其他第三方客户端。 15 | * 16 | * 2. 服务器:Redis服务器是Redis的核心组件,负责处理客户端请求、执行命令和存储数据。服务器使用单线程事件循环模型,通过监听网络连接和处理事件来实现高并发和低延迟的性能。 17 | * 18 | * 3. 数据存储:Redis支持多种数据结构,包括字符串、哈希表、列表、集合和有序集合等。这些数据结构都可以通过唯一的键来进行访问和操作。Redis的数据存储是基于内存的,但也可以通过持久化机制将数据写入磁盘,以便在服务器重启时进行恢复。 19 | * 20 | * 4. 内存管理:Redis通过使用自定义的内存分配器来管理内存,以提高性能并减少内存碎片。它使用了多种技术,如对象共享、对象池和内存压缩,来有效地管理内存使用。 21 | * 22 | * 5. 高可用性:为了实现高可用性和数据冗余,Redis提供了主从复制和哨兵机制。主从复制通过将数据从主服务器复制到多个从服务器,实现数据的冗余备份和读写分离。哨兵机制用于监控和管理Redis服务器集群,当主服务器发生故障时,自动选举新的主服务器。 23 | * 24 | * 6. 集群模式:Redis还提供了集群模式,可以将数据分片存储在多个节点上,实现水平扩展和负载均衡。集群模式使用哈希槽来划分数据,每个节点负责管理一部分哈希槽的数据。 25 | * 26 | * 总体而言,Redis的架构设计简单而灵活,适用于各种场景。它的高性能、丰富的数据结构和功能,使其成为处理大规模数据、高并发访问和实时应用的理想选择。 27 | * 28 | * 29 | * 30 | * 31 | * 32 | * 33 | * 34 | * 35 | * 36 | * 37 | * 38 | */ 39 | public class Q1 { 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/interview/algorithm/AddString.java: -------------------------------------------------------------------------------- 1 | package com.interview.algorithm; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description 力扣 415. 字符串相加 6 | * @Date 10:35 AM 6/19/2023 7 | */ 8 | public class AddString { 9 | public static String addStrings(String num1, String num2) { 10 | int i = num1.length() - 1; 11 | int j = num2.length() - 1; 12 | int carry = 0; 13 | StringBuilder result = new StringBuilder(); 14 | 15 | while (i >= 0 || j >= 0) { 16 | int digit1 = i >= 0 ? num1.charAt(i) - '0' : 0; 17 | int digit2 = j >= 0 ? num2.charAt(j) - '0' : 0; 18 | 19 | int sum = digit1 + digit2 + carry; 20 | result.insert(0, sum % 10); 21 | carry = sum / 10; 22 | 23 | i--; 24 | j--; 25 | } 26 | 27 | if (carry != 0) { 28 | result.insert(0, carry); 29 | } 30 | 31 | return result.toString(); 32 | } 33 | 34 | public static void main(String[] args) { 35 | System.out.println(addStrings("11", "123")); // 134 36 | System.out.println(addStrings("456", "77")); // 533 37 | System.out.println(addStrings("0", "0")); // 0 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/interview/algorithm/MD5Example.java: -------------------------------------------------------------------------------- 1 | package com.interview.algorithm; 2 | 3 | import java.math.BigInteger; 4 | import java.security.MessageDigest; 5 | import java.security.NoSuchAlgorithmException; 6 | 7 | /** 8 | * @Author qcl 9 | * @Description 10 | * @Date 10:19 AM 6/13/2023 11 | */ 12 | public class MD5Example { 13 | public static void main(String[] args) { 14 | String input = "Hello, World!"; 15 | String md5Hash = getMD5Hash(input); 16 | System.out.println("MD5 Hash: " + md5Hash); 17 | } 18 | 19 | public static String getMD5Hash(String input) { 20 | try { 21 | // 创建MD5摘要算法实例 22 | MessageDigest md = MessageDigest.getInstance("MD5"); 23 | // 计算哈希值 24 | byte[] hashBytes = md.digest(input.getBytes()); 25 | // 转换为十六进制表示 26 | BigInteger hashNumber = new BigInteger(1, hashBytes); 27 | String hashString = hashNumber.toString(16); 28 | // 如果哈希值的长度不足32位,前面补0 29 | while (hashString.length() < 32) { 30 | hashString = "0" + hashString; 31 | } 32 | return hashString; 33 | } catch (NoSuchAlgorithmException e) { 34 | e.printStackTrace(); 35 | } 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/interview/leetcode/Q1071.java: -------------------------------------------------------------------------------- 1 | package com.interview.leetcode; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description 1071: 字符串的最大公约数 /// 字符串的最大公因子 6 | * @Date 2:36 PM 6/21/2023 7 | */ 8 | public class Q1071 { 9 | public static void main(String[] args) { 10 | System.out.println(gcdOfStrings("ABC", "ABC")); // ABC 11 | System.out.println(gcdOfStrings("ABABAB", "ABAB")); // AB 12 | } 13 | 14 | /** 15 | * gcd解法 16 | * @param str1 17 | * @param str2 18 | * @return 19 | */ 20 | public static String gcdOfStrings(String str1, String str2) { 21 | if (!str1.concat(str2).equals(str2.concat(str1))) { 22 | return ""; 23 | } 24 | return str1.substring(0, gcd(str1.length(), str2.length())); 25 | } 26 | 27 | /** 28 | * gcd 公式 29 | * @param m 30 | * @param n 31 | * @return 32 | */ 33 | public static int gcd1(int m, int n) { 34 | return n == 0 ? m : gcd(n, m % n); 35 | } 36 | 37 | /** 38 | * 耗时最低 39 | * @param a 40 | * @param b 41 | * @return 42 | */ 43 | public static int gcd(int a, int b) { 44 | int remainder = a % b; 45 | while (remainder != 0) { 46 | a = b; 47 | b = remainder; 48 | remainder = a % b; 49 | } 50 | return b; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/interview/base/QuickSort.java: -------------------------------------------------------------------------------- 1 | package com.interview.base; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description 6 | * @Date 12:10 PM 6/1/2023 7 | */ 8 | public class QuickSort { 9 | public static void main(String[] args) { 10 | int[] arr = {9, 2, 5, 1, 1000, 3, 7, 4, 6}; 11 | int max = findMaxValue(arr); 12 | System.out.println("最大值为: " + max); 13 | } 14 | 15 | public static int findMaxValue(int[] arr) { 16 | quickSort(arr, 0, arr.length - 1); 17 | return arr[arr.length - 1]; 18 | } 19 | 20 | public static void quickSort(int[] arr, int low, int high) { 21 | if (low < high) { 22 | int pivotIndex = partition(arr, low, high); 23 | quickSort(arr, low, pivotIndex - 1); 24 | quickSort(arr, pivotIndex + 1, high); 25 | } 26 | } 27 | 28 | public static int partition(int[] arr, int low, int high) { 29 | int pivot = arr[high]; 30 | int i = low - 1; 31 | for (int j = low; j < high; j++) { 32 | if (arr[j] <= pivot) { 33 | i++; 34 | int temp = arr[i]; 35 | arr[i] = arr[j]; 36 | arr[j] = temp; 37 | } 38 | } 39 | int temp = arr[i + 1]; 40 | arr[i + 1] = arr[high]; 41 | arr[high] = temp; 42 | return i + 1; 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/main/java/com/interview/leetcode/Q55.java: -------------------------------------------------------------------------------- 1 | package com.interview.leetcode; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description 跳跃游戏(力扣55题) 6 | * 7 | * 给定一个非负整数数组 `nums` ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标。 8 | * 9 | * 10 | ``` 11 | 输入:nums = [2,3,1,1,4] 12 | 输出:true 13 | 解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。 14 | ``` 15 | 16 | ``` 17 | 输入:nums = [3,2,1,0,4] 18 | 输出:false 19 | 解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。 20 | 21 | ``` 22 | * 23 | * 24 | * @Date 9:52 AM 6/27/2023 25 | */ 26 | public class Q55 { 27 | 28 | public static boolean canJump(int[] nums) { 29 | int maxReach = 0; // 当前能够到达的最远位置 30 | int n = nums.length; 31 | 32 | for (int i = 0; i < n; i++) { 33 | if (i > maxReach) { 34 | // 如果当前位置已经超过了能够到达的最远位置,说明无法到达最后一个位置 35 | return false; 36 | } 37 | 38 | maxReach = Math.max(maxReach, i + nums[i]); 39 | 40 | if (maxReach >= n - 1) { 41 | // 如果当前能够到达的最远位置已经超过或等于最后一个位置,说明可以到达最后一个位置 42 | return true; 43 | } 44 | } 45 | 46 | return false; 47 | } 48 | 49 | public static void main(String[] args) { 50 | int[] nums1 = new int[]{2,3,1,1,4}; 51 | System.out.println(canJump(nums1)); 52 | 53 | int[] nums2 = new int[]{3,2,1,0,4}; 54 | System.out.println(canJump(nums2)); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/interview/algorithm/LongestSubstring.java: -------------------------------------------------------------------------------- 1 | package com.interview.algorithm; 2 | 3 | import java.util.HashSet; 4 | 5 | /** 6 | * @Author qcl 7 | * @Description 找不重复子串的最长的字串 - 力扣第三题 8 | * @Date 9:55 AM 6/12/2023 9 | */ 10 | public class LongestSubstring { 11 | public static String findLongestSubstring(String str) { 12 | int n = str.length(); 13 | int left = 0; 14 | int right = 0; 15 | int maxLength = 0; 16 | String longestSubstring = ""; 17 | 18 | HashSet set = new HashSet<>(); 19 | 20 | while (right < n) { 21 | char currentChar = str.charAt(right); 22 | if (!set.contains(currentChar)) { 23 | set.add(currentChar); 24 | int currentLength = right - left + 1; 25 | if (currentLength > maxLength) { 26 | maxLength = currentLength; 27 | longestSubstring = str.substring(left, right + 1); 28 | } 29 | right++; 30 | } else { 31 | set.remove(str.charAt(left)); 32 | left++; 33 | } 34 | } 35 | 36 | return longestSubstring; 37 | } 38 | 39 | public static void main(String[] args) { 40 | String input = "pwwkew"; 41 | String longestSubstring = findLongestSubstring(input); 42 | System.out.println("Longest Substring: " + longestSubstring); 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/main/java/com/interview/distributed/TCC.java: -------------------------------------------------------------------------------- 1 | package com.interview.distributed; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description 6 | * @Date 9:34 AM 5/19/2023 7 | * 8 | * 9 | * @TCC 相关面试题 10 | * 11 | * @Q1:分布式事务有了解过吗?你是如何解决的呢? 12 | * 13 | *`分布式事务`是指在分布式系统中执行的涉及多个独立事务资源的事务操作。在分布式系统中,不同的服务或组件可能分布在不同的计算机节点上,而这些节点可能属于不同的管理域、运行在不同的操作系统上,甚至位于不同的物理位置。由于分布式系统的特性,需要在跨越`多个节点`的操作中保证数据的`一致性`和`可靠性`,这就引入了分布式事务的概念。 14 | * 15 | * 传统的单机事务通常使用`ACID(原子性、一致性、隔离性和持久性)`属性来确保事务的正确执行。然而,在分布式环境中,由于存在`网络延迟`、`节点故障`等因素,要实现`ACID`属性变得更加困难。因此,分布式事务提供了一种机制来确保在分布式环境下事务的正确性。 16 | * 17 | * 分布式事务的`关键问题`是如何协调多个节点之间的`事务操作`,以确保事务的`一致性`。常见的分布式事务模型包括`两阶段提交(Two-Phase Commit,2PC)、三阶段提交(Three-Phase Commit,3PC)、补偿事务(Compensating Transaction)`等。 18 | * 19 | * 在两阶段提交中,事务的协调者负责向参与者发送准备请求,并等待参与者的响应。第一阶段是`准备阶段`,协调者向所有参与者发送准备请求,并等待它们的响应。如果所有参与者都准备就绪,那么协调者进入第二阶段,即`提交阶段`,向所有参与者发送提交请求。参与者接收到提交请求后,执行事务,并向协调者发送响应。如果所有参与者都成功执行事务,那么协调者提交事务,否则协调者发送回滚请求,要求参与者进行回滚操作。 20 | * 21 | * 三阶段提交是对两阶段提交的改进,引入了`超时机制`和`预提交阶段`。在预提交阶段,协调者向所有参与者发送预提交请求,并等待它们的响应。如果所有参与者都预提交成功,那么协调者进入第二阶段,即正式提交阶段,向所有参与者发送正式提交请求。参与者接收到正式提交请求后,执行事务,并向协调者发送响应。在第三阶段,协调者向所有参与者发送最终提交请求,参与者确认事务的最终提交。 22 | * 23 | * `补偿事务`是一种基于补偿机制的分布式事务处理方法。在补偿事务中,每个参与者在执行事务之前,会记录执行事务所需的所有操作。如果在事务执行过程中发生错误,参与者可以使用记录的操作信息进行回滚操作,从而恢复到事务开始之前的状态。 24 | * 25 | * 分布式事务的设计与实现需要考虑到系统的`可用性、性能、数据一致性`等方面的权衡。合理选择分布式事务模型,并在系统设计中考虑故障恢复机制和容错机制,可以有效地处理分布式系统中的事务操作。 26 | * 27 | * 28 | * 29 | * 30 | * 31 | * 32 | * 33 | * 34 | * 35 | * 36 | * 37 | * 38 | * @Q2:为什么用TCC?(为什么不用Seata、2PC?) 39 | * @Q3:TCC原理 40 | * 41 | * 42 | * 43 | */ 44 | public class TCC { 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/interview/base/SynchronizeQ.java: -------------------------------------------------------------------------------- 1 | package com.interview.base; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description 6 | * @Date 4:15 PM 5/16/2023 7 | * 8 | * @请谈一下对Java中synchronize的理解 9 | * 10 | * 在Java中,关键字"synchronize"用于实现多线程之间的同步。它可以应用于方法或代码块,并确保在同一时间只有一个线程可以访问被同步的代码段。下面是对Java中"synchronize"的深入理解: 11 | * 12 | * 1. 保证原子性:synchronized关键字用于对临界区代码进行加锁,确保多个线程无法同时执行被同步的代码块或方法。这样可以保证代码块或方法中的操作是原子的, 13 | * 即要么全部执行完毕,要么不执行,从而避免了竞态条件(race condition)的问题。 14 | * 15 | * 2. 内置锁(Intrinsic Lock):在Java中,每个对象都有一个内置的锁,也称为监视器锁或互斥锁。当一个线程进入synchronized代码块时,它必须首先获得对象的内置锁,其他线程必须等待直到锁被释放。这样确保了同一时间只有一个线程能够执行被同步的代码块。 16 | * 17 | * 3. 互斥性和可见性:synchronized关键字不仅提供了互斥访问,还确保了可见性。当一个线程获取到锁时,它会清空工作内存中的变量副本,强制从主内存中重新加载变量的值。这样可以确保线程在访问变量时获取最新的值,而不是使用过期的副本。 18 | * 19 | * 4. 锁的粒度: 20 | * - 对象锁:当使用synchronized修饰实例方法时,锁定的是当前对象实例。这意味着同一时间只有一个线程可以访问该实例的synchronized方法,不同的实例之间互不影响。 21 | * - 类锁:当使用synchronized修饰静态方法时,锁定的是整个类对象。这意味着同一时间只有一个线程可以访问该类的任意一个synchronized静态方法,无论是不同实例还是相同实例。 22 | * 23 | * 5. 重入性:Java中的synchronized关键字支持重入性。也就是说,如果一个线程已经获得了一个对象的锁,它可以再次获取该对象的锁而不会被阻塞。这种机制允许线程在同步代码块内部调用其他同步方法,避免了自己阻塞自己的情况。 24 | * 25 | * 总之,Java中的synchronized关键字提供了一种简单而有效的方法来确保多线程之间的同步和线程安全性。它通过使用内置锁、互斥性和可见性保证了临界区代码的原子性执行,避免了数据竞争和数据不一致的问题。 26 | * 27 | */ 28 | public class SynchronizeQ { 29 | 30 | /** 31 | * 同步代码块(Synchronized Blocks):除了使用synchronized修饰整个方法外,还可以使用synchronized修饰一段代码块,称为同步代码块。这样可以在方法中只对某一部分代码进行同步,而不是整个方法。 32 | */ 33 | public void someMethod() { 34 | // 非同步代码 35 | synchronized (this) { 36 | // 需要同步的代码 37 | } 38 | // 非同步代码 39 | } 40 | 41 | } 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/main/java/com/interview/leetcode/Q1431.java: -------------------------------------------------------------------------------- 1 | package com.interview.leetcode; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * @Author qcl 8 | * @Description 1431: 拥有最多糖果的孩子 9 | *给你一个数组 candies 和一个整数 extraCandies ,其中 candies[i] 代表第 i 个孩子拥有的糖果数目。 10 | * 11 | * 对每一个孩子,检查是否存在一种方案,将额外的 extraCandies 个糖果分配给孩子们之后,此孩子有 最多 的糖果。注意,允许有多个孩子同时拥有 最多 的糖果数目。 12 | * 13 | * 输入:candies = [2,3,5,1,3], extraCandies = 3 14 | * 输出:[true,true,true,false,true] 15 | * 来源:力扣(LeetCode) 16 | * 链接:https://leetcode.cn/problems/kids-with-the-greatest-number-of-candies 17 | * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 18 | * 19 | * 20 | * @Date 2:57 PM 6/21/2023 21 | */ 22 | public class Q1431 { 23 | 24 | public static void main(String[] args) { 25 | int[] candies = new int[]{2,3,5,1,3}; 26 | System.out.println(kidsWithCandies(candies, 3)); // [true, true, true, false, true] 27 | 28 | int[] candies1 = new int[]{4,2,1,1,2}; 29 | System.out.println(kidsWithCandies(candies1, 1)); // [true, false, false, false, false] 30 | } 31 | 32 | /** 33 | * 可以使用证明方法解题 34 | * @param candies 35 | * @param extraCandies 36 | * @return 37 | */ 38 | public static List kidsWithCandies(int[] candies, int extraCandies) { 39 | int maxCandies = 0; 40 | int len = candies.length; 41 | for(int i=0;i < len; ++i) { 42 | maxCandies = Math.max(maxCandies, candies[i]); 43 | } 44 | 45 | List ret = new ArrayList<>(); 46 | for(int i=0;i < len; ++i) { 47 | ret.add(candies[i] + extraCandies >= maxCandies); 48 | } 49 | 50 | return ret; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/interview/base/Bitmap.java: -------------------------------------------------------------------------------- 1 | package com.interview.base; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description 6 | * @Date 10:06 AM 5/24/2023 7 | */ 8 | public class Bitmap { 9 | private int[] bitmap; 10 | 11 | public Bitmap(int size) { 12 | bitmap = new int[size]; 13 | } 14 | 15 | public void setBit(int index) { 16 | int arrayIndex = index / 32; 17 | int bitOffset = index % 32; 18 | bitmap[arrayIndex] |= (1 << bitOffset); 19 | } 20 | 21 | public boolean getBit(int index) { 22 | int arrayIndex = index / 32; 23 | int bitOffset = index % 32; 24 | return (bitmap[arrayIndex] & (1 << bitOffset)) != 0; 25 | } 26 | 27 | public void expandBitmap(int newSize) { 28 | if (newSize <= bitmap.length * 32) { 29 | return; 30 | } 31 | 32 | int[] newBitmap = new int[newSize / 32 + 1]; 33 | System.arraycopy(bitmap, 0, newBitmap, 0, bitmap.length); 34 | bitmap = newBitmap; 35 | } 36 | 37 | public static void main(String[] args) { 38 | int size = 64; 39 | Bitmap bitmap = new Bitmap(size); 40 | 41 | bitmap.setBit(5); 42 | bitmap.setBit(10); 43 | bitmap.setBit(30); 44 | bitmap.setBit(300); 45 | 46 | System.out.println("Bit 5: " + bitmap.getBit(5)); 47 | System.out.println("Bit 10: " + bitmap.getBit(10)); 48 | System.out.println("Bit 30: " + bitmap.getBit(30)); 49 | System.out.println("Bit 20: " + bitmap.getBit(20)); 50 | System.out.println("Bit 300: " + bitmap.getBit(300)); 51 | 52 | int newSize = 128; 53 | bitmap.expandBitmap(newSize); 54 | 55 | System.out.println("Bit 120: " + bitmap.getBit(120)); 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/main/java/com/interview/algorithm/KMPSearch.java: -------------------------------------------------------------------------------- 1 | package com.interview.algorithm; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description KMP算法实现的代码,用于找出字符串中第一个匹配项的下标。如果找不到匹配项,返回-1。 6 | * @Date 10:32 AM 6/13/2023 7 | */ 8 | public class KMPSearch { 9 | public static void main(String[] args) { 10 | String text = "Hello, World!"; 11 | String pattern = "Hells"; 12 | 13 | int index = kmpSearch(text, pattern); 14 | System.out.println("First match index: " + index); 15 | } 16 | 17 | public static int kmpSearch(String text, String pattern) { 18 | if (text == null || pattern == null || text.isEmpty() || pattern.isEmpty()) { 19 | return -1; 20 | } 21 | 22 | int[] lps = computeLPSArray(pattern); 23 | 24 | int i = 0; // text中的索引 25 | int j = 0; // pattern中的索引 26 | 27 | while (i < text.length()) { 28 | if (text.charAt(i) == pattern.charAt(j)) { 29 | i++; 30 | j++; 31 | 32 | if (j == pattern.length()) { 33 | return i - j; // 匹配成功,返回第一个匹配项的下标 34 | } 35 | } else { 36 | if (j != 0) { 37 | j = lps[j - 1]; // 回溯到最长公共前后缀的下一个位置 38 | } else { 39 | i++; // 没有找到匹配项,继续在text中前进 40 | } 41 | } 42 | } 43 | 44 | return -1; // 没有找到匹配项 45 | } 46 | 47 | private static int[] computeLPSArray(String pattern) { 48 | int[] lps = new int[pattern.length()]; 49 | int len = 0; // 最长公共前后缀的长度 50 | int i = 1; 51 | 52 | while (i < pattern.length()) { 53 | if (pattern.charAt(i) == pattern.charAt(len)) { 54 | len++; 55 | lps[i] = len; 56 | i++; 57 | } else { 58 | if (len != 0) { 59 | len = lps[len - 1]; // 回溯到前一个位置继续匹配 60 | } else { 61 | lps[i] = 0; 62 | i++; 63 | } 64 | } 65 | } 66 | 67 | return lps; 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/main/java/com/interview/base/LockLevel.java: -------------------------------------------------------------------------------- 1 | package com.interview.base; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description 锁升级过程 6 | * @Date 10:13 AM 5/18/2023 7 | */ 8 | public class LockLevel { 9 | } 10 | 11 | //锁升级是指在Java中,synchronized关键字在不同的情况下可以升级使用不同级别的锁,以提高性能和减少开销。Java中的锁升级主要涉及到偏向锁、轻量级锁和重量级锁这三个级别。下面详细介绍每个级别及其升级过程。 12 | // 13 | // 1. 偏向锁(Biased Locking): 14 | // 偏向锁是指当只有一个线程访问同步块时,为了减少同步开销,JVM会自动将对象的锁记录下来,标记为偏向锁状态。当其他线程访问该同步块时,不需要竞争锁,而是直接获取锁。 15 | // 16 | // 锁升级过程: 17 | // - 初始状态:对象没有锁标记。 18 | // - 偏向锁申请:当第一个线程访问同步块时,JVM将该线程ID记录在对象头部,并将对象的标记状态设置为偏向锁。 19 | // - 偏向锁撤销:当其他线程尝试获取锁时,发现对象的偏向锁被占用,会撤销偏向锁,升级为轻量级锁。 20 | // 21 | // Java代码示例: 22 | 23 | class BiasedLockExample { 24 | private static final Object lock = new Object(); 25 | 26 | public static void main(String[] args) { 27 | synchronized (lock) { 28 | // 同步块 29 | } 30 | } 31 | } 32 | 33 | 34 | // 2. 轻量级锁(Lightweight Locking): 35 | // 轻量级锁是指当多个线程轻度竞争同步块时,JVM会将对象的锁记录在线程的栈帧中,而不是在对象头中。线程在进入同步块之前,通过CAS(比较并交换)操作尝试获取锁。如果CAS成功,则表示获取锁成功,进入同步块;如果CAS失败,表示存在竞争,升级为重量级锁。 36 | // 37 | // 锁升级过程: 38 | // - 初始状态:对象没有锁标记。 39 | // - 轻量级锁申请:第一个线程进入同步块时,JVM将锁的记录信息复制到线程的栈帧中,并将对象的标记状态设置为轻量级锁。 40 | // - 轻量级锁竞争:当其他线程尝试获取锁时,会使用CAS操作来竞争锁。如果CAS成功,表示获取锁成功,进入同步块;如果CAS失败,表示存在竞争,升级为重量级锁。 41 | 42 | class LightweightLockExample { 43 | private static final Object lock = new Object(); 44 | 45 | public static void main(String[] args) { 46 | synchronized (lock) { 47 | // 同步块 48 | } 49 | } 50 | } 51 | 52 | // 3. 重量级锁(Heavyweight Locking): 53 | // 重量级锁是指当多个线程激烈竞争同步块时,JVM会将对象的锁升级为重量级锁,使用操作系统提供的互斥量来实现锁机制。重量级锁涉及到线程的阻塞和唤醒操作,开销较大。 54 | // 55 | // 锁升级过程: 56 | // - 初始状态:对象没有锁标记。 57 | // - 重量级锁申请:当多个线程轮流竞争同步块时,锁会直接升级为重量级锁,通过操作系统提供的互斥量来实现锁机制。 58 | 59 | 60 | class HeavyweightLockExample { 61 | private static final Object lock = new Object(); 62 | 63 | public static void main(String[] args) { 64 | synchronized (lock) { 65 | // 同步块 66 | } 67 | } 68 | } 69 | 70 | 71 | // 需要注意的是,锁升级的过程是由JVM自动完成的,开发人员无需显式地控制锁升级。JVM会根据同步竞争的情况来自动选择合适的锁级别,以提供更好的性能和效率。 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /src/main/java/com/interview/leetcode/Q328.java: -------------------------------------------------------------------------------- 1 | package com.interview.leetcode; 2 | 3 | /** 4 | * @Author qcl 5 | * @Description 奇偶链表 力扣328题 6 | * 7 | * 给定单链表的头节点 head,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。 8 | * 9 | * 第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。 10 | * 11 | * 请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。 12 | * 13 | * 14 | * 15 | * ``` 16 | * 输入: head = [1,2,3,4,5] 17 | * 输出: [1,3,5,2,4] 18 | * ``` 19 | * 20 | * ``` 21 | * 输入: head = [2,1,3,5,6,4,7] 22 | * 输出: [2,3,6,7,1,5,4] 23 | * ``` 24 | * 25 | * @Date 9:54 AM 6/28/2023 26 | */ 27 | public class Q328 { 28 | 29 | public static class ListNode { 30 | int val; 31 | ListNode next; 32 | 33 | ListNode(int val) { 34 | this.val = val; 35 | this.next = null; 36 | } 37 | } 38 | 39 | 40 | 41 | public static ListNode oddEvenList(ListNode head) { 42 | if (head == null || head.next == null) { 43 | return head; 44 | } 45 | 46 | ListNode oddHead = head; 47 | ListNode evenHead = head.next; 48 | ListNode oddTail = oddHead; 49 | ListNode evenTail = evenHead; 50 | 51 | while (evenTail != null && evenTail.next != null) { 52 | oddTail.next = evenTail.next; 53 | oddTail = oddTail.next; 54 | evenTail.next = oddTail.next; 55 | evenTail = evenTail.next; 56 | } 57 | 58 | oddTail.next = evenHead; 59 | return oddHead; 60 | } 61 | 62 | public static void main(String[] args) { 63 | // Test Case 1: [1,2,3,4,5] 64 | ListNode head1 = new ListNode(1); 65 | head1.next = new ListNode(2); 66 | head1.next.next = new ListNode(3); 67 | head1.next.next.next = new ListNode(4); 68 | head1.next.next.next.next = new ListNode(5); 69 | ListNode result1 = oddEvenList(head1); 70 | printList(result1); // Expected: [1,3,5,2,4] 71 | 72 | // Test Case 2: [2,1,3,5,6,4,7] 73 | ListNode head2 = new ListNode(2); 74 | head2.next = new ListNode(1); 75 | head2.next.next = new ListNode(3); 76 | head2.next.next.next = new ListNode(5); 77 | head2.next.next.next.next = new ListNode(6); 78 | head2.next.next.next.next.next = new ListNode(4); 79 | head2.next.next.next.next.next.next = new ListNode(7); 80 | ListNode result2 = oddEvenList(head2); 81 | printList(result2); // Expected: [2,3,6,7,1,5,4] 82 | } 83 | 84 | private static void printList(ListNode head) { 85 | while (head != null) { 86 | System.out.print(head.val + " "); 87 | head = head.next; 88 | } 89 | System.out.println(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/interview/redis/Q3.java: -------------------------------------------------------------------------------- 1 | package com.interview.redis; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.nio.ByteBuffer; 6 | import java.nio.channels.*; 7 | import java.util.Iterator; 8 | 9 | /** 10 | * @Author qcl 11 | * @Description 12 | * @Date 10:15 AM 5/17/2023 13 | * 14 | * @请详细讲一下redis的非阻塞式IO和多路复用,并讲出它的实现原理 15 | * 16 | * 17 | * Redis使用非阻塞式I/O和多路复用技术来实现高性能的事件驱动模型。下面将详细介绍Redis的非阻塞式I/O和多路复用,并解释其实现原理: 18 | * 19 | * 1. 非阻塞式I/O: 20 | * 非阻塞式I/O是一种I/O操作的模式,在进行读取或写入操作时,不会阻塞线程等待操作完成。Redis使用非阻塞I/O来实现事件驱动模型,它的实现原理如下: 21 | * 22 | * - 设置套接字为非阻塞模式:Redis在接受客户端连接或创建套接字时,将套接字设置为非阻塞模式。这样,在进行读取或写入操作时,不会阻塞线程,而是立即返回。 23 | * 24 | * - 非阻塞的读取操作:当Redis执行读取操作时,它会检查套接字上是否有数据可读。如果没有数据可读,读取操作会立即返回,避免线程阻塞。Redis可以使用非阻塞的方式读取数据,以便处理其他事件。 25 | * 26 | * - 非阻塞的写入操作:当Redis执行写入操作时,它会检查套接字是否可写。如果套接字不可写,写入操作会立即返回,避免线程阻塞。Redis可以使用非阻塞的方式写入数据,以便处理其他事件。 27 | * 28 | * 2. 多路复用: 29 | * 多路复用是一种I/O多路复用技术,用于同时监视多个套接字上的事件。Redis使用多路复用技术来管理多个套接字上的事件,以实现高效的事件驱动模型。下面是多路复用的实现原理: 30 | * 31 | * - 注册事件和套接字:Redis将需要监听的套接字注册到多路复用器中,并指定感兴趣的事件类型,如可读、可写或异常。 32 | * 33 | * - 多路复用器监听事件:多路复用器负责监听所有已注册套接字上的事件。它使用系统调用(如select、poll或epoll)来监视套接字上的事件状态。 34 | * 35 | * - 事件就绪通知:当套接字上的事件状态发生变化时,多路复用器会通知Redis。这样,Redis可以及时处理事件。 36 | * 37 | * - 事件处理:一旦事件就绪,Redis会调用相应的事件处理函数来处理事件。例如,如果有可读事件就绪,Redis会调用读取数据的函数来处理请求。 38 | * 39 | * 通过非阻塞式I/O和多路复用的组合,Redis实现了高效的事件驱动模型。非阻塞式I/O使得Redis能够在进行读取或写入操作时不会阻塞线程,提高了并发处理能力。多路复用技术 40 | * 41 | * 允许Redis同时监听多个套接字上的事件,提供高效的事件管理和触发能力。这种组合使得Redis能够处理大量并发请求,提供高性能和低延迟的服务。 42 | * 43 | * 44 | * 45 | */ 46 | public class Q3 { 47 | /** 48 | * 使用Java的Selector类实现了非阻塞式I/O和多路复用的原理: 49 | * @param args 50 | * @throws IOException 51 | */ 52 | public static void main(String[] args) throws IOException { 53 | ServerSocketChannel serverChannel = ServerSocketChannel.open(); 54 | serverChannel.configureBlocking(false); 55 | serverChannel.bind(new InetSocketAddress(8888)); 56 | 57 | Selector selector = Selector.open(); 58 | serverChannel.register(selector, SelectionKey.OP_ACCEPT); 59 | 60 | while (true) { 61 | selector.select(); 62 | 63 | Iterator iterator = selector.selectedKeys().iterator(); 64 | while (iterator.hasNext()) { 65 | SelectionKey key = iterator.next(); 66 | iterator.remove(); 67 | 68 | if (key.isAcceptable()) { 69 | ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); 70 | SocketChannel clientChannel = serverSocketChannel.accept(); 71 | clientChannel.configureBlocking(false); 72 | clientChannel.register(selector, SelectionKey.OP_READ); 73 | } else if (key.isReadable()) { 74 | SocketChannel clientChannel = (SocketChannel) key.channel(); 75 | ByteBuffer buffer = ByteBuffer.allocate(1024); 76 | int bytesRead = clientChannel.read(buffer); 77 | if (bytesRead > 0) { 78 | buffer.flip(); 79 | byte[] data = new byte[buffer.remaining()]; 80 | buffer.get(data); 81 | String message = new String(data); 82 | System.out.println("Received message: " + message); 83 | } 84 | clientChannel.close(); 85 | } 86 | } 87 | } 88 | } 89 | } 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/main/java/com/interview/redis/Q2.java: -------------------------------------------------------------------------------- 1 | package com.interview.redis; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | 6 | /** 7 | * @Author qcl 8 | * @Description 9 | * @Date 3:32 PM 5/16/2023 10 | * 11 | * @请详细讲一下redis的线程模型并讲出实现原理?? 12 | * 13 | * Redis的线程模型采用的是单线程的事件循环模型,也称为I/O多路复用模型。下面将详细介绍Redis的线程模型和实现原理: 14 | * 15 | * 1. 单线程模型: 16 | * Redis使用单线程来处理所有的客户端请求和数据库操作。这意味着Redis在任何给定的时间只能处理一个请求,但通过事件循环机制,它能够高效地处理大量并发请求。 17 | * 18 | * 2. 事件循环: 19 | * Redis使用事件循环来实现高并发和低延迟的性能。事件循环是通过I/O多路复用技术实现的,通常使用select、poll或epoll等系统调用。Redis通过监听套接字上的事件,如可读、可写或异常事件,来感知和处理客户端请求。 20 | * 21 | * 3. 非阻塞I/O: 22 | * Redis使用非阻塞I/O来实现事件循环。在接收到客户端请求时,Redis不会阻塞等待请求完成,而是立即返回到事件循环并继续处理其他请求。当请求的I/O操作完成时,Redis会通过回调函数来处理响应数据。 23 | * 24 | * 4. 文件事件: 25 | * Redis使用文件事件来表示与客户端或其他网络连接相关的事件。每当有新的客户端连接或数据到达时,Redis会生成相应的文件事件并将其加入到事件队列中。事件循环会从队列中获取文件事件并处理它们。 26 | * 27 | * 5. 时间事件: 28 | * Redis还支持时间事件,用于执行定时任务。时间事件可以是一次性的,也可以是周期性的。通过时间事件,Redis可以执行诸如过期键的清理、统计信息的更新等后台任务。 29 | * 30 | * 6. 非阻塞操作: 31 | * Redis的数据存储是基于内存的,因此读写操作通常是非阻塞的。这使得Redis能够在快速的内存访问下提供高性能的读写操作。 32 | * 33 | * 7. 多个数据库: 34 | * Redis支持多个数据库,每个数据库都有自己的键值空间。在单线程模型下,Redis使用字典来存储数据库,通过索引来快速访问不同的数据库。 35 | * 36 | * 通过以上的线程模型,Redis能够高效地处理大量的并发请求,而无需为每个请求创建线程或进程。单线程模型可以避免线程切换和同步开销,提供较低的延迟和较高的吞吐量。同时,Redis通过使用非阻塞I/O和事件驱动的方式,实现了高效的事件处理和资源利用。 37 | 38 | * 39 | */ 40 | public class Q2 { 41 | public static void main(String[] args) throws InterruptedException { 42 | RedisTaskQueue taskQueue = new RedisTaskQueue(); 43 | RedisEventLoop eventLoop = new RedisEventLoop(taskQueue); 44 | RedisClient client = new RedisClient(taskQueue); 45 | 46 | // 启动事件处理器线程 47 | Thread eventLoopThread = new Thread(eventLoop); 48 | eventLoopThread.start(); 49 | 50 | // 模拟客户端发送命令 51 | client.sendCommand("SET key1 value1"); 52 | client.sendCommand("GET key1"); 53 | client.sendCommand("DEL key1"); 54 | 55 | // 等待所有命令执行完成 56 | eventLoopThread.join(); 57 | 58 | // Executing Redis command: SET key1 value1 59 | // Executing Redis command: GET key1 60 | // Executing Redis command: DEL key1 61 | } 62 | } 63 | 64 | 65 | // 模拟Redis命令的执行任务 66 | class RedisCommandTask { 67 | private String command; 68 | 69 | public RedisCommandTask(String command) { 70 | this.command = command; 71 | } 72 | 73 | public void execute() { 74 | // 模拟命令的执行 75 | System.out.println("Executing Redis command: " + command); 76 | } 77 | } 78 | 79 | // 模拟Redis的任务队列 80 | class RedisTaskQueue { 81 | private Queue queue = new LinkedList<>(); 82 | 83 | public synchronized void enqueue(RedisCommandTask task) { 84 | queue.add(task); 85 | notify(); // 通知等待中的线程有新任务 86 | } 87 | 88 | public synchronized RedisCommandTask dequeue() throws InterruptedException { 89 | while (queue.isEmpty()) { 90 | wait(); // 如果队列为空,则等待新任务的到来 91 | } 92 | return queue.poll(); 93 | } 94 | } 95 | 96 | 97 | 98 | // 模拟Redis的事件处理器 99 | class RedisEventLoop implements Runnable { 100 | private RedisTaskQueue taskQueue; 101 | 102 | public RedisEventLoop(RedisTaskQueue taskQueue) { 103 | this.taskQueue = taskQueue; 104 | } 105 | 106 | @Override 107 | public void run() { 108 | while (true) { 109 | try { 110 | RedisCommandTask task = taskQueue.dequeue(); 111 | task.execute(); 112 | } catch (InterruptedException e) { 113 | Thread.currentThread().interrupt(); 114 | break; 115 | } 116 | } 117 | } 118 | } 119 | 120 | // 模拟Redis的客户端 121 | class RedisClient { 122 | private RedisTaskQueue taskQueue; 123 | 124 | public RedisClient(RedisTaskQueue taskQueue) { 125 | this.taskQueue = taskQueue; 126 | } 127 | 128 | public void sendCommand(String command) { 129 | RedisCommandTask task = new RedisCommandTask(command); 130 | taskQueue.enqueue(task); 131 | } 132 | } 133 | 134 | 135 | 136 | //继续上述代码的解释: 137 | // 138 | //在示例中,使用`synchronized`关键字实现了任务队列的线程安全性。`enqueue()`方法用于将任务添加到队列中,并使用`notify()`通知等待中的线程有新任务可执行。`dequeue()`方法则在队列为空时调用`wait()`进入等待状态,直到有新任务到来时被唤醒。 139 | // 140 | //事件处理器线程在`start()`方法中通过循环不断从任务队列中取出任务并执行。如果队列为空,线程会调用`wait()`方法进入等待状态,直到有新任务到来。这样保证了事件处理器在没有任务时不会空转消耗CPU资源。 141 | // 142 | //主程序中创建了一个`RedisClient`实例,并通过`sendCommand()`方法模拟了客户端发送命令的过程。每个命令都被封装成`RedisCommandTask`对象,并通过任务队列传递给事件处理器线程执行。这种方式模拟了Redis的非阻塞命令执行,即命令被添加到任务队列后,客户端可以继续发送其他命令而无需等待命令执行完成。 143 | // 144 | //通过这个简单的示例,我们可以看到单线程模型的特点:Redis使用单线程顺序执行命令,通过任务队列和事件处理器实现命令的异步执行。这种模型的优势在于避免了多线程之间的竞争和同步开销,简化了代码实现,并且能够充分利用内存访问的高效性能。 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java相关面试题合集(每日更新八股文教程) 2 | 3 | **本着把自己知道的都告诉大家,如果本文对您有所帮助,`点赞+关注`鼓励一下呗~** 4 | 5 | 6 | ## 相关文章 7 | 8 | - [查漏补缺第一期(Redis相关)](https://mp.weixin.qq.com/s/ETAVvqwU3ZkB0MDtQ8hBAg) 9 | - [查漏补缺第二期(synchronized & 锁升级)](https://mp.weixin.qq.com/s/KbVOq7CKza8Pdp1DDPjD5A) 10 | - [查漏补缺第三期(分布式事务相关)](https://mp.weixin.qq.com/s/tejPBx-huaDMYRFeWzH2jA) 11 | - [查漏补缺第四期(Mysql相关)](https://mp.weixin.qq.com/s/DsNWgaPYrgE7QqPgPjuEcA) 12 | - [查漏补缺第五期(HashMap & ConcurrentHashMap)](https://mp.weixin.qq.com/s/4CDkuH2w8Fe0acI-xW-5JA) 13 | - [查漏补缺第六期(京东一面)](https://mp.weixin.qq.com/s/AhQZABtVjIxXs4CVhDaPbg) 14 | - [查漏补缺第七期(美团到店一面)](https://mp.weixin.qq.com/s/jongfptIxQINp6HdrfiGUw) 15 | - [查漏补缺第八期(阿里一面)](https://mp.weixin.qq.com/s/A9mIydMAt_rGM1w9-tYe6A) 16 | - [查漏补缺第九期(阿里二面)](https://mp.weixin.qq.com/s/IJLWdKu4brmfQ2BZgApbtQ) 17 | - [查漏补缺第十期(网易实习一面)](https://mp.weixin.qq.com/s/T1mNDir9y8tcHNXOKQ0nVA) 18 | - [查漏补缺第十一期(网易实习二面)](https://mp.weixin.qq.com/s/kRdF0X_CDPI46GXkMTHquQ) 19 | - [查漏补缺第十二期(网易实习三面)](https://mp.weixin.qq.com/s/Dy7shhrcrBpoZFn8wN17zw) 20 | - [查漏补缺第十三期(滴滴实习一面)](https://mp.weixin.qq.com/s/B6Myjth7nd-ieBtCV7Q0-Q) 21 | - [查漏补缺第十四期(滴滴实习二面)](https://mp.weixin.qq.com/s/G-hmZJDTY0gr4qvrbRBGyg) 22 | - [查漏补缺第十五期(华为一面)](https://mp.weixin.qq.com/s/lio5r9YlX1UKqkeBrZLEeg) 23 | - [查漏补缺第十六期(华为二面)](https://mp.weixin.qq.com/s/7jqkMzY4h5zcWBRQCZOM9A) 24 | - [查漏补缺第十七期(华为三面)](https://mp.weixin.qq.com/s/IvNY9_Iz4uwAoT1vLvjogw) 25 | - [查漏补缺第十八期(你了解class文件吗)](https://mp.weixin.qq.com/s/lLsgqQcNNWJ_Q6xyUywXYw) 26 | 27 | 28 | 29 | ## 项目源码(源码已更新 欢迎star⭐️) 30 | 31 | - [java-interview-all: https://github.com/qiuChengleiy/java-interview-all](https://github.com/qiuChengleiy/java-interview-all) 32 | 33 | 34 | ## 往期设计模式相关文章 35 | 36 | - [一起来学设计模式之认识设计模式](https://mp.weixin.qq.com/s/1sFPpYKMYyRvKzU0EFse1A) 37 | - [一起来学设计模式之单例模式](https://mp.weixin.qq.com/s/9K6LxQoJs11iBNv9kzbDyQ) 38 | - [一起来学设计模式之工厂模式](https://mp.weixin.qq.com/s/E1ish-KysPsS5hjyzxkTow) 39 | - [一起来学设计模式之建造者模式](https://mp.weixin.qq.com/s/a-rTYNwnZAYPeEQQ0vuoUg) 40 | - [一起来学设计模式之原型模式](https://mp.weixin.qq.com/s/u9hJuZeUFEqyxdj7EP4NLA) 41 | - [一起来学设计模式之适配器模式](https://mp.weixin.qq.com/s/bazzKTQ08Tny4OdrQmaF8w) 42 | - [一起来学设计模式之桥接模式](https://mp.weixin.qq.com/s/jsh5oovdFG8W7PdJ4KQWRQ) 43 | - [一起来学设计模式之组合模式](https://mp.weixin.qq.com/s/Xm0HLLZSuaN6xRBSIu1bUA) 44 | - [一起来学设计模式之装饰器模式](https://mp.weixin.qq.com/s/U-93tjfB4OW0iMdFe0ziUg) 45 | - [一起来学设计模式之外观模式](https://mp.weixin.qq.com/s/9RzFRZT3KA5z4GahV1j6aw) 46 | - [一起来学设计模式之享元模式](https://mp.weixin.qq.com/s/0K0U7cUlgsxSxFJdde8E7A) 47 | - [一起来学设计模式之代理模式](https://mp.weixin.qq.com/s/hBp0fJ83Y9ijSnoraqlyIA) 48 | - [一起来学设计模式之责任链模式](https://mp.weixin.qq.com/s/552JCyXhNcxqOIrVC3OJWQ) 49 | - [一起来学设计模式之命令模式](https://mp.weixin.qq.com/s/9ENTuLqpmwSfUSc9Qe1vqQ) 50 | - [一起来学设计模式之解释器模式](https://mp.weixin.qq.com/s/UMi7GX9dOXYXgaxPCMuKgQ) 51 | - [一起来学设计模式之迭代器模式](https://mp.weixin.qq.com/s/ql_jDSK9LNHWCyhQ6uK6aQ) 52 | - [一起来学设计模式之中介者模式](https://mp.weixin.qq.com/s/_8j1QFYZWTiFEP5qz0rHfw) 53 | - [一起来学设计模式之备忘录模式](https://mp.weixin.qq.com/s/lUiySMrzx2hdmSsHMJPgpA) 54 | - [一起来学设计模式之观察者模式](https://mp.weixin.qq.com/s/THiXHvZG0W-qYU-EjUT1Nw) 55 | - [一起来学设计模式之状态模式](https://mp.weixin.qq.com/s/6k-HQCsGCTBBI9ie3eKk9A) 56 | - [一起来学设计模式之策略模式](https://mp.weixin.qq.com/s/pjbtiLss4TDGsWeXBQGU0A) 57 | - [一起来学设计模式之模板方法模式](https://mp.weixin.qq.com/s/QKWfGkot27sutkDD2-epww) 58 | - [一起来学设计模式之访问者模式](https://mp.weixin.qq.com/s/W8saETmyLRsV6LfvPxSxXQ) 59 | - [一起来学设计模式之依赖注入模式](https://mp.weixin.qq.com/s/tI0JxsgDKIz8Kfd--HcQEg) 60 | 61 | 62 | ## 设计模式项目源码(源码已更新 欢迎star⭐️) 63 | - [java-design-mode-all: https://github.com/qiuChengleiy/java-design-mode-all](https://github.com/qiuChengleiy/java-design-mode-all) 64 | 65 | 66 | ## Kafka 专题学习 67 | 68 | - [一起来学kafka之Kafka集群搭建](https://mp.weixin.qq.com/s/XV2GUwz8mKSNmdI5YWwYVg) 69 | - [一起来学kafka之整合SpringBoot基本使用](https://mp.weixin.qq.com/s/wgE21lrcjEYWP5-2kQeACA) 70 | - [一起来学kafka之整合SpringBoot深入使用(一)](https://mp.weixin.qq.com/s/fI4KNz497qUYueWuOkoUNA) 71 | - [一起来学kafka之整合SpringBoot深入使用(二)](https://mp.weixin.qq.com/s/xLkVTRn2OLcp9hblvw3Xvg) 72 | - [一起来学kafka之整合SpringBoot深入使用(三)](https://mp.weixin.qq.com/s/8iVJ8nyRIpF1gnql1X6yVg) 73 | 74 | ## 项目源码(源码已更新 欢迎star⭐️) 75 | 76 | - [springboot-kafka-all: https://github.com/qiuChengleiy/springboot-kafka-all](https://github.com/qiuChengleiy/springboot-kafka-all) 77 | 78 | ## ElasticSearch 专题学习 79 | 80 | - [利用docker搭建es集群](https://mp.weixin.qq.com/s/Z6HhZWNbmUrp4kRn5EX3FA) 81 | - [一起来学ElasticSearch(一)](https://mp.weixin.qq.com/s/UXDc2mmWlXHhn3R3D0rong) 82 | - [一起来学ElasticSearch(二)](https://mp.weixin.qq.com/s/BBidY95hm1_sGNYYbJT10g) 83 | - [一起来学ElasticSearch(三)](https://mp.weixin.qq.com/s/ItBixu4IN2vrEoFBuTIiMA) 84 | - [一起来学ElasticSearch(四)](https://mp.weixin.qq.com/s/AWEjVAzFS76IDUcuoIJUFw) 85 | - [一起来学ElasticSearch(五)](https://mp.weixin.qq.com/s/3xdqocmPmNRhmVl5C_FIOg) 86 | - [一起来学ElasticSearch(六)](https://mp.weixin.qq.com/s/RJgZCA04v4bzLEbZn-Rc2Q) 87 | - [一起来学ElasticSearch(七)](https://mp.weixin.qq.com/s/bZqDMPg-3yC7dI3hIuOLcA) 88 | - [一起来学ElasticSearch(八)](https://mp.weixin.qq.com/s/JhkCoI6sNTvwlkRmS5psHg) 89 | - [一起来学ElasticSearch(九)](https://mp.weixin.qq.com/s/rABSZhegOdQL7tEvF2bT0g) 90 | - [一起来学ElasticSearch(十)](https://mp.weixin.qq.com/s/rCIoJkVpqcJpDJ0molBaGw) 91 | 92 | - [一起来学ElasticSearch之整合SpringBoot(一)](https://mp.weixin.qq.com/s/Y1wiGPam81iD26KCu2bWKw) 93 | - [一起来学ElasticSearch之整合SpringBoot(二)](https://mp.weixin.qq.com/s/gJ4ueNKr0RqYVe7541gbGA) 94 | - [一起来学ElasticSearch之整合SpringBoot(三)](https://mp.weixin.qq.com/s/ngKMEzZJ59FiR4xnEzz-pA) 95 | 96 | ## 项目源码(源码已更新 欢迎star⭐️) 97 | 98 | - [springboot-es-all: https://github.com/qiuChengleiy/springboot-es-all](https://github.com/qiuChengleiy/springboot-es-all) 99 | 100 | ## 往期并发编程内容推荐 101 | 102 | - [Java多线程专题之线程与进程概述](https://mp.weixin.qq.com/s/PvFx7mm46bsFl94IUWZMUw) 103 | - [Java多线程专题之线程类和接口入门](https://mp.weixin.qq.com/s/Uze3brfNfqMg8eGUqU0lHg) 104 | - [Java多线程专题之进阶学习Thread(含源码分析)](https://mp.weixin.qq.com/s/R9MUmSmEF3HvNAV441rmrw) 105 | - [Java多线程专题之Callable、Future与FutureTask(含源码分析)](https://mp.weixin.qq.com/s/qlKTI3VXBJfypy6XvFo0cg) 106 | - [面试官: 有了解过线程组和线程优先级吗](https://mp.weixin.qq.com/s/obLO_Bmq9Uuy0VuF9z7NeA) 107 | - [面试官: 说一下线程的生命周期过程](https://mp.weixin.qq.com/s/LsyduaUkTdeTaZ3S3phAIw) 108 | - [面试官: 说一下线程间的通信](https://mp.weixin.qq.com/s/oKYUxw01YasA-sMFH8W3GQ) 109 | - [面试官: 说一下Java的共享内存模型](https://mp.weixin.qq.com/s/-n0x_Amt4t4V30IIXj0FnA) 110 | - [面试官: 有了解过指令重排吗,什么是happens-before](https://mp.weixin.qq.com/s/3nGVYKxaavweU40da96OMg) 111 | - [面试官: 有了解过volatile关键字吗 说说看](https://mp.weixin.qq.com/s/uSDAw_X3R7X-f3TvqXDuzg) 112 | - [面试官: 有了解过Synchronized吗 说说看](https://mp.weixin.qq.com/s/pY3A2iWb0derRXY2tx3SiQ) 113 | - [Java多线程专题之Lock锁的使用](https://mp.weixin.qq.com/s/KtS0cnnWQZItcwGKYxJ6pw) 114 | - [面试官: 有了解过ReentrantLock的底层实现吗?说说看](https://mp.weixin.qq.com/s/IErZZadVkqazjvUOibi-eQ) 115 | - [面试官: 有了解过CAS和原子操作吗?说说看](https://mp.weixin.qq.com/s/Waw9C4QHWqOH5nOoHT6sEQ) 116 | - [Java多线程专题之线程池的基本使用](https://mp.weixin.qq.com/s/7PHqofkjX_L4kHU1agTCfg) 117 | - [面试官: 有了解过线程池的工作原理吗?说说看](https://mp.weixin.qq.com/s/mTrV4tr70zT-umtWtyyIiQ) 118 | - [面试官: 线程池是如何做到线程复用的?有了解过吗,说说看](https://mp.weixin.qq.com/s/t1xU4rJFsHjzF9uWkXFjGA) 119 | - [面试官: 阻塞队列有了解过吗?说说看](https://mp.weixin.qq.com/s/SeF4BnGzqg2ossG2fhlU_g) 120 | - [面试官: 阻塞队列的底层实现有了解过吗? 说说看](https://mp.weixin.qq.com/s/zcc10wGeV0AkfH9OUmjL9g) 121 | - [面试官: 同步容器和并发容器有用过吗? 说说看](https://mp.weixin.qq.com/s/4BbePlrfDbyOR7V8TK6nfw) 122 | - [面试官: CopyOnWrite容器有了解过吗? 说说看](https://mp.weixin.qq.com/s/lE0BErDXLIFUGSiM-FWmPg) 123 | - [面试官: Semaphore在项目中有使用过吗?说说看(源码剖析)](https://mp.weixin.qq.com/s/x_q0FQqsmP5ojQF4e6PXAg) 124 | - [面试官: Exchanger在项目中有使用过吗?说说看(源码剖析)](https://mp.weixin.qq.com/s/4Ik4z_8gfzftLDb0FFW6tw) 125 | - [面试官: CountDownLatch有了解过吗?说说看(源码剖析)](https://mp.weixin.qq.com/s/nABrvJ0A5NL0OqF_J6AIlA) 126 | - [面试官: CyclicBarrier有了解过吗?说说看(源码剖析)](https://mp.weixin.qq.com/s/2Zoh3jUYnnL9TTj0kw8q3w) 127 | - [面试官: Phaser有了解过吗?说说看](https://mp.weixin.qq.com/s/rAAc1UH-iP6ox4LpmBJvEQ) 128 | - [面试官: Fork/Join 有了解过吗?说说看(含源码分析)](https://mp.weixin.qq.com/s/YESb3PH12jHAn8HSs_yQ9g) 129 | - [面试官: Stream并行流有了解过吗?说说看](https://mp.weixin.qq.com/s/Nmexsl9RrHZudRBrU0b1FA) 130 | 131 | 132 | ## 推荐 SpringBoot & SpringCloud (源码已更新 欢迎star⭐️) 133 | 134 | - [springboot-all](https://github.com/qiuChengleiy/springboot-all.git) 135 | 136 | - ```地址```: https://github.com/qiuChengleiy/springboot-all.git 137 | 138 | - [SpringBoot系列教程合集](https://mp.weixin.qq.com/s/FOTDTwNcVlW5yxSpwBf-aA) 139 | 140 | - [一起来学SpringCloud合集](https://mp.weixin.qq.com/s/au2e3uXoCRAlEHHnoop2Cw) 141 | 142 | - [SpringCloud整合 Oauth2+Gateway+Jwt+Nacos 实现授权码模式的服务认证(一)](https://mp.weixin.qq.com/s/TCHLaTywd2C_oSqUmZL6lw) 143 | - [SpringCloud整合 Oauth2+Gateway+Jwt+Nacos 实现授权码模式的服务认证(二)](https://mp.weixin.qq.com/s/dlX1pXIT19nJWN6pvH3tgw) 144 | 145 | 146 | 147 | ## 博客(阅读体验较佳) 148 | 149 | - [我的博客(阅读体验较佳)](https://www.qiuchenglei.top) 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | --------------------------------------------------------------------------------