diff --git "a/src/main/java/algorithm_practice/LeetCode/code000/E001_\344\270\244\346\225\260\344\271\213\345\222\214.java" "b/src/main/java/algorithm_practice/LeetCode/code000/E001_\344\270\244\346\225\260\344\271\213\345\222\214.java" index 7c684133..8c7225e3 100644 --- "a/src/main/java/algorithm_practice/LeetCode/code000/E001_\344\270\244\346\225\260\344\271\213\345\222\214.java" +++ "b/src/main/java/algorithm_practice/LeetCode/code000/E001_\344\270\244\346\225\260\344\271\213\345\222\214.java" @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Optional; /* diff --git "a/src/main/java/algorithm_practice/LeetCode/code000/E002_\344\270\244\346\225\260\347\233\270\345\212\240.java" "b/src/main/java/algorithm_practice/LeetCode/code000/E002_\344\270\244\346\225\260\347\233\270\345\212\240.java" new file mode 100644 index 00000000..3aa4a82d --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code000/E002_\344\270\244\346\225\260\347\233\270\345\212\240.java" @@ -0,0 +1,30 @@ +package algorithm_practice.LeetCode.code000; + +import common.datastruct.ListNode; + +public class E002_两数相加 { + /* + 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。 + +如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 + +您可以假设除了数字 0 之外,这两个数都不会以 0 开头。 + +示例: + +输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) +输出:7 -> 0 -> 8 +原因:342 + 465 = 807 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/add-two-numbers +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + public static void main(String[] args) { + + } + + public static ListNode addTwoNumbers(ListNode l1, ListNode l2) { + return l1; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code000/H032_\346\234\200\351\225\277\346\234\211\346\225\210\346\213\254\345\217\267.java" "b/src/main/java/algorithm_practice/LeetCode/code000/H032_\346\234\200\351\225\277\346\234\211\346\225\210\346\213\254\345\217\267.java" new file mode 100644 index 00000000..a9c0077e --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code000/H032_\346\234\200\351\225\277\346\234\211\346\225\210\346\213\254\345\217\267.java" @@ -0,0 +1,59 @@ +package algorithm_practice.LeetCode.code000; + +import java.util.Stack; + +public class H032_最长有效括号 { + + /* +给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。 + +示例 1: + +输入: "(()" +输出: 2 +解释: 最长有效括号子串为 "()" +示例 2: + +输入: ")()())" +输出: 4 +解释: 最长有效括号子串为 "()()" + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/longest-valid-parentheses +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + public static void main(String[] args) { + H032_最长有效括号 obj = new H032_最长有效括号(); + assert 2 == obj.longestValidParentheses("(()"); + assert 4 == obj.longestValidParentheses(")()())"); + assert 6 == obj.longestValidParentheses("(()())"); + } + + /* + 思路:遇到(,当前坐标入栈。 + 遇到),pop栈内的元素,如果pop之后栈为空,就把当前坐标入栈。如果pop之后不为空,就在max和(当前坐标 - 栈顶元素)之间取最大值。 + 为了计算的正确性,一开始把-1 push到栈中。 + 时间复杂度O(n),空间复杂度O(n) + 这样栈最底下的元素一定是上一次没能进行左匹配的右括号的坐标 + */ + public int longestValidParentheses(String s) { + char[] c = s.toCharArray(); + + Stack stack = new Stack<>(); + stack.push(-1); + int maxans = 0; + for (int i = 0; i < c.length; i++) { + if (c[i] == '(') { + stack.push(i); + } else { + stack.pop(); + if (stack.isEmpty()) { + stack.push(i); + } else { + maxans = Math.max(maxans, i - stack.peek()); + } + } + } + return maxans; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code000/H044_\351\200\232\351\205\215\347\254\246\345\214\271\351\205\215.java" "b/src/main/java/algorithm_practice/LeetCode/code000/H044_\351\200\232\351\205\215\347\254\246\345\214\271\351\205\215.java" new file mode 100644 index 00000000..cdf212d2 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code000/H044_\351\200\232\351\205\215\347\254\246\345\214\271\351\205\215.java" @@ -0,0 +1,103 @@ +package algorithm_practice.LeetCode.code000; + +public class H044_通配符匹配 { + /* +给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?' 和 '*' 的通配符匹配。 + +'?' 可以匹配任何单个字符。 +'*' 可以匹配任意字符串(包括空字符串)。 +两个字符串完全匹配才算匹配成功。 + +说明: + +s 可能为空,且只包含从 a-z 的小写字母。 +p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。 +示例 1: + +输入: +s = "aa" +p = "a" +输出: false +解释: "a" 无法匹配 "aa" 整个字符串。 +示例 2: + +输入: +s = "aa" +p = "*" +输出: true +解释: '*' 可以匹配任意字符串。 +示例 3: + +输入: +s = "cb" +p = "?a" +输出: false +解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。 +示例 4: + +输入: +s = "adceb" +p = "*a*b" +输出: true +解释: 第一个 '*' 可以匹配空字符串, 第二个 '*' 可以匹配字符串 "dce". +示例 5: + +输入: +s = "acdcb" +p = "a*c?b" +输出: false + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/wildcard-matching +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public static void main(String[] args) { + H044_通配符匹配 obj = new H044_通配符匹配(); + assert obj.isMatch("aa", "*"); + assert !obj.isMatch("aa", "a"); + assert !obj.isMatch("cb", "*a"); + assert obj.isMatch("adceb", "*a*b"); + assert !obj.isMatch("acdcb", "a*c?b"); + } + + /* + 1.动态规划 +状态转移方程: +模式p[j] != '*'且 p[j] != '?'时, dp[i][j] = (p[j] == s[i]) && dp[i-1][j-1] +模式p[j] == '?'时,dp[i][j] =dp[i-1][j-1] +模式p[j] == '*'时,*可以匹配0个词,也可以匹配1个词并且*还存在,这样下次计算*还存在的时候,*还可以匹配0或1个词,以此类推*可以匹配0或无限多个字。 +所以这时候dp[i][j] = dp[i-1][j] (这时候匹配了一个词) || dp[i][j-1](这时候匹配了0个词) +边界条件: +为了满足某一方为空的场景,所以dp数组的大小是s.length+1行,p.length+1列。 +其中dp[0][0]是两个空串匹配场景,所以dp[0][0] = true; +而空的p模式无法与任何非空字符串匹配,所以dp[any][0] = false; +*可以匹配0个字母,所以dp[0][从头到尾最后一个连续的*的位置] = true,其余为false。 + */ + public boolean isMatch(String s, String p) { + int sl = s.length(); + int pl = p.length(); + boolean[][] dp = new boolean[sl + 1][pl + 1]; + + // 初始化dp数组边界条件 + dp[0][0] = true; + for (int i = 1; i <= pl; i++) { + if (p.charAt(i - 1) == '*') { + dp[0][i] = true; + } else { + break; + } + } + + for (int i = 1; i <= sl; i++) { + for (int j = 1; j <= pl; j++) { + if (p.charAt(j - 1) == '*') { + dp[i][j] = dp[i - 1][j] || dp[i][j - 1]; + } else if (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '?') { + dp[i][j] = dp[i - 1][j - 1]; + } + } + } + return dp[sl][pl]; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code000/H097_\344\272\244\351\224\231\345\255\227\347\254\246\344\270\262.java" "b/src/main/java/algorithm_practice/LeetCode/code000/H097_\344\272\244\351\224\231\345\255\227\347\254\246\344\270\262.java" new file mode 100644 index 00000000..473c68e0 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code000/H097_\344\272\244\351\224\231\345\255\227\347\254\246\344\270\262.java" @@ -0,0 +1,65 @@ +package algorithm_practice.LeetCode.code000; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +public class H097_交错字符串 { + /* + 给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的。 + +示例 1: + +输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac" +输出: true +示例 2: + +输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc" +输出: false + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/interleaving-string +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public static void main(String[] args) { + H097_交错字符串 obj = new H097_交错字符串(); + System.out.println(obj.isInterleave("aabcc", "dbbca", "aadbbcbcac")); + System.out.println(obj.isInterleave("aabcc", "dbbca", "aadbbbaccc")); + } + + public boolean isInterleave(String s1, String s2, String s3) { + int m = s1.length(); + int n = s2.length(); + int p = s3.length(); + + if (n + m != p) { + // directly fail + return false; + } + + // init dp array + boolean[][] dp = new boolean[m + 1][n + 1]; + dp[0][0] = true; + + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + int l = i + j - 1; + if (i > 0) { + dp[i][j] = dp[i][j] || (dp[i - 1][j] && s1.charAt(i - 1) == s3.charAt(l)); + } + if (j > 0) { + dp[i][j] = dp[i][j] || (dp[i][j - 1] && s2.charAt(j - 1) == s3.charAt(l)); + } + } + } + return dp[m][n]; + } + +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code000/M015_\344\270\211\346\225\260\344\271\213\345\222\214.java" "b/src/main/java/algorithm_practice/LeetCode/code000/M015_\344\270\211\346\225\260\344\271\213\345\222\214.java" index c2484e09..13d5fbf4 100644 --- "a/src/main/java/algorithm_practice/LeetCode/code000/M015_\344\270\211\346\225\260\344\271\213\345\222\214.java" +++ "b/src/main/java/algorithm_practice/LeetCode/code000/M015_\344\270\211\346\225\260\344\271\213\345\222\214.java" @@ -1,7 +1,6 @@ package algorithm_practice.LeetCode.code000; import com.alibaba.fastjson.JSON; -import com.sun.tools.doclint.Entity; import junit.framework.TestCase; import org.junit.Test; diff --git "a/src/main/java/algorithm_practice/LeetCode/code000/M056_\345\220\210\345\271\266\345\214\272\351\227\264.java" "b/src/main/java/algorithm_practice/LeetCode/code000/M056_\345\220\210\345\271\266\345\214\272\351\227\264.java" index 8b8663c3..345cb6fb 100644 --- "a/src/main/java/algorithm_practice/LeetCode/code000/M056_\345\220\210\345\271\266\345\214\272\351\227\264.java" +++ "b/src/main/java/algorithm_practice/LeetCode/code000/M056_\345\220\210\345\271\266\345\214\272\351\227\264.java" @@ -3,7 +3,6 @@ import common.util.SysOut; import junit.framework.TestCase; import org.junit.Test; -import sun.jvm.hotspot.utilities.BitMap; import java.util.ArrayList; import java.util.Arrays; @@ -47,8 +46,8 @@ public void testCase() { int[][] intervals5 = new int[][]{{1, 4}, {2, 3}}; SysOut.printArray(merge(intervals5)); - - int[][] intervals6 = new int[][]{{2,3},{2,2},{3,3},{1,3},{5,7},{2,2},{4,6}}; + + int[][] intervals6 = new int[][]{{2, 3}, {2, 2}, {3, 3}, {1, 3}, {5, 7}, {2, 2}, {4, 6}}; SysOut.printArray(merge(intervals6)); } @@ -81,7 +80,7 @@ public int compare(int[] o1, int[] o2) { continue; } - int[] resultInterval = result.get(result.size()-1); + int[] resultInterval = result.get(result.size() - 1); if (interval[1] < resultInterval[1]) { continue; @@ -90,7 +89,7 @@ public int compare(int[] o1, int[] o2) { if (interval[0] <= resultInterval[1]) { resultInterval[1] = interval[1]; - result.remove(result.size()-1); + result.remove(result.size() - 1); result.add(resultInterval); } else { result.add(interval); @@ -99,39 +98,4 @@ public int compare(int[] o1, int[] o2) { return result.toArray(new int[0][]); } - - /** - * 使用BitMap - */ - public int[][] merge2(int[][] intervals) { - BitMap bitMap = new BitMap(100); - - for (int i = 0; i < intervals.length; i++) { - int[] interval = intervals[i]; - for (int j = interval[0]; j < interval[1]; j++) { - if (j > bitMap.size()) { - bitMap.set_size(j); - } - - bitMap.atPut(j, true); - } - } - - int[][] result = new int[1][2]; - int count = 0; - for (int i = 0; i < bitMap.size(); i++) { - if (bitMap.at(i)) { - result[count][0] = i; - while (bitMap.at(i)) { - i++; - } - - result[count][1] = i; - - count++; - } - } - - return result; - } } diff --git "a/src/main/java/algorithm_practice/LeetCode/code000/M062_\344\270\215\345\220\214\350\267\257\345\276\204.java" "b/src/main/java/algorithm_practice/LeetCode/code000/M062_\344\270\215\345\220\214\350\267\257\345\276\204.java" new file mode 100644 index 00000000..fa453d9a --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code000/M062_\344\270\215\345\220\214\350\267\257\345\276\204.java" @@ -0,0 +1,60 @@ +package algorithm_practice.LeetCode.code000; + +public class M062_不同路径 { + + /* +一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 + +机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 + +问总共有多少条不同的路径? + +示例 1: + +输入: m = 3, n = 2 +输出: 3 +解释: +从左上角开始,总共有 3 条路径可以到达右下角。 +1. 向右 -> 向右 -> 向下 +2. 向右 -> 向下 -> 向右 +3. 向下 -> 向右 -> 向右 +示例 2: + +输入: m = 7, n = 3 +输出: 28 + +提示: + +1 <= m, n <= 100 +题目数据保证答案小于等于 2 * 10 ^ 9 + */ + public static void main(String[] args) { + assert uniquePaths(3, 2) == 3; + assert uniquePaths(7, 3) == 28; + } + + /* + 1. 时间复杂度O(n^2),空间复杂度O(n^2) +状态转移方程: +机器人可以向下或者向右移动,所以数组中的某点dp[i][j]应当是dp[i-1][j] + dp[i][j-1]。 +边界条件: +到达初始位置只有一种可能,所以dp[0][0] =1。 +沿着初始的行和列行走都只有一种可能,所以dp[any][0] = 1, dp[0][any] = 1. + + */ + public static int uniquePaths(int m, int n) { + int[][] dp = new int[m][n]; + for (int i = 0; i < m; i++) { + dp[i][0] = 1; + } + for (int j = 0; j < n; j++) { + dp[0][j] = 1; + } + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + return dp[m - 1][n - 1]; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code000/M063_\344\270\215\345\220\214\350\267\257\345\276\204II.java" "b/src/main/java/algorithm_practice/LeetCode/code000/M063_\344\270\215\345\220\214\350\267\257\345\276\204II.java" new file mode 100644 index 00000000..546feae5 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code000/M063_\344\270\215\345\220\214\350\267\257\345\276\204II.java" @@ -0,0 +1,77 @@ +package algorithm_practice.LeetCode.code000; + +public class M063_不同路径II { + + /* + 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 + +机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 + +现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径? + +网格中的障碍物和空位置分别用 1 和 0 来表示。 + +说明:m 和 n 的值均不超过 100。 + +示例 1: + +输入: +[ +  [0,0,0], +  [0,1,0], +  [0,0,0] +] +输出: 2 +解释: +3x3 网格的正中间有一个障碍物。 +从左上角到右下角一共有 2 条不同的路径: +1. 向右 -> 向右 -> 向下 -> 向下 +2. 向下 -> 向下 -> 向右 -> 向右 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/unique-paths-ii +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + public static void main(String[] args) { + int[][] grid = new int[3][3]; + grid[1][1] = 1; + assert uniquePathsWithObstacles(grid) == 2; + } + + /* + 动态规划 +1.时间复杂度O(n^2),空间复杂度O(n^2) +状态转移方程: +和62题类似,但是这时候加入了block,所以dp数组的取值还和block也就是grid m,n点是否有障碍物有关。 +我们有dp[m][n] = dp[m-1][n] + dp[m][n-1] (if grid[m][n] != 1) + = 0 (if grid[m][n] == 1, blocked) +边界条件: +在初始位置同行同列中,我们可以知道在遇到block之前都是1,遇到block之后必然都是0. + + */ + public static int uniquePathsWithObstacles(int[][] obstacleGrid) { + int[][] dp = new int[obstacleGrid.length][obstacleGrid[0].length]; + for (int i = 0; i < obstacleGrid.length; i++) { + if (obstacleGrid[i][0] == 1) { + break; + } + dp[i][0] = 1; + } + for (int j = 0; j < obstacleGrid[0].length; j++) { + if (obstacleGrid[0][j] == 1) { + break; + } + dp[0][j] = 1; + } + for (int i = 1; i < obstacleGrid.length; i++) { + for (int j = 1; j < obstacleGrid[0].length; j++) { + if (obstacleGrid[i][j] == 1) { + dp[i][j] = 0; + } else { + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + } + return dp[obstacleGrid.length - 1][obstacleGrid[0].length - 1]; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code000/M064_\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" "b/src/main/java/algorithm_practice/LeetCode/code000/M064_\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" new file mode 100644 index 00000000..d7594b37 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code000/M064_\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" @@ -0,0 +1,58 @@ +package algorithm_practice.LeetCode.code000; + +public class M064_最小路径和 { + + /* + 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 + +说明:每次只能向下或者向右移动一步。 + +示例: + +输入: +[ +  [1,3,1], + [1,5,1], + [4,2,1] +] +输出: 7 +解释: 因为路径 1→3→1→1→1 的总和最小。 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/minimum-path-sum +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public static void main(String[] args) { + int[][] path = new int[][]{new int[]{1, 3, 1}, new int[]{1, 5, 1}, new int[]{4, 2, 1}}; + M064_最小路径和 obj = new M064_最小路径和(); + System.out.println(obj.minPathSum(path)); + } + + /* + 解法: +1.动态规划 +数组:动态规划数组大小dp[m][n],和arry相同大小。数组内容代表到达这点的最小路径消耗。 +转移方程:dp[i][j]的大小取决于它上面和左边元素的大小,我们有dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + array[i][j] +边界条件:dp[0][0]就是array[0][0],第一行就是累加,第一列也是累加。 + */ + public int minPathSum(int[][] grid) { + int m = grid.length; + int n = grid[0].length; + int[][] dp = new int[m][n]; + dp[0][0] = grid[0][0]; + for (int i = 1; i < m; i++) { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + for (int j = 1; j < n; j++) { + dp[0][j] = dp[0][j - 1] + grid[0][j]; + } + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]; + } + } + + return dp[m - 1][n - 1]; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code000/M095_\344\270\215\345\220\214\347\232\204\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221II.java" "b/src/main/java/algorithm_practice/LeetCode/code000/M095_\344\270\215\345\220\214\347\232\204\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221II.java" new file mode 100644 index 00000000..5c89731b --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code000/M095_\344\270\215\345\220\214\347\232\204\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221II.java" @@ -0,0 +1,86 @@ +package algorithm_practice.LeetCode.code000; + + +import common.datastruct.TreeNode; +import common.util.BTreePrinter; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class M095_不同的二叉搜索树II { + + /* + 给定一个整数 n,生成所有由 1 ... n 为节点所组成的 二叉搜索树 。 + +  + +示例: + +输入:3 +输出: +[ +  [1,null,3,2], +  [3,2,null,1], +  [3,1,null,null,2], +  [2,1,3], +  [1,null,2,null,3] +] +解释: +以上的输出对应以下 5 种不同结构的二叉搜索树: + + 1 3 3 2 1 + \ / / / \ \ + 3 2 1 1 3 2 + / / \ \ + 2 1 2 3 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/unique-binary-search-trees-ii +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + /** + * Definition for a binary tree node. public class TreeNode { int val; TreeNode left; TreeNode + * right; TreeNode() {} TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, + * TreeNode right) { this.val = val; this.left = left; this.right = right; } } + */ + + public static void main(String[] args) { + for (TreeNode treeNode : new M095_不同的二叉搜索树II().generateTrees(3)) { + BTreePrinter.printTreeNode(treeNode); + } + } + + public List generateTrees(int n) { + if (n == 0) { + return Collections.emptyList(); + } + + return generateTrees(1, n); + } + + private List generateTrees(int start, int end) { + List allTrees = new ArrayList<>(); + if (start > end) { + allTrees.add(null); + return allTrees; + } + + // for every position as root + for (int i = start; i <= end; i++) { + List leftTrees = generateTrees(start, i - 1); + List rightTrees = generateTrees(i + 1, end); + + for (TreeNode left : leftTrees) { + for (TreeNode right : rightTrees) { + TreeNode treeNode = new TreeNode(i, left, right); + allTrees.add(treeNode); + } + } + } + + return allTrees; + } + +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code100/E108_\345\260\206\346\234\211\345\272\217\346\225\260\347\273\204\350\275\254\346\215\242\344\270\272\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.java" "b/src/main/java/algorithm_practice/LeetCode/code100/E108_\345\260\206\346\234\211\345\272\217\346\225\260\347\273\204\350\275\254\346\215\242\344\270\272\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.java" new file mode 100644 index 00000000..9f6e2e99 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code100/E108_\345\260\206\346\234\211\345\272\217\346\225\260\347\273\204\350\275\254\346\215\242\344\270\272\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.java" @@ -0,0 +1,53 @@ +package algorithm_practice.LeetCode.code100; + +import common.datastruct.TreeNode; +import common.util.BTreePrinter; + +/* + +将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。 + +本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。 + +示例: + +给定有序数组: [-10,-3,0,5,9], + +一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树: + + 0 + / \ + -3 9 + / / + -10 5 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class E108_将有序数组转换为平衡二叉树 { + + public static void main(String[] args) { + E108_将有序数组转换为平衡二叉树 obj = new E108_将有序数组转换为平衡二叉树(); + BTreePrinter.printTreeNode((obj.sortedArrayToBST(new int[]{-10,-3,0,5,9}))); + } + + public TreeNode sortedArrayToBST(int[] nums) { + return calTree(nums, 0, nums.length - 1); + } + + /* + 二分递归,每次从中间开始一分为二,中间是root。终止条件是left > right。 + 每次选择的新的根结点都是 (left + right) /2 + */ + private TreeNode calTree(int[] nums, int start, int end) { + if(start > end) { + return null; + } + int mid = (start + end) / 2; + TreeNode root = new TreeNode(nums[mid]); + root.left = calTree(nums, start, mid - 1); + root.right = calTree(nums, mid +1, end); + return root; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code100/E112_\350\267\257\345\276\204\346\200\273\345\222\214.java" "b/src/main/java/algorithm_practice/LeetCode/code100/E112_\350\267\257\345\276\204\346\200\273\345\222\214.java" new file mode 100644 index 00000000..5edc0b9a --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code100/E112_\350\267\257\345\276\204\346\200\273\345\222\214.java" @@ -0,0 +1,65 @@ +package algorithm_practice.LeetCode.code100; + +import common.datastruct.TreeNode; + +public class E112_路径总和 { + + /* + 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。 + +说明: 叶子节点是指没有子节点的节点。 + +示例:  +给定如下二叉树,以及目标和 sum = 22, + + 5 + / \ + 4 8 + / / \ + 11 13 4 + / \ \ + 7 2 1 +返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/path-sum +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + public static void main(String[] args) { + TreeNode node5 = new TreeNode(5); + TreeNode node4 = new TreeNode(4); + TreeNode node8 = new TreeNode(8); + TreeNode node11 = new TreeNode(11); + TreeNode node13 = new TreeNode(13); + TreeNode node4_2 = new TreeNode(4); + TreeNode node7 = new TreeNode(7); + TreeNode node2 = new TreeNode(2); + TreeNode node1 = new TreeNode(1); + node5.left = node4; + node5.right = node8; + node4.left = node11; + node11.left = node7; + node11.right = node2; + node8.left = node13; + node8.right = node4_2; + node4_2.right = node1; + assert true == hasPathSum(node5, 22); + } + + /* + 解法: + 1. 递归调用,转换为子树的同样问题,根节点判断。 +时间复杂度:O(N),其中 N 是树的节点数。对每个节点访问一次。 + +空间复杂度:O(H),其中 H 是树的高度。空间复杂度主要取决于递归时栈空间的开销,最坏情况下,树呈现链状,空间复杂度为 O(N)。平均情况下树的高度与节点数的对数正相关,空间复杂度为 O(logN)。 + */ + public static boolean hasPathSum(TreeNode root, int sum) { + if (root == null) { + return false; + } + if (root.left == null && root.right == null) { + return sum == root.val; + } + return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val); + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code100/E167_\344\270\244\346\225\260\344\271\213\345\222\214II\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204.java" "b/src/main/java/algorithm_practice/LeetCode/code100/E167_\344\270\244\346\225\260\344\271\213\345\222\214II\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204.java" new file mode 100644 index 00000000..04c51ef6 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code100/E167_\344\270\244\346\225\260\344\271\213\345\222\214II\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204.java" @@ -0,0 +1,49 @@ +package algorithm_practice.LeetCode.code100; + +import java.util.Arrays; + +public class E167_两数之和II输入有序数组 { + + /* + 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。 + +函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。 + +说明: + +返回的下标值(index1 和 index2)不是从零开始的。 +你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。 +示例: + +输入: numbers = [2, 7, 11, 15], target = 9 +输出: [1,2] +解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + public static void main(String[] args) { + System.out.println(Arrays.toString(twoSum(new int[]{2, 7, 11, 15}, 9))); + } + + /* + 解题思路: +1.双指针 +地位指针low,高位指针high,当arr[low] + arr[high] > target时,high --; + 当arr[low] + arr[hig] < target是,low++; + 当arr[low] + arr[high] == target时, return low , high + */ + public static int[] twoSum(int[] numbers, int target) { + int low = 0; + int high = numbers.length - 1; + while (numbers[low] + numbers[high] != target) { + if (numbers[low] + numbers[high] > target) { + high--; + } else { + low++; + } + } + return new int[]{low + 1, high + 1}; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code100/H174_\345\234\260\344\270\213\345\237\216\346\270\270\346\210\217.java" "b/src/main/java/algorithm_practice/LeetCode/code100/H174_\345\234\260\344\270\213\345\237\216\346\270\270\346\210\217.java" new file mode 100644 index 00000000..60f5b5d0 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code100/H174_\345\234\260\344\270\213\345\237\216\346\270\270\346\210\217.java" @@ -0,0 +1,61 @@ +package algorithm_practice.LeetCode.code100; + +import java.util.Arrays; + +public class H174_地下城游戏 { + + /* + 一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。 + +骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。 + +有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。 + +为了尽快到达公主,骑士决定每次只向右或向下移动一步。 + +  + +编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。 + +例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。 + +-2 (K) -3 3 +-5 -10 1 +10 30 -5 (P) +  + +说明: + +骑士的健康点数没有上限。 + +任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/dungeon-game +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + public static void main(String[] args) { + int[][] dungeon = new int[3][3]; + dungeon[0] = new int[]{-2, -3, 3}; + dungeon[1] = new int[]{-5, -10, 1}; + dungeon[2] = new int[]{10, 30, -5}; + System.out.println(calculateMinimumHP(dungeon)); + } + + public static int calculateMinimumHP(int[][] dungeon) { + int m = dungeon.length; + int n = dungeon[0].length; + int[][] dp = new int[m + 1][n + 1]; + for (int i = 0; i < m + 1; i++) { + Arrays.fill(dp[i], Integer.MAX_VALUE); + } + dp[m][n - 1] = dp[m - 1][n] = 1; + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + int minH = Math.min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]; + dp[i][j] = Math.max(minH, 1); + } + } + return dp[0][0]; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code100/M114_\344\272\214\345\217\211\346\240\221\345\261\225\345\274\200\344\270\272\351\223\276\350\241\250.java" "b/src/main/java/algorithm_practice/LeetCode/code100/M114_\344\272\214\345\217\211\346\240\221\345\261\225\345\274\200\344\270\272\351\223\276\350\241\250.java" new file mode 100644 index 00000000..2c053b57 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code100/M114_\344\272\214\345\217\211\346\240\221\345\261\225\345\274\200\344\270\272\351\223\276\350\241\250.java" @@ -0,0 +1,74 @@ +package algorithm_practice.LeetCode.code100; + +import common.datastruct.TreeNode; +import java.util.ArrayList; +import java.util.List; + +public class M114_二叉树展开为链表 { + + /* + 给定一个二叉树,原地将它展开为一个单链表。 + +  + +例如,给定二叉树 + + 1 + / \ + 2 5 + / \ \ +3 4 6 +将其展开为: + +1 + \ + 2 + \ + 3 + \ + 4 + \ + 5 + \ + 6 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + public static void main(String[] args) { + + } + + /** + * Definition for a binary tree node. public class TreeNode { int val; TreeNode left; TreeNode + * right; TreeNode() {} TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, + * TreeNode right) { this.val = val; this.left = left; this.right = right; } } + */ + /* + 解法: +先使用递归算法完成二叉树先根序遍历,结果存放在list中,然后拼凑成右子树链表。 + */ + public void flatten(TreeNode root) { + List tnList = new ArrayList<>(); + preOrderTravel(root, tnList); + + int size = tnList.size(); + for(int i=1; i tnList) { + if(root != null) { + tnList.add(root); + preOrderTravel(root.left, tnList); + preOrderTravel(root.right, tnList); + int a = Integer.MIN_VALUE; + } + } + +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code100/M120_\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" "b/src/main/java/algorithm_practice/LeetCode/code100/M120_\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" new file mode 100644 index 00000000..52d74078 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code100/M120_\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" @@ -0,0 +1,70 @@ +package algorithm_practice.LeetCode.code100; + +import java.util.Arrays; +import java.util.List; + +public class M120_三角形最小路径和 { + + /* + 给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。 + +相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。 + +  + +例如,给定三角形: + +[ + [2], + [3,4], + [6,5,7], + [4,1,8,3] +] +自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。 + +  + +说明: + +如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。 + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/triangle +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + public static void main(String[] args) { + List level1 = Arrays.asList(2); + List level2 = Arrays.asList(3, 4); + List level3 = Arrays.asList(6, 5, 7); + List level4 = Arrays.asList(4, 1, 8, 3); + List> triangle = Arrays.asList(level1, level2, level3, level4); + System.out.println(minimumTotal(triangle)); + } + + /* + 1.动态规划 +转移方程:记动态规划数组dp[i][j]为到达i,j点的最小路径长度,我们有dp[i][j] = min(dp[i-1][j-1], dp[i][j-1]) + triangle[i][j]), +最终triangle[n-1]行中最小的元素就是我们要的 +边界条件:dp[0][0] = triangle[i][j], dp[i][0] = sum(triangle[0 to i][0]),dp [i][i] = sum(triangle[o to i][0 to i])。 + + */ + public static int minimumTotal(List> triangle) { + int n = triangle.size(); + int[][] dp = new int[n][n]; + dp[0][0] = triangle.get(0).get(0); + for (int i = 1; i < n; i++) { + dp[i][0] = triangle.get(i).get(0) + dp[i - 1][0]; + for (int j = 1; j < i; j++) { + dp[i][j] = Math.min(dp[i - 1][j - 1], dp[i - 1][j]) + triangle.get(i).get(j); + } + dp[i][i] = triangle.get(i).get(i) + dp[i - 1][i - 1]; + } + int min = Integer.MAX_VALUE; + for (int i = 0; i < n; i++) { + if (dp[n - 1][i] < min) { + min = dp[n - 1][i]; + } + } + return min; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code300/E350_\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206II.java" "b/src/main/java/algorithm_practice/LeetCode/code300/E350_\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206II.java" new file mode 100644 index 00000000..6723c5e5 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code300/E350_\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206II.java" @@ -0,0 +1,71 @@ +package algorithm_practice.LeetCode.code300; + +import java.util.ArrayList; +import java.util.Arrays; + +public class E350_两个数组的交集II { + + /* + 给定两个数组,编写一个函数来计算它们的交集。 + +  + +示例 1: + +输入:nums1 = [1,2,2,1], nums2 = [2,2] +输出:[2,2] +示例 2: + +输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4] +输出:[4,9] +  + +说明: + +输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。 +我们可以不考虑输出结果的顺序。 +进阶: + +如果给定的数组已经排好序呢?你将如何优化你的算法? +如果 nums1 的大小比 nums2 小很多,哪种方法更优? +如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办? + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/intersection-of-two-arrays-ii +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + public static void main(String[] args) { + System.out.println(Arrays.toString(intersect(new int[]{1, 2, 2, 1}, new int[]{2, 2}))); + System.out.println(Arrays.toString(intersect(new int[]{4, 9, 5}, new int[]{9, 4, 9, 8, 4}))); + } + + /* + 1.双指针 +比较常规的一道问题,只需要对两个数组进行排序,然后维护双指针 i, j +扫一遍两个数组直到某个指针先扫完数组,如果arrayA[i] == arrayB[j], +则把这个值放入result,i++同时j++,否则arrayA[i] 和arrayB[j]中对应较少的那个i或j ++,终止条件是遇到数组结尾。 + */ + public static int[] intersect(int[] nums1, int[] nums2) { + Arrays.sort(nums1); + Arrays.sort(nums2); + int i = 0; + int j = 0; + int[] res = new int[Math.max(nums1.length, nums2.length)]; + int index = 0; + while (i < nums1.length && j < nums2.length) { + if (nums1[i] == nums2[j]) { + res[index] = nums1[i]; + i++; + j++; + index ++; + } else { + if (nums1[i] < nums2[j]) { + i++; + } else { + j++; + } + } + } + return Arrays.copyOfRange(res, 0, index); + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code300/E392_\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227.java" "b/src/main/java/algorithm_practice/LeetCode/code300/E392_\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227.java" new file mode 100644 index 00000000..1aacf26e --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code300/E392_\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227.java" @@ -0,0 +1,51 @@ +package algorithm_practice.LeetCode.code300; + +public class E392_判断子序列 { + /* + 给定字符串 s 和 t ,判断 s 是否为 t 的子序列。 + +你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。 + +字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。 + +示例 1: +s = "abc", t = "ahbgdc" + +返回 true. + +示例 2: +s = "axc", t = "ahbgdc" + +返回 false. + +后续挑战 : + +如果有大量输入的 S,称作S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码? + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/is-subsequence +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + + public static void main(String[] args) { + System.out.println(new E392_判断子序列().isSubsequence("abc", "axxdsbgdgasc")); + } + + /* + 思路: + 双指针 + */ + public boolean isSubsequence(String s, String t) { + int s1 = 0; + int s2 = 0; + while(s1 != s.length() && s2 != t.length()) { + if(s.charAt(s1) == t.charAt(s2)) { + s1++; + s2++; + } else { + s2++; + } + } + return s1 == s.length(); + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code300/H315_\350\256\241\347\256\227\345\217\263\344\276\247\345\260\217\344\272\216\345\275\223\345\211\215\345\205\203\347\264\240\347\232\204\344\270\252\346\225\260.java" "b/src/main/java/algorithm_practice/LeetCode/code300/H315_\350\256\241\347\256\227\345\217\263\344\276\247\345\260\217\344\272\216\345\275\223\345\211\215\345\205\203\347\264\240\347\232\204\344\270\252\346\225\260.java" new file mode 100644 index 00000000..c122fbb9 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code300/H315_\350\256\241\347\256\227\345\217\263\344\276\247\345\260\217\344\272\216\345\275\223\345\211\215\345\205\203\347\264\240\347\232\204\344\270\252\346\225\260.java" @@ -0,0 +1,110 @@ +package algorithm_practice.LeetCode.code300; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class H315_计算右侧小于当前元素的个数 { + + /* + 给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是  nums[i] 右侧小于 nums[i] 的元素的数量。 + +示例: + +输入: [5,2,6,1] +输出: [2,1,1,0] +解释: +5 的右侧有 2 个更小的元素 (2 和 1). +2 的右侧仅有 1 个更小的元素 (1). +6 的右侧有 1 个更小的元素 (1). +1 的右侧有 0 个更小的元素. + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + private int[] c; + private int[] a; + + public static void main(String[] args) { + int[] result = new int[]{2, 1, 1, 0}; + List cal = new H315_计算右侧小于当前元素的个数().countSmaller(new int[]{5, 2, 6, 1}); + for (int i = 0; i < result.length; i++) { + System.out.println(result[i] == cal.get(i)); + } + } + + /* + 解法:看hints就知道这道题肯定有多种解决方法,而树状数组和线段树代表了需要新的数据结构,所以这道题被我归类到新的数据结构类问题的范畴。 +朴素的思维来看就是从右向左遍历数组,遍历到每个元素的同事记录一下已经遍历的每个元素值的个数,然后在res的该位置设置为已经遍历过的元素中小于当前值的元素的个数和。可以看到这时候就需要一个高效的数据结构提供以下功能: + 1. 利用较少的空间按顺序记录每个已经遍历过的元素以及他们出现的次数 + 2. 利用最快的时间找到小于某个元素的那些值在数组中的个数 +这时候就需要使用到树状数组,同时这个数组可能是很稀疏的,有很多的0,我们需要对其进行离散化操作以节约空间。 + +「树状数组」是一种可以动态维护序列前缀和的数据结构,它的功能是: + +单点更新 update(i, v): 把序列 ii 位置的数加上一个值 vv,在该题中 v = 1v=1 +区间查询 query(i): 查询序列 [1 \cdots i][1⋯i] 区间的区间和,即 ii 位置的前缀和 +修改和查询的时间代价都是 O(\log n)O(logn),其中 nn 为需要维护前缀和的序列的长度。 + + */ + public List countSmaller(int[] nums) { + List resultList = new ArrayList<>(); + discretization(nums); + init(nums.length + 5); + for (int i = nums.length - 1; i >= 0; --i) { + int id = getId(nums[i]); + resultList.add(query(id - 1)); + update(id); + } + Collections.reverse(resultList); + return resultList; + } + + private void init(int length) { + c = new int[length]; + Arrays.fill(c, 0); + } + + private int lowBit(int x) { + return x & (-x); + } + + private void update(int pos) { + while (pos < c.length) { + c[pos] += 1; + pos += lowBit(pos); + } + } + + private int query(int pos) { + int ret = 0; + while (pos > 0) { + ret += c[pos]; + pos -= lowBit(pos); + } + + return ret; + } + + private void discretization(int[] nums) { + Set set = new HashSet<>(); + for (int num : nums) { + set.add(num); + } + int size = set.size(); + a = new int[size]; + int index = 0; + for (int num : set) { + a[index++] = num; + } + Arrays.sort(a); + } + + private int getId(int x) { + return Arrays.binarySearch(a, x) + 1; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code300/M309_\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237.java" "b/src/main/java/algorithm_practice/LeetCode/code300/M309_\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237.java" new file mode 100644 index 00000000..4f59a837 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code300/M309_\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237.java" @@ -0,0 +1,45 @@ +package algorithm_practice.LeetCode.code300; + +public class M309_最佳买卖股票时机含冷冻期 { + + /* + 给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​ + +设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): + +你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 +卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。 +示例: + +输入: [1,2,3,0,2] +输出: 3 +解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出] + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ + public static void main(String[] args) { +// System.out.println(maxProfit(new int[]{1, 2, 3, 0, 2})); +// System.out.println(maxProfit(new int[]{3, 2, 6, 5, 0, 3})); +// System.out.println(maxProfit(new int[]{2, 1, 4, 5, 2, 9, 7})); + System.out.println(maxProfit(new int[]{2,6,8,7,8,7,9,4,1,2,4,5,8})); + } + + /* + + */ + public static int maxProfit(int[] prices) { + if(prices.length == 0) { + return 0; + } + int[][] dp = new int[prices.length][3]; + dp[0][0] = - prices[0]; + for(int i=1; i queue = new LinkedList<>(); + queue.offer(i); + while (!queue.isEmpty()) { + int node = queue.poll(); + int neighborColor = color[node] == RED ? BLUE : RED; + for (int nei : graph[node]) { + if (color[nei] == UNCOLOR) { + color[nei] = neighborColor; + queue.offer(nei); + } else if (color[nei] != neighborColor) { + return false; + } + } + } + } + } + return true; + } + +} diff --git a/src/main/java/algorithm_practice/LeetCode/doubleWeekMatch/week30/match01.java b/src/main/java/algorithm_practice/LeetCode/doubleWeekMatch/week30/match01.java new file mode 100644 index 00000000..a64e240b --- /dev/null +++ b/src/main/java/algorithm_practice/LeetCode/doubleWeekMatch/week30/match01.java @@ -0,0 +1,49 @@ +package algorithm_practice.LeetCode.doubleWeekMatch.week30; + +import java.util.HashMap; +import java.util.Map; + +public class match01 { + + public static void main(String[] args) { + System.out.println(reformatDate("6th Jun 1933")); + } + + public static String reformatDate(String date) { + String[] s = date.split(" "); + int day = processDay(s[0]); + Map monthMap = new HashMap<>(); + monthMap.put("Jan", 1); + monthMap.put("Feb", 2); + monthMap.put("Mar", 3); + monthMap.put("Apr", 4); + monthMap.put("May", 5); + monthMap.put("Jun", 6); + monthMap.put("Jul", 7); + monthMap.put("Aug", 8); + monthMap.put("Sep", 9); + monthMap.put("Oct", 10); + monthMap.put("Nov", 11); + monthMap.put("Dec", 12); + int month = monthMap.get(s[1]); + String ms = fillZero(month); + int year = Integer.parseInt(s[2]); + String ds = fillZero(day); + return year + "-" + ms + "-" + ds; + } + + private static String fillZero(int month) { + if(month<10) { + return "0" + month; + } + return String.valueOf(month); + } + + private static int processDay(String s) { + if (s.length() == 3) { + return Integer.parseInt(s.substring(0, 1)); + } else { + return Integer.parseInt(s.substring(0, 2)); + } + } +} diff --git a/src/main/java/algorithm_practice/LeetCode/doubleWeekMatch/week30/match02.java b/src/main/java/algorithm_practice/LeetCode/doubleWeekMatch/week30/match02.java new file mode 100644 index 00000000..cb15f1e7 --- /dev/null +++ b/src/main/java/algorithm_practice/LeetCode/doubleWeekMatch/week30/match02.java @@ -0,0 +1,31 @@ +package algorithm_practice.LeetCode.doubleWeekMatch.week30; + +import java.util.Arrays; + +public class match02 { + + public static void main(String[] args) { + System.out.printf("" + new match02().rangeSum(new int[]{1,2,3,4}, 4, 3, 4)); + } + + public int rangeSum(int[] nums, int n, int left, int right) { + int[] arr = new int[(n * (n + 1) / 2)]; + int idx = 0; + for (int i = 0; i < n; i++) { + for (int j = i; j < n; j++) { + arr[idx] = sum(nums, i, j); + idx++; + } + } + Arrays.sort(arr); + return sum(arr, left-1, right-1); + } + + private int sum(int[] nums, int start, int end) { + int res = 0; + for(int i=start; i<=end; i++) { + res += nums[i]; + } + return res; + } +} diff --git a/src/main/java/algorithm_practice/LeetCode/doubleWeekMatch/week30/match03.java b/src/main/java/algorithm_practice/LeetCode/doubleWeekMatch/week30/match03.java new file mode 100644 index 00000000..d321d723 --- /dev/null +++ b/src/main/java/algorithm_practice/LeetCode/doubleWeekMatch/week30/match03.java @@ -0,0 +1,26 @@ +package algorithm_practice.LeetCode.doubleWeekMatch.week30; + +import java.util.Arrays; + +public class match03 { + + public static void main(String[] args) { + System.out.println("" + new match03().minDifference(new int[]{5, 3, 2, 4})); + System.out.println("" + new match03().minDifference(new int[]{1, 5, 0, 10, 14})); + System.out.println("" + new match03().minDifference(new int[]{6, 6, 0, 1, 1, 4, 6})); + System.out.println("" + new match03().minDifference(new int[]{1, 5, 6, 14, 15})); + System.out.println("" + new match03().minDifference(new int[]{82, 81, 95, 75, 20})); + } + + public int minDifference(int[] nums) { + if (nums.length <= 4) { + return 0; + } + Arrays.sort(nums); + int a1 = nums[nums.length - 4] - nums[0]; + int a2 = nums[nums.length - 3] - nums[1]; + int a3 = nums[nums.length - 2] - nums[2]; + int a4 = nums[nums.length - 1] - nums[3]; + return Math.min(Math.min(a1, a2), Math.min(a3, a4)); + } +} diff --git a/src/main/java/algorithm_practice/LeetCode/doubleWeekMatch/week30/match04.java b/src/main/java/algorithm_practice/LeetCode/doubleWeekMatch/week30/match04.java new file mode 100644 index 00000000..5cba5384 --- /dev/null +++ b/src/main/java/algorithm_practice/LeetCode/doubleWeekMatch/week30/match04.java @@ -0,0 +1,41 @@ +package algorithm_practice.LeetCode.doubleWeekMatch.week30; + +public class match04 { + + public static void main(String[] args) { + for(int i=1; i<48; i++) { + System.out.println("i=" + i + " is " + new match04().winnerSquareGame(i)); + } + } + + public boolean winnerSquareGame(int n) { + boolean[] dp = new boolean[n+1]; + for(int i=1; i<=n; i++) { + if(isPower(i)) { + dp[i] = true; + continue; + } + dp[i] = process(dp, i); + } + return dp[n]; + } + + private boolean process(boolean[] dp, int target) { + boolean res = true; + for(int i=1; i<=Math.sqrt(target)+1; i++) { + if(target - i*i >0) { + res = res && dp[target - i*i]; + } + } + return !res; + } + + private boolean isPower(int i) { + for(int a =1; a fset = new HashSet<>(); + Set bset = new HashSet<>(); + farr[0] =1; + fset.add(s.charAt(0)); + for(int i=1; i=0;j--) { + if(bset.contains(s.charAt(j))) { + barr[j] = barr[j+1]; + } else { + barr[j] = barr[j+1] +1; + bset.add(s.charAt(j)); + } + } + + int res = 0; + for(int i=0;i> cntMap = new HashMap<>(); + TreeSet ts = new TreeSet<>(); + for (int i = 0; i < target.length; i++) { + ts.add(target[i]); + if (cntMap.containsKey(target[i])) { + cntMap.get(target[i]).add(i); + } else { + List lst = new ArrayList<>(); + lst.add(i); + cntMap.put(target[i], lst); + } + } + List mIdx = new ArrayList<>(); + + int res = 0; + for (int i = cntMap.size() - 1; i > 1; i--) { + int num = ts.pollLast(); + mIdx.addAll(cntMap.get(num)); + int sub = num - ts.last(); + res += process(mIdx) * sub; + } + return res; + } + + private int process(List mIdx) { + int cnt = 1 ; + int[] arr = new int[mIdx.size()]; + for(int i=0;i c = new ArrayList<>(); + ArrayList n = new ArrayList<>(); + + char current = s.charAt(0); + int cnt=1; + for(int i=1; i= min) { + n.remove(i); + c.remove(i); + k = k -min; + } else { + n.set(i, n.get(i) - k); + k = 0; + } + } + } + min ++; + } + + StringBuilder sb = new StringBuilder(); + for(int i=0; i ts = new TreeSet<>(); + for (int i = 0; i <= k; i++) { + ts.add(shorter * i + longer * (k - i)); + } + int[] res = new int[ts.size()]; + final int[] index = {0}; + ts.forEach(val -> { + res[index[0]] = val; + index[0]++; + }); + return res; + } + + /* + 2. 数学方法,如果k==0,return空数组,如果x == y,那么只有一种k*x的结果,否则是等差数列,从小的那个数x开始,每次增加(y - x)直到增加了k个y-x + + */ + public static int[] divingBoard(int shorter, int longer, int k) { + if (k == 0) { + return new int[]{}; + } + if (shorter == longer) { + return new int[]{shorter * k}; + } + int[] res = new int[k + 1]; + int adder = longer - shorter; + for (int i = 0; i <= k; i++) { + res[i] = shorter * k + i * adder; + } + return res; + } +} diff --git a/src/main/java/common/datastruct/TreeNode.java b/src/main/java/common/datastruct/TreeNode.java index 55d74757..d77d3a46 100644 --- a/src/main/java/common/datastruct/TreeNode.java +++ b/src/main/java/common/datastruct/TreeNode.java @@ -4,11 +4,21 @@ * LeetCode 二叉树结点 */ public class TreeNode { - public Integer val; - public TreeNode left; - public TreeNode right; - public TreeNode(Integer val) { - this.val = val; - } + public Integer val; + public TreeNode left; + public TreeNode right; + + public TreeNode(Integer val) { + this.val = val; + } + + public TreeNode() { + } + + public TreeNode(Integer val, TreeNode left, TreeNode right) { + this.val = val; + this.left = left; + this.right = right; + } } diff --git a/src/main/java/common/util/BTreePrinter.java b/src/main/java/common/util/BTreePrinter.java new file mode 100644 index 00000000..973063c4 --- /dev/null +++ b/src/main/java/common/util/BTreePrinter.java @@ -0,0 +1,93 @@ +package common.util; + +import common.datastruct.TreeNode; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class BTreePrinter { + + public static void printTreeNode(TreeNode root) { + int maxLevel = BTreePrinter.maxLevel(root); + + printTreeNodeInternal(Collections.singletonList(root), 1, maxLevel); + } + + private static void printTreeNodeInternal(List TreeNodes, int level, int maxLevel) { + if (TreeNodes.isEmpty() || BTreePrinter.isAllElementsNull(TreeNodes)) + return; + + int floor = maxLevel - level; + int edgeLines = (int) Math.pow(2, (Math.max(floor - 1, 0))); + int firstSpaces = (int) Math.pow(2, (floor)) - 1; + int betweenSpaces = (int) Math.pow(2, (floor + 1)) - 1; + + BTreePrinter.printWhitespaces(firstSpaces); + + List newTreeNodes = new ArrayList<>(); + for (TreeNode TreeNode : TreeNodes) { + if (TreeNode != null) { + System.out.print(TreeNode.val); + newTreeNodes.add(TreeNode.left); + newTreeNodes.add(TreeNode.right); + } else { + newTreeNodes.add(null); + newTreeNodes.add(null); + System.out.print(" "); + } + + BTreePrinter.printWhitespaces(betweenSpaces); + } + System.out.println(""); + + for (int i = 1; i <= edgeLines; i++) { + for (int j = 0; j < TreeNodes.size(); j++) { + BTreePrinter.printWhitespaces(firstSpaces - i); + if (TreeNodes.get(j) == null) { + BTreePrinter.printWhitespaces(edgeLines + edgeLines + i + 1); + continue; + } + + if (TreeNodes.get(j).left != null) + System.out.print("/"); + else + BTreePrinter.printWhitespaces(1); + + BTreePrinter.printWhitespaces(i + i - 1); + + if (TreeNodes.get(j).right != null) + System.out.print("\\"); + else + BTreePrinter.printWhitespaces(1); + + BTreePrinter.printWhitespaces(edgeLines + edgeLines - i); + } + + System.out.println(""); + } + + printTreeNodeInternal(newTreeNodes, level + 1, maxLevel); + } + + private static void printWhitespaces(int count) { + for (int i = 0; i < count; i++) + System.out.print(" "); + } + + private static int maxLevel(TreeNode TreeNode) { + if (TreeNode == null) + return 0; + + return Math.max(BTreePrinter.maxLevel(TreeNode.left), BTreePrinter.maxLevel(TreeNode.right)) + 1; + } + + private static boolean isAllElementsNull(List list) { + for (Object object : list) { + if (object != null) + return false; + } + + return true; + } + +} diff --git a/src/main/java/jdk/concurrent/AQS/AbstractQueuedSynchronizer.java b/src/main/java/jdk/concurrent/AQS/AbstractQueuedSynchronizer.java deleted file mode 100644 index a8a67a4b..00000000 --- a/src/main/java/jdk/concurrent/AQS/AbstractQueuedSynchronizer.java +++ /dev/null @@ -1,1924 +0,0 @@ -package jdk.concurrent.AQS; - -import java.util.concurrent.TimeUnit; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.concurrent.locks.AbstractOwnableSynchronizer; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.LockSupport; - -import sun.misc.Unsafe; - -public abstract class AbstractQueuedSynchronizer - extends AbstractOwnableSynchronizer - implements java.io.Serializable { - - private static final long serialVersionUID = 7373984972572414691L; - - protected AbstractQueuedSynchronizer() { - } - - static final class Node { - static final Node SHARED = new Node(); //共享锁标识 - static final Node EXCLUSIVE = null; //独占锁标识 - - static final int CANCELLED = 1; - static final int SIGNAL = -1; - static final int CONDITION = -2; - static final int PROPAGATE = -3; - - /* - 节点的状态 - 0:默认状态,当前节点在队列中等待获取锁 - 1:当前节点被取消 - -1:当前节点的后继节点将要或已经被阻塞,在当前节点释放的时候,需要unpark后继节点 - -2:当前节点在等待队列(Condition Queue)中 - -3:表示releaseShared需要被传播给后续节点(仅在共享模式下使用) - */ - volatile int waitStatus; - - volatile Node prev; - volatile Node next; - volatile Thread thread; - - /** - * 1. Condition Queue中,用于连接后继节点 - * 2. 判断 Sync Queue中,当前节点是不是共享模式 - */ - Node nextWaiter; - - final boolean isShared() { - return nextWaiter == SHARED; - } - - //获取 node 的前驱节点 - final Node predecessor() throws NullPointerException { - Node p = prev; - if (p == null) { - throw new NullPointerException(); - } else { - return p; - } - } - - Node() { - } - - //用于初始化 Sync Queue 节点 - Node(Thread thread, Node mode) { // Used by addWaiter - this.nextWaiter = mode; - this.thread = thread; - } - - //用于初始化 Condition Queue 节点 - Node(Thread thread, int waitStatus) { // Used by Condition - this.waitStatus = waitStatus; - this.thread = thread; - } - } - - //头结点的waitStatus=1(头结点不存储Thread,只存储next节点的引用) - private transient volatile Node head; - private transient volatile Node tail; - - /** - * status = 0 代表无锁, - * 当前队列线程每加一次锁 state ++,每解一次锁 state -- - */ - private volatile int state; - - protected final int getState() { - return state; - } - - protected final void setState(int newState) { - state = newState; - } - - protected final boolean compareAndSetState(int expect, int update) { - // See below for intrinsics setup to support this - return unsafe.compareAndSwapInt(this, stateOffset, expect, update); - } - - static final long spinForTimeoutThreshold = 1000L; - - /** - * Inserts node into queue - */ - private Node enq(final Node node) { - for (; ; ) { - Node t = tail; - if (t == null) { - //首次插入,初始化头结点,然后 tail = head - if (compareAndSetHead(new Node())) { - tail = head; - } - } else { - //说明其他线程已经帮你把头结点初始化好了,直接插到tail.next就好了 - node.prev = t; - if (compareAndSetTail(t, node)) { - t.next = node; - return t; - } - } - } - } - - /** - * 将mode节点插到队尾 - */ - private Node addWaiter(Node mode) { - // 将当前线程封装成一个独占锁节点 - Node node = new Node(Thread.currentThread(), mode); - Node pred = tail; - // 尾节点不为空,则直接将node到tail.next - if (pred != null) { - node.prev = pred; - if (compareAndSetTail(pred, node)) { - pred.next = node; - return node; - } - } - // 否则直接enq - enq(node); - return node; - } - - /** - * Sets head of queue to be node, thus dequeuing. Called only by - * acquire methods. Also nulls out unused fields for sake of GC - * and to suppress unnecessary signals and traversals. - * - * @param node the node - */ - private void setHead(Node node) { - head = node; - node.thread = null; - node.prev = null; - } - - /** - * Wakes up node's successor, if one exists. - * - * @param node the node - */ - private void unparkSuccessor(Node node) { - /* - * If status is negative (i.e., possibly needing signal) try - * to clear in anticipation of signalling. It is OK if this - * fails or if status is changed by waiting thread. - */ - int ws = node.waitStatus; - if (ws < 0) { - compareAndSetWaitStatus(node, ws, 0); - } - - /* - * Thread to unpark is held in successor, which is normally - * just the next node. But if cancelled or apparently null, - * traverse backwards from tail to find the actual - * non-cancelled successor. - */ - Node s = node.next; - if (s == null || s.waitStatus > 0) { - s = null; - for (Node t = tail; t != null && t != node; t = t.prev) { - if (t.waitStatus <= 0) { - s = t; - } - } - } - if (s != null) { - LockSupport.unpark(s.thread); - } - } - - /** - * Release action for shared mode -- signals successor and ensures - * propagation. (Note: For exclusive mode, release just amounts - * to calling unparkSuccessor of head if it needs signal.) - */ - private void doReleaseShared() { - /* - * Ensure that a release propagates, even if there are other - * in-progress acquires/releases. This proceeds in the usual - * way of trying to unparkSuccessor of head if it needs - * signal. But if it does not, status is set to PROPAGATE to - * ensure that upon release, propagation continues. - * Additionally, we must loop in case a new node is added - * while we are doing this. Also, unlike other uses of - * unparkSuccessor, we need to know if CAS to reset status - * fails, if so rechecking. - */ - for (; ; ) { - Node h = head; - if (h != null && h != tail) { - int ws = h.waitStatus; - if (ws == Node.SIGNAL) { - if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) { - continue; // loop to recheck cases - } - unparkSuccessor(h); - } else if (ws == 0 && - !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) { - continue; // loop on failed CAS - } - } - if (h == head) // loop if head changed - { - break; - } - } - } - - /** - * Sets head of queue, and checks if successor may be waiting - * in shared mode, if so propagating if either propagate > 0 or - * PROPAGATE status was set. - * - * @param node the node - * @param propagate the return value from a tryAcquireShared - */ - private void setHeadAndPropagate(Node node, int propagate) { - Node h = head; // Record old head for check below - setHead(node); - /* - * Try to signal next queued node if: - * Propagation was indicated by caller, - * or was recorded (as h.waitStatus either before - * or after setHead) by a previous operation - * (note: this uses sign-check of waitStatus because - * PROPAGATE status may transition to SIGNAL.) - * and - * The next node is waiting in shared mode, - * or we don't know, because it appears null - * - * The conservatism in both of these checks may cause - * unnecessary wake-ups, but only when there are multiple - * racing acquires/releases, so most need signals now or soon - * anyway. - */ - if (propagate > 0 || h == null || h.waitStatus < 0 || - (h = head) == null || h.waitStatus < 0) { - Node s = node.next; - if (s == null || s.isShared()) { - doReleaseShared(); - } - } - } - - // Utilities for various versions of acquire - - /** - * Cancels an ongoing attempt to acquire. - * - * @param node the node - */ - private void cancelAcquire(Node node) { - // Ignore if node doesn't exist - if (node == null) { - return; - } - - node.thread = null; - - // Skip cancelled predecessors - Node pred = node.prev; - while (pred.waitStatus > 0) { - node.prev = pred = pred.prev; - } - - // predNext is the apparent node to unsplice. CASes below will - // fail if not, in which case, we lost race vs another cancel - // or signal, so no further action is necessary. - Node predNext = pred.next; - - // Can use unconditional write instead of CAS here. - // After this atomic step, other Nodes can skip past us. - // Before, we are free of interference from other threads. - node.waitStatus = Node.CANCELLED; - - // If we are the tail, remove ourselves. - if (node == tail && compareAndSetTail(node, pred)) { - compareAndSetNext(pred, predNext, null); - } else { - // If successor needs signal, try to set pred's next-link - // so it will get one. Otherwise wake it up to propagate. - int ws; - if (pred != head && - ((ws = pred.waitStatus) == Node.SIGNAL || - (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && - pred.thread != null) { - Node next = node.next; - if (next != null && next.waitStatus <= 0) { - compareAndSetNext(pred, predNext, next); - } - } else { - unparkSuccessor(node); - } - - node.next = node; // help GC - } - } - - /** - * Checks and updates status for a node that failed to acquire. - * Returns true if thread should block. This is the main signal - * control in all acquire loops. Requires that pred == node.prev. - * - * @param pred node's predecessor holding status - * @param node the node - * @return {@code true} if thread should block - */ - private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { - int ws = pred.waitStatus; - if (ws == Node.SIGNAL) { - //当前节点的前驱已经设置了请求释放信号的状态,因此它可以安全地中断 - return true; - } - if (ws > 0) { - //前驱节点被取消了。则继续删除该节点,继续往前找,找到第一个不被取消的节点,设为当前node的前驱节点 - do { - node.prev = pred = pred.prev; - } while (pred.waitStatus > 0); - pred.next = node; - } else { - /* - * CAS 设置前驱节点的 waitStatus 为 -1 - */ - compareAndSetWaitStatus(pred, ws, Node.SIGNAL); - } - return false; - } - - /** - * Convenience method to interrupt current thread. - */ - static void selfInterrupt() { - Thread.currentThread().interrupt(); - } - - /** - * 阻塞当前线程,返回当前线程的中断状态并复位中断状态 - * (interrupted() 如果当前线程是中断状态,则返回true,否则返回false) - * - * @return {@code true} if interrupted - */ - private final boolean parkAndCheckInterrupt() { - LockSupport.park(this); - return Thread.interrupted(); - } - - /** - * 获取队列中已存在线程的独占不可中断模式。用于条件等待方法以及获取。 - *

- * 死循环,尝试获取锁,直到成功。返回中断标志位 - * - * @param node the node - * @param arg {@code 1} - */ - final boolean acquireQueued(final Node node, int arg) { - boolean failed = true; - try { - boolean interrupted = false; - for (; ; ) { - final Node p = node.predecessor(); - if (p == head && tryAcquire(arg)) { - /** - * 若当前节点的前驱节点为head && (state ++ 成功) - * 说明当前线程刚刚被 addWaiter,且队列中只剩下这一个独占锁了 - * 将当前节点设为头结点,释放头结点 - * 设置独占锁成功,不可中断 - */ - setHead(node); - p.next = null; // help GC - failed = false; - return interrupted; - } - /** - * 如果前驱节点不是head,或state++ 失败,则判断是否需要中断 - * 如果前驱节点的waitStatus = -1,说明可以安全中断 - * 否则本次循环不执行中断操作,使用CAS防止将前驱节点的 waitStatus 设为 -1 - * 中断成功,则当前线程取消获取锁,并挂起。 - */ - - if (shouldParkAfterFailedAcquire(p, node) && - parkAndCheckInterrupt()) { - interrupted = true; - } - } - } finally { - if (failed) { - cancelAcquire(node); - } - } - } - - /** - * Acquires in exclusive interruptible mode. - * - * @param arg the acquire argument - */ - private void doAcquireInterruptibly(int arg) - throws InterruptedException { - final Node node = addWaiter(Node.EXCLUSIVE); - boolean failed = true; - try { - for (; ; ) { - final Node p = node.predecessor(); - if (p == head && tryAcquire(arg)) { - setHead(node); - p.next = null; // help GC - failed = false; - return; - } - if (shouldParkAfterFailedAcquire(p, node) && - parkAndCheckInterrupt()) { - throw new InterruptedException(); - } - } - } finally { - if (failed) { - cancelAcquire(node); - } - } - } - - /** - * Acquires in exclusive timed mode. - * - * @param arg the acquire argument - * @param nanosTimeout max wait time - * @return {@code true} if acquired - */ - private boolean doAcquireNanos(int arg, long nanosTimeout) - throws InterruptedException { - if (nanosTimeout <= 0L) { - return false; - } - final long deadline = System.nanoTime() + nanosTimeout; - final Node node = addWaiter(Node.EXCLUSIVE); - boolean failed = true; - try { - for (; ; ) { - final Node p = node.predecessor(); - if (p == head && tryAcquire(arg)) { - setHead(node); - p.next = null; // help GC - failed = false; - return true; - } - nanosTimeout = deadline - System.nanoTime(); - if (nanosTimeout <= 0L) { - return false; - } - if (shouldParkAfterFailedAcquire(p, node) && - nanosTimeout > spinForTimeoutThreshold) { - LockSupport.parkNanos(this, nanosTimeout); - } - if (Thread.interrupted()) { - throw new InterruptedException(); - } - } - } finally { - if (failed) { - cancelAcquire(node); - } - } - } - - /** - * Acquires in shared uninterruptible mode. - * - * @param arg the acquire argument - */ - private void doAcquireShared(int arg) { - final Node node = addWaiter(Node.SHARED); - boolean failed = true; - try { - boolean interrupted = false; - for (; ; ) { - final Node p = node.predecessor(); - if (p == head) { - int r = tryAcquireShared(arg); - if (r >= 0) { - setHeadAndPropagate(node, r); - p.next = null; // help GC - if (interrupted) { - selfInterrupt(); - } - failed = false; - return; - } - } - if (shouldParkAfterFailedAcquire(p, node) && - parkAndCheckInterrupt()) { - interrupted = true; - } - } - } finally { - if (failed) { - cancelAcquire(node); - } - } - } - - /** - * Acquires in shared interruptible mode. - * - * @param arg the acquire argument - */ - private void doAcquireSharedInterruptibly(int arg) - throws InterruptedException { - final Node node = addWaiter(Node.SHARED); - boolean failed = true; - try { - for (; ; ) { - final Node p = node.predecessor(); - if (p == head) { - int r = tryAcquireShared(arg); - if (r >= 0) { - setHeadAndPropagate(node, r); - p.next = null; // help GC - failed = false; - return; - } - } - if (shouldParkAfterFailedAcquire(p, node) && - parkAndCheckInterrupt()) { - throw new InterruptedException(); - } - } - } finally { - if (failed) { - cancelAcquire(node); - } - } - } - - /** - * Acquires in shared timed mode. - * - * @param arg the acquire argument - * @param nanosTimeout max wait time - * @return {@code true} if acquired - */ - private boolean doAcquireSharedNanos(int arg, long nanosTimeout) - throws InterruptedException { - if (nanosTimeout <= 0L) { - return false; - } - final long deadline = System.nanoTime() + nanosTimeout; - final Node node = addWaiter(Node.SHARED); - boolean failed = true; - try { - for (; ; ) { - final Node p = node.predecessor(); - if (p == head) { - int r = tryAcquireShared(arg); - if (r >= 0) { - setHeadAndPropagate(node, r); - p.next = null; // help GC - failed = false; - return true; - } - } - nanosTimeout = deadline - System.nanoTime(); - if (nanosTimeout <= 0L) { - return false; - } - if (shouldParkAfterFailedAcquire(p, node) && - nanosTimeout > spinForTimeoutThreshold) { - LockSupport.parkNanos(this, nanosTimeout); - } - if (Thread.interrupted()) { - throw new InterruptedException(); - } - } - } finally { - if (failed) { - cancelAcquire(node); - } - } - } - - // Main exported methods - - /** - * Attempts to acquire in exclusive mode. This method should query - * if the state of the object permits it to be acquired in the - * exclusive mode, and if so to acquire it. - * - *

This method is always invoked by the thread performing - * acquire. If this method reports failure, the acquire method - * may queue the thread, if it is not already queued, until it is - * signalled by a release from some other thread. This can be used - * - *

The default - * implementation throws {@link UnsupportedOperationException}. - * - * @param arg the acquire argument. This value is always the one - * passed to an acquire method, or is the value saved on entry - * to a condition wait. The value is otherwise uninterpreted - * and can represent anything you like. - * @return {@code true} if successful. Upon success, this object has - * been acquired. - * @throws IllegalMonitorStateException if acquiring would place this - * synchronizer in an illegal state. This exception must be - * thrown in a consistent fashion for synchronization to work - * correctly. - * @throws UnsupportedOperationException if exclusive mode is not supported - */ - protected boolean tryAcquire(int arg) { - throw new UnsupportedOperationException(); - } - - /** - * Attempts to set the state to reflect a release in exclusive - * mode. - * - *

This method is always invoked by the thread performing release. - * - *

The default implementation throws - * {@link UnsupportedOperationException}. - * - * @param arg the release argument. This value is always the one - * passed to a release method, or the current state value upon - * entry to a condition wait. The value is otherwise - * uninterpreted and can represent anything you like. - * @return {@code true} if this object is now in a fully released - * state, so that any waiting threads may attempt to acquire; - * and {@code false} otherwise. - * @throws IllegalMonitorStateException if releasing would place this - * synchronizer in an illegal state. This exception must be - * thrown in a consistent fashion for synchronization to work - * correctly. - * @throws UnsupportedOperationException if exclusive mode is not supported - */ - protected boolean tryRelease(int arg) { - throw new UnsupportedOperationException(); - } - - /** - * Attempts to acquire in shared mode. This method should query if - * the state of the object permits it to be acquired in the shared - * mode, and if so to acquire it. - * - *

This method is always invoked by the thread performing - * acquire. If this method reports failure, the acquire method - * may queue the thread, if it is not already queued, until it is - * signalled by a release from some other thread. - * - *

The default implementation throws {@link - * UnsupportedOperationException}. - * - * @param arg the acquire argument. This value is always the one - * passed to an acquire method, or is the value saved on entry - * to a condition wait. The value is otherwise uninterpreted - * and can represent anything you like. - * @return a negative value on failure; zero if acquisition in shared - * mode succeeded but no subsequent shared-mode acquire can - * succeed; and a positive value if acquisition in shared - * mode succeeded and subsequent shared-mode acquires might - * also succeed, in which case a subsequent waiting thread - * must check availability. (Support for three different - * return values enables this method to be used in contexts - * where acquires only sometimes act exclusively.) Upon - * success, this object has been acquired. - * @throws IllegalMonitorStateException if acquiring would place this - * synchronizer in an illegal state. This exception must be - * thrown in a consistent fashion for synchronization to work - * correctly. - * @throws UnsupportedOperationException if shared mode is not supported - */ - protected int tryAcquireShared(int arg) { - throw new UnsupportedOperationException(); - } - - /** - * Attempts to set the state to reflect a release in shared mode. - * - *

This method is always invoked by the thread performing release. - * - *

The default implementation throws - * {@link UnsupportedOperationException}. - * - * @param arg the release argument. This value is always the one - * passed to a release method, or the current state value upon - * entry to a condition wait. The value is otherwise - * uninterpreted and can represent anything you like. - * @return {@code true} if this release of shared mode may permit a - * waiting acquire (shared or exclusive) to succeed; and - * {@code false} otherwise - * @throws IllegalMonitorStateException if releasing would place this - * synchronizer in an illegal state. This exception must be - * thrown in a consistent fashion for synchronization to work - * correctly. - * @throws UnsupportedOperationException if shared mode is not supported - */ - protected boolean tryReleaseShared(int arg) { - throw new UnsupportedOperationException(); - } - - /** - * Returns {@code true} if synchronization is held exclusively with - * respect to the current (calling) thread. This method is invoked - * upon each call to a non-waiting {@link ConditionObject} method. - * (Waiting methods instead invoke {@link #release}.) - * - *

The default implementation throws {@link - * UnsupportedOperationException}. This method is invoked - * internally only within {@link ConditionObject} methods, so need - * not be defined if conditions are not used. - * - * @return {@code true} if synchronization is held exclusively; - * {@code false} otherwise - * @throws UnsupportedOperationException if conditions are not supported - */ - protected boolean isHeldExclusively() { - throw new UnsupportedOperationException(); - } - - /** - * 获取独占锁 - * 1. tryAcquire 尝试设置state++,并设置当前线程为独占锁 - * 2. 成功则返回 void,失败则执行下一步 - * 3. 调用addWaiter()方法,将线程设为独占锁添加到队列尾部 - * 4. 自旋获取锁,若获取锁失败,则直接将当前线程挂起 - * 5. 设置线程中断 - * - * @param arg {@code 1} - */ - public final void acquire(int arg) { - if (!tryAcquire(arg) && - acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) { - selfInterrupt(); - } - } - - /** - * Acquires in exclusive mode, aborting if interrupted. - * Implemented by first checking interrupt status, then invoking - * at least once {@link #tryAcquire}, returning on - * success. Otherwise the thread is queued, possibly repeatedly - * blocking and unblocking, invoking {@link #tryAcquire} - * until success or the thread is interrupted. This method can be - * - * @param arg the acquire argument. This value is conveyed to - * {@link #tryAcquire} but is otherwise uninterpreted and - * can represent anything you like. - * @throws InterruptedException if the current thread is interrupted - */ - public final void acquireInterruptibly(int arg) - throws InterruptedException { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - if (!tryAcquire(arg)) { - doAcquireInterruptibly(arg); - } - } - - /** - * Attempts to acquire in exclusive mode, aborting if interrupted, - * and failing if the given timeout elapses. Implemented by first - * checking interrupt status, then invoking at least once {@link - * #tryAcquire}, returning on success. Otherwise, the thread is - * queued, possibly repeatedly blocking and unblocking, invoking - * {@link #tryAcquire} until success or the thread is interrupted - * or the timeout elapses. This method can be used to implement - * - * @param arg the acquire argument. This value is conveyed to - * {@link #tryAcquire} but is otherwise uninterpreted and - * can represent anything you like. - * @param nanosTimeout the maximum number of nanoseconds to wait - * @return {@code true} if acquired; {@code false} if timed out - * @throws InterruptedException if the current thread is interrupted - */ - public final boolean tryAcquireNanos(int arg, long nanosTimeout) - throws InterruptedException { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - return tryAcquire(arg) || - doAcquireNanos(arg, nanosTimeout); - } - - /** - * Releases in exclusive mode. Implemented by unblocking one or - * more threads if {@link #tryRelease} returns true. - * - * @param arg the release argument. This value is conveyed to - * {@link #tryRelease} but is otherwise uninterpreted and - * can represent anything you like. - * @return the value returned from {@link #tryRelease} - */ - public final boolean release(int arg) { - if (tryRelease(arg)) { - Node h = head; - if (h != null && h.waitStatus != 0) { - unparkSuccessor(h); - } - return true; - } - return false; - } - - /** - * Acquires in shared mode, ignoring interrupts. Implemented by - * first invoking at least once {@link #tryAcquireShared}, - * returning on success. Otherwise the thread is queued, possibly - * repeatedly blocking and unblocking, invoking {@link - * #tryAcquireShared} until success. - * - * @param arg the acquire argument. This value is conveyed to - * {@link #tryAcquireShared} but is otherwise uninterpreted - * and can represent anything you like. - */ - public final void acquireShared(int arg) { - if (tryAcquireShared(arg) < 0) { - doAcquireShared(arg); - } - } - - /** - * Acquires in shared mode, aborting if interrupted. Implemented - * by first checking interrupt status, then invoking at least once - * {@link #tryAcquireShared}, returning on success. Otherwise the - * thread is queued, possibly repeatedly blocking and unblocking, - * invoking {@link #tryAcquireShared} until success or the thread - * is interrupted. - * - * @param arg the acquire argument. - * This value is conveyed to {@link #tryAcquireShared} but is - * otherwise uninterpreted and can represent anything - * you like. - * @throws InterruptedException if the current thread is interrupted - */ - public final void acquireSharedInterruptibly(int arg) - throws InterruptedException { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - if (tryAcquireShared(arg) < 0) { - doAcquireSharedInterruptibly(arg); - } - } - - /** - * Attempts to acquire in shared mode, aborting if interrupted, and - * failing if the given timeout elapses. Implemented by first - * checking interrupt status, then invoking at least once {@link - * #tryAcquireShared}, returning on success. Otherwise, the - * thread is queued, possibly repeatedly blocking and unblocking, - * invoking {@link #tryAcquireShared} until success or the thread - * is interrupted or the timeout elapses. - * - * @param arg the acquire argument. This value is conveyed to - * {@link #tryAcquireShared} but is otherwise uninterpreted - * and can represent anything you like. - * @param nanosTimeout the maximum number of nanoseconds to wait - * @return {@code true} if acquired; {@code false} if timed out - * @throws InterruptedException if the current thread is interrupted - */ - public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) - throws InterruptedException { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - return tryAcquireShared(arg) >= 0 || - doAcquireSharedNanos(arg, nanosTimeout); - } - - /** - * Releases in shared mode. Implemented by unblocking one or more - * threads if {@link #tryReleaseShared} returns true. - * - * @param arg the release argument. This value is conveyed to - * {@link #tryReleaseShared} but is otherwise uninterpreted - * and can represent anything you like. - * @return the value returned from {@link #tryReleaseShared} - */ - public final boolean releaseShared(int arg) { - if (tryReleaseShared(arg)) { - doReleaseShared(); - return true; - } - return false; - } - - // Queue inspection methods - - /** - * Queries whether any threads are waiting to acquire. Note that - * because cancellations due to interrupts and timeouts may occur - * at any time, a {@code true} return does not guarantee that any - * other thread will ever acquire. - * - *

In this implementation, this operation returns in - * constant time. - * - * @return {@code true} if there may be other threads waiting to acquire - */ - public final boolean hasQueuedThreads() { - return head != tail; - } - - /** - * Queries whether any threads have ever contended to acquire this - * synchronizer; that is if an acquire method has ever blocked. - * - *

In this implementation, this operation returns in - * constant time. - * - * @return {@code true} if there has ever been contention - */ - public final boolean hasContended() { - return head != null; - } - - /** - * Returns the first (longest-waiting) thread in the queue, or - * {@code null} if no threads are currently queued. - * - *

In this implementation, this operation normally returns in - * constant time, but may iterate upon contention if other threads are - * concurrently modifying the queue. - * - * @return the first (longest-waiting) thread in the queue, or - * {@code null} if no threads are currently queued - */ - public final Thread getFirstQueuedThread() { - // handle only fast path, else relay - return (head == tail) ? null : fullGetFirstQueuedThread(); - } - - /** - * Version of getFirstQueuedThread called when fastpath fails - */ - private Thread fullGetFirstQueuedThread() { - /* - * The first node is normally head.next. Try to get its - * thread field, ensuring consistent reads: If thread - * field is nulled out or s.prev is no longer head, then - * some other thread(s) concurrently performed setHead in - * between some of our reads. We try this twice before - * resorting to traversal. - */ - Node h, s; - Thread st; - if (((h = head) != null && (s = h.next) != null && - s.prev == head && (st = s.thread) != null) || - ((h = head) != null && (s = h.next) != null && - s.prev == head && (st = s.thread) != null)) { - return st; - } - - /* - * Head's next field might not have been set yet, or may have - * been unset after setHead. So we must check to see if tail - * is actually first node. If not, we continue on, safely - * traversing from tail back to head to find first, - * guaranteeing termination. - */ - - Node t = tail; - Thread firstThread = null; - while (t != null && t != head) { - Thread tt = t.thread; - if (tt != null) { - firstThread = tt; - } - t = t.prev; - } - return firstThread; - } - - /** - * Returns true if the given thread is currently queued. - * - *

This implementation traverses the queue to determine - * presence of the given thread. - * - * @param thread the thread - * @return {@code true} if the given thread is on the queue - * @throws NullPointerException if the thread is null - */ - public final boolean isQueued(Thread thread) { - if (thread == null) { - throw new NullPointerException(); - } - for (Node p = tail; p != null; p = p.prev) { - if (p.thread == thread) { - return true; - } - } - return false; - } - - /** - * Returns {@code true} if the apparent first queued thread, if one - * exists, is waiting in exclusive mode. If this method returns - * {@code true}, and the current thread is attempting to acquire in - * shared mode (that is, this method is invoked from {@link - * #tryAcquireShared}) then it is guaranteed that the current thread - * is not the first queued thread. Used only as a heuristic in - * ReentrantReadWriteLock. - */ - final boolean apparentlyFirstQueuedIsExclusive() { - Node h, s; - return (h = head) != null && - (s = h.next) != null && - !s.isShared() && - s.thread != null; - } - - /** - * 队列是否有 前驱节点 - * (如果没有,说明当前队列中还没有其他线程) - * - * @return - */ - public final boolean hasQueuedPredecessors() { - Node t = tail; - Node h = head; - Node s; - return h != t && - ((s = h.next) == null || s.thread != Thread.currentThread()); - } - - - // Instrumentation and monitoring methods - - /** - * Returns an estimate of the number of threads waiting to - * acquire. The value is only an estimate because the number of - * threads may change dynamically while this method traverses - * internal data structures. This method is designed for use in - * monitoring system state, not for synchronization - * control. - * - * @return the estimated number of threads waiting to acquire - */ - public final int getQueueLength() { - int n = 0; - for (Node p = tail; p != null; p = p.prev) { - if (p.thread != null) { - ++n; - } - } - return n; - } - - /** - * Returns a collection containing threads that may be waiting to - * acquire. Because the actual set of threads may change - * dynamically while constructing this result, the returned - * collection is only a best-effort estimate. The elements of the - * returned collection are in no particular order. This method is - * designed to facilitate construction of subclasses that provide - * more extensive monitoring facilities. - * - * @return the collection of threads - */ - public final Collection getQueuedThreads() { - ArrayList list = new ArrayList(); - for (Node p = tail; p != null; p = p.prev) { - Thread t = p.thread; - if (t != null) { - list.add(t); - } - } - return list; - } - - /** - * Returns a collection containing threads that may be waiting to - * acquire in exclusive mode. This has the same properties - * as {@link #getQueuedThreads} except that it only returns - * those threads waiting due to an exclusive acquire. - * - * @return the collection of threads - */ - public final Collection getExclusiveQueuedThreads() { - ArrayList list = new ArrayList(); - for (Node p = tail; p != null; p = p.prev) { - if (!p.isShared()) { - Thread t = p.thread; - if (t != null) { - list.add(t); - } - } - } - return list; - } - - /** - * Returns a collection containing threads that may be waiting to - * acquire in shared mode. This has the same properties - * as {@link #getQueuedThreads} except that it only returns - * those threads waiting due to a shared acquire. - * - * @return the collection of threads - */ - public final Collection getSharedQueuedThreads() { - ArrayList list = new ArrayList(); - for (Node p = tail; p != null; p = p.prev) { - if (p.isShared()) { - Thread t = p.thread; - if (t != null) { - list.add(t); - } - } - } - return list; - } - - /** - * Returns a string identifying this synchronizer, as well as its state. - * The state, in brackets, includes the String {@code "State ="} - * followed by the current value of {@link #getState}, and either - * {@code "nonempty"} or {@code "empty"} depending on whether the - * queue is empty. - * - * @return a string identifying this synchronizer, as well as its state - */ - @Override - public String toString() { - int s = getState(); - String q = hasQueuedThreads() ? "non" : ""; - return super.toString() + - "[State = " + s + ", " + q + "empty queue]"; - } - - - // Internal support methods for Conditions - - /** - * Returns true if a node, always one that was initially placed on - * a condition queue, is now waiting to reacquire on sync queue. - * - * @param node the node - * @return true if is reacquiring - */ - final boolean isOnSyncQueue(Node node) { - if (node.waitStatus == Node.CONDITION || node.prev == null) { - return false; - } - if (node.next != null) // If has successor, it must be on queue - { - return true; - } - /* - * node.prev can be non-null, but not yet on queue because - * the CAS to place it on queue can fail. So we have to - * traverse from tail to make sure it actually made it. It - * will always be near the tail in calls to this method, and - * unless the CAS failed (which is unlikely), it will be - * there, so we hardly ever traverse much. - */ - return findNodeFromTail(node); - } - - /** - * Returns true if node is on sync queue by searching backwards from tail. - * Called only when needed by isOnSyncQueue. - * - * @return true if present - */ - private boolean findNodeFromTail(Node node) { - Node t = tail; - for (; ; ) { - if (t == node) { - return true; - } - if (t == null) { - return false; - } - t = t.prev; - } - } - - /** - * Transfers a node from a condition queue onto sync queue. - * Returns true if successful. - * - * @param node the node - * @return true if successfully transferred (else the node was - * cancelled before signal) - */ - final boolean transferForSignal(Node node) { - /* - * If cannot change waitStatus, the node has been cancelled. - */ - if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) { - return false; - } - - /* - * Splice onto queue and try to set waitStatus of predecessor to - * indicate that thread is (probably) waiting. If cancelled or - * attempt to set waitStatus fails, wake up to resync (in which - * case the waitStatus can be transiently and harmlessly wrong). - */ - Node p = enq(node); - int ws = p.waitStatus; - if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) { - LockSupport.unpark(node.thread); - } - return true; - } - - /** - * Transfers node, if necessary, to sync queue after a cancelled wait. - * Returns true if thread was cancelled before being signalled. - * - * @param node the node - * @return true if cancelled before the node was signalled - */ - final boolean transferAfterCancelledWait(Node node) { - if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) { - enq(node); - return true; - } - /* - * If we lost out to a signal(), then we can't proceed - * until it finishes its enq(). Cancelling during an - * incomplete transfer is both rare and transient, so just - * spin. - */ - while (!isOnSyncQueue(node)) { - Thread.yield(); - } - return false; - } - - /** - * Invokes release with current state value; returns saved state. - * Cancels node and throws exception on failure. - * - * @param node the condition node for this wait - * @return previous sync state - */ - final int fullyRelease(Node node) { - boolean failed = true; - try { - int savedState = getState(); - if (release(savedState)) { - failed = false; - return savedState; - } else { - throw new IllegalMonitorStateException(); - } - } finally { - if (failed) { - node.waitStatus = Node.CANCELLED; - } - } - } - - // Instrumentation methods for conditions - - /** - * Queries whether the given ConditionObject - * uses this synchronizer as its concurrent. - * - * @param condition the condition - * @return {@code true} if owned - * @throws NullPointerException if the condition is null - */ - public final boolean owns(ConditionObject condition) { - return condition.isOwnedBy(this); - } - - /** - * Queries whether any threads are waiting on the given condition - * associated with this synchronizer. Note that because timeouts - * and interrupts may occur at any time, a {@code true} return - * does not guarantee that a future {@code signal} will awaken - * any threads. This method is designed primarily for use in - * monitoring of the system state. - * - * @param condition the condition - * @return {@code true} if there are any waiting threads - * @throws IllegalMonitorStateException if exclusive synchronization - * is not held - * @throws IllegalArgumentException if the given condition is - * not associated with this synchronizer - * @throws NullPointerException if the condition is null - */ - public final boolean hasWaiters(ConditionObject condition) { - if (!owns(condition)) { - throw new IllegalArgumentException("Not owner"); - } - return condition.hasWaiters(); - } - - /** - * Returns an estimate of the number of threads waiting on the - * given condition associated with this synchronizer. Note that - * because timeouts and interrupts may occur at any time, the - * estimate serves only as an upper bound on the actual number of - * waiters. This method is designed for use in monitoring of the - * system state, not for synchronization control. - * - * @param condition the condition - * @return the estimated number of waiting threads - * @throws IllegalMonitorStateException if exclusive synchronization - * is not held - * @throws IllegalArgumentException if the given condition is - * not associated with this synchronizer - * @throws NullPointerException if the condition is null - */ - public final int getWaitQueueLength(ConditionObject condition) { - if (!owns(condition)) { - throw new IllegalArgumentException("Not owner"); - } - return condition.getWaitQueueLength(); - } - - /** - * Returns a collection containing those threads that may be - * waiting on the given condition associated with this - * synchronizer. Because the actual set of threads may change - * dynamically while constructing this result, the returned - * collection is only a best-effort estimate. The elements of the - * returned collection are in no particular order. - * - * @param condition the condition - * @return the collection of threads - * @throws IllegalMonitorStateException if exclusive synchronization - * is not held - * @throws IllegalArgumentException if the given condition is - * not associated with this synchronizer - * @throws NullPointerException if the condition is null - */ - public final Collection getWaitingThreads(ConditionObject condition) { - if (!owns(condition)) { - throw new IllegalArgumentException("Not owner"); - } - return condition.getWaitingThreads(); - } - - /** - * Condition implementation for a {@link - * AbstractQueuedSynchronizer} serving as the basis of a {@link - * - *

Method documentation for this class describes mechanics, - * not behavioral specifications from the point of view of Lock - * and Condition users. Exported versions of this class will in - * general need to be accompanied by documentation describing - * condition semantics that rely on those of the associated - * {@code AbstractQueuedSynchronizer}. - * - *

This class is Serializable, but all fields are transient, - * so deserialized conditions have no waiters. - */ - public class ConditionObject implements Condition, java.io.Serializable { - private static final long serialVersionUID = 1173984872572414699L; - /** - * First node of condition queue. - */ - private transient Node firstWaiter; - /** - * Last node of condition queue. - */ - private transient Node lastWaiter; - - /** - * Creates a new {@code ConditionObject} instance. - */ - public ConditionObject() { - } - - // Internal methods - - /** - * Adds a new waiter to wait queue. - * - * @return its new wait node - */ - private Node addConditionWaiter() { - Node t = lastWaiter; - // If lastWaiter is cancelled, clean out. - if (t != null && t.waitStatus != Node.CONDITION) { - unlinkCancelledWaiters(); - t = lastWaiter; - } - Node node = new Node(Thread.currentThread(), Node.CONDITION); - if (t == null) { - firstWaiter = node; - } else { - t.nextWaiter = node; - } - lastWaiter = node; - return node; - } - - /** - * Removes and transfers nodes until hit non-cancelled one or - * null. Split out from signal in part to encourage compilers - * to inline the case of no waiters. - * - * @param first (non-null) the first node on condition queue - */ - private void doSignal(Node first) { - do { - if ((firstWaiter = first.nextWaiter) == null) { - lastWaiter = null; - } - first.nextWaiter = null; - } while (!transferForSignal(first) && - (first = firstWaiter) != null); - } - - /** - * Removes and transfers all nodes. - * - * @param first (non-null) the first node on condition queue - */ - private void doSignalAll(Node first) { - lastWaiter = firstWaiter = null; - do { - Node next = first.nextWaiter; - first.nextWaiter = null; - transferForSignal(first); - first = next; - } while (first != null); - } - - /** - * Unlinks cancelled waiter nodes from condition queue. - * Called only while holding concurrent. This is called when - * cancellation occurred during condition wait, and upon - * insertion of a new waiter when lastWaiter is seen to have - * been cancelled. This method is needed to avoid garbage - * retention in the absence of signals. So even though it may - * require a full traversal, it comes into play only when - * timeouts or cancellations occur in the absence of - * signals. It traverses all nodes rather than stopping at a - * particular target to unlink all pointers to garbage nodes - * without requiring many re-traversals during cancellation - * storms. - */ - private void unlinkCancelledWaiters() { - Node t = firstWaiter; - Node trail = null; - while (t != null) { - Node next = t.nextWaiter; - if (t.waitStatus != Node.CONDITION) { - t.nextWaiter = null; - if (trail == null) { - firstWaiter = next; - } else { - trail.nextWaiter = next; - } - if (next == null) { - lastWaiter = trail; - } - } else { - trail = t; - } - t = next; - } - } - - // public methods - - /** - * Moves the longest-waiting thread, if one exists, from the - * wait queue for this condition to the wait queue for the - * owning concurrent. - * - * @throws IllegalMonitorStateException if {@link #isHeldExclusively} - * returns {@code false} - */ - @Override - public final void signal() { - if (!isHeldExclusively()) { - throw new IllegalMonitorStateException(); - } - Node first = firstWaiter; - if (first != null) { - doSignal(first); - } - } - - /** - * Moves all threads from the wait queue for this condition to - * the wait queue for the owning concurrent. - * - * @throws IllegalMonitorStateException if {@link #isHeldExclusively} - * returns {@code false} - */ - @Override - public final void signalAll() { - if (!isHeldExclusively()) { - throw new IllegalMonitorStateException(); - } - Node first = firstWaiter; - if (first != null) { - doSignalAll(first); - } - } - - /** - * Implements uninterruptible condition wait. - *

    - *
  1. Save concurrent state returned by {@link #getState}. - *
  2. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
  3. Block until signalled. - *
  4. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. - *
- */ - @Override - public final void awaitUninterruptibly() { - Node node = addConditionWaiter(); - int savedState = fullyRelease(node); - boolean interrupted = false; - while (!isOnSyncQueue(node)) { - LockSupport.park(this); - if (Thread.interrupted()) { - interrupted = true; - } - } - if (acquireQueued(node, savedState) || interrupted) { - selfInterrupt(); - } - } - - /* - * For interruptible waits, we need to track whether to throw - * InterruptedException, if interrupted while blocked on - * condition, versus reinterrupt current thread, if - * interrupted while blocked waiting to re-acquire. - */ - - /** - * Mode meaning to reinterrupt on exit from wait - */ - private static final int REINTERRUPT = 1; - /** - * Mode meaning to throw InterruptedException on exit from wait - */ - private static final int THROW_IE = -1; - - /** - * Checks for interrupt, returning THROW_IE if interrupted - * before signalled, REINTERRUPT if after signalled, or - * 0 if not interrupted. - */ - private int checkInterruptWhileWaiting(Node node) { - return Thread.interrupted() ? - (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) : - 0; - } - - /** - * Throws InterruptedException, reinterrupts current thread, or - * does nothing, depending on mode. - */ - private void reportInterruptAfterWait(int interruptMode) - throws InterruptedException { - if (interruptMode == THROW_IE) { - throw new InterruptedException(); - } else if (interruptMode == REINTERRUPT) { - selfInterrupt(); - } - } - - /** - * Implements interruptible condition wait. - *
    - *
  1. If current thread is interrupted, throw InterruptedException. - *
  2. Save concurrent state returned by {@link #getState}. - *
  3. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
  4. Block until signalled or interrupted. - *
  5. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. - *
  6. If interrupted while blocked in step 4, throw InterruptedException. - *
- */ - @Override - public final void await() throws InterruptedException { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - Node node = addConditionWaiter(); - int savedState = fullyRelease(node); - int interruptMode = 0; - while (!isOnSyncQueue(node)) { - LockSupport.park(this); - if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) { - break; - } - } - if (acquireQueued(node, savedState) && interruptMode != THROW_IE) { - interruptMode = REINTERRUPT; - } - if (node.nextWaiter != null) // clean up if cancelled - { - unlinkCancelledWaiters(); - } - if (interruptMode != 0) { - reportInterruptAfterWait(interruptMode); - } - } - - /** - * Implements timed condition wait. - *
    - *
  1. If current thread is interrupted, throw InterruptedException. - *
  2. Save concurrent state returned by {@link #getState}. - *
  3. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
  4. Block until signalled, interrupted, or timed out. - *
  5. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. - *
  6. If interrupted while blocked in step 4, throw InterruptedException. - *
- */ - @Override - public final long awaitNanos(long nanosTimeout) - throws InterruptedException { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - Node node = addConditionWaiter(); - int savedState = fullyRelease(node); - final long deadline = System.nanoTime() + nanosTimeout; - int interruptMode = 0; - while (!isOnSyncQueue(node)) { - if (nanosTimeout <= 0L) { - transferAfterCancelledWait(node); - break; - } - if (nanosTimeout >= spinForTimeoutThreshold) { - LockSupport.parkNanos(this, nanosTimeout); - } - if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) { - break; - } - nanosTimeout = deadline - System.nanoTime(); - } - if (acquireQueued(node, savedState) && interruptMode != THROW_IE) { - interruptMode = REINTERRUPT; - } - if (node.nextWaiter != null) { - unlinkCancelledWaiters(); - } - if (interruptMode != 0) { - reportInterruptAfterWait(interruptMode); - } - return deadline - System.nanoTime(); - } - - /** - * Implements absolute timed condition wait. - *
    - *
  1. If current thread is interrupted, throw InterruptedException. - *
  2. Save concurrent state returned by {@link #getState}. - *
  3. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
  4. Block until signalled, interrupted, or timed out. - *
  5. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. - *
  6. If interrupted while blocked in step 4, throw InterruptedException. - *
  7. If timed out while blocked in step 4, return false, else true. - *
- */ - @Override - public final boolean awaitUntil(Date deadline) - throws InterruptedException { - long abstime = deadline.getTime(); - if (Thread.interrupted()) { - throw new InterruptedException(); - } - Node node = addConditionWaiter(); - int savedState = fullyRelease(node); - boolean timedout = false; - int interruptMode = 0; - while (!isOnSyncQueue(node)) { - if (System.currentTimeMillis() > abstime) { - timedout = transferAfterCancelledWait(node); - break; - } - LockSupport.parkUntil(this, abstime); - if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) { - break; - } - } - if (acquireQueued(node, savedState) && interruptMode != THROW_IE) { - interruptMode = REINTERRUPT; - } - if (node.nextWaiter != null) { - unlinkCancelledWaiters(); - } - if (interruptMode != 0) { - reportInterruptAfterWait(interruptMode); - } - return !timedout; - } - - /** - * Implements timed condition wait. - *
    - *
  1. If current thread is interrupted, throw InterruptedException. - *
  2. Save concurrent state returned by {@link #getState}. - *
  3. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
  4. Block until signalled, interrupted, or timed out. - *
  5. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. - *
  6. If interrupted while blocked in step 4, throw InterruptedException. - *
  7. If timed out while blocked in step 4, return false, else true. - *
- */ - @Override - public final boolean await(long time, TimeUnit unit) - throws InterruptedException { - long nanosTimeout = unit.toNanos(time); - if (Thread.interrupted()) { - throw new InterruptedException(); - } - Node node = addConditionWaiter(); - int savedState = fullyRelease(node); - final long deadline = System.nanoTime() + nanosTimeout; - boolean timedout = false; - int interruptMode = 0; - while (!isOnSyncQueue(node)) { - if (nanosTimeout <= 0L) { - timedout = transferAfterCancelledWait(node); - break; - } - if (nanosTimeout >= spinForTimeoutThreshold) { - LockSupport.parkNanos(this, nanosTimeout); - } - if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) { - break; - } - nanosTimeout = deadline - System.nanoTime(); - } - if (acquireQueued(node, savedState) && interruptMode != THROW_IE) { - interruptMode = REINTERRUPT; - } - if (node.nextWaiter != null) { - unlinkCancelledWaiters(); - } - if (interruptMode != 0) { - reportInterruptAfterWait(interruptMode); - } - return !timedout; - } - - // support for instrumentation - - /** - * Returns true if this condition was created by the given - * synchronization object. - * - * @return {@code true} if owned - */ - final boolean isOwnedBy(AbstractQueuedSynchronizer sync) { - return sync == AbstractQueuedSynchronizer.this; - } - - /** - * Queries whether any threads are waiting on this condition. - * Implements {@link AbstractQueuedSynchronizer#hasWaiters(ConditionObject)}. - * - * @return {@code true} if there are any waiting threads - * @throws IllegalMonitorStateException if {@link #isHeldExclusively} - * returns {@code false} - */ - protected final boolean hasWaiters() { - if (!isHeldExclusively()) { - throw new IllegalMonitorStateException(); - } - for (Node w = firstWaiter; w != null; w = w.nextWaiter) { - if (w.waitStatus == Node.CONDITION) { - return true; - } - } - return false; - } - - /** - * Returns an estimate of the number of threads waiting on - * this condition. - * Implements {@link AbstractQueuedSynchronizer#getWaitQueueLength(ConditionObject)}. - * - * @return the estimated number of waiting threads - * @throws IllegalMonitorStateException if {@link #isHeldExclusively} - * returns {@code false} - */ - protected final int getWaitQueueLength() { - if (!isHeldExclusively()) { - throw new IllegalMonitorStateException(); - } - int n = 0; - for (Node w = firstWaiter; w != null; w = w.nextWaiter) { - if (w.waitStatus == Node.CONDITION) { - ++n; - } - } - return n; - } - - /** - * Returns a collection containing those threads that may be - * waiting on this Condition. - * Implements {@link AbstractQueuedSynchronizer#getWaitingThreads(ConditionObject)}. - * - * @return the collection of threads - * @throws IllegalMonitorStateException if {@link #isHeldExclusively} - * returns {@code false} - */ - protected final Collection getWaitingThreads() { - if (!isHeldExclusively()) { - throw new IllegalMonitorStateException(); - } - ArrayList list = new ArrayList(); - for (Node w = firstWaiter; w != null; w = w.nextWaiter) { - if (w.waitStatus == Node.CONDITION) { - Thread t = w.thread; - if (t != null) { - list.add(t); - } - } - } - return list; - } - } - - /** - * Setup to support compareAndSet. We need to natively implement - * this here: For the sake of permitting future enhancements, we - * cannot explicitly subclass AtomicInteger, which would be - * efficient and useful otherwise. So, as the lesser of evils, we - * natively implement using hotspot intrinsics API. And while we - * are at it, we do the same for other CASable fields (which could - * otherwise be done with atomic field updaters). - */ - private static final Unsafe unsafe = Unsafe.getUnsafe(); - private static final long stateOffset; - private static final long headOffset; - private static final long tailOffset; - private static final long waitStatusOffset; - private static final long nextOffset; - - static { - try { - stateOffset = unsafe.objectFieldOffset - (AbstractQueuedSynchronizer.class.getDeclaredField("state")); - headOffset = unsafe.objectFieldOffset - (AbstractQueuedSynchronizer.class.getDeclaredField("head")); - tailOffset = unsafe.objectFieldOffset - (AbstractQueuedSynchronizer.class.getDeclaredField("tail")); - waitStatusOffset = unsafe.objectFieldOffset - (Node.class.getDeclaredField("waitStatus")); - nextOffset = unsafe.objectFieldOffset - (Node.class.getDeclaredField("next")); - - } catch (Exception ex) { - throw new Error(ex); - } - } - - /** - * CAS head field. Used only by enq. - */ - private final boolean compareAndSetHead(Node update) { - return unsafe.compareAndSwapObject(this, headOffset, null, update); - } - - /** - * CAS tail field. Used only by enq. - */ - private final boolean compareAndSetTail(Node expect, Node update) { - return unsafe.compareAndSwapObject(this, tailOffset, expect, update); - } - - /** - * CAS waitStatus field of a node. - */ - private static final boolean compareAndSetWaitStatus(Node node, - int expect, - int update) { - return unsafe.compareAndSwapInt(node, waitStatusOffset, - expect, update); - } - - /** - * CAS next field of a node. - */ - private static final boolean compareAndSetNext(Node node, - Node expect, - Node update) { - return unsafe.compareAndSwapObject(node, nextOffset, expect, update); - } -} diff --git a/src/main/java/jdk/concurrent/AQS/ConditionObject.java b/src/main/java/jdk/concurrent/AQS/ConditionObject.java deleted file mode 100644 index 85add893..00000000 --- a/src/main/java/jdk/concurrent/AQS/ConditionObject.java +++ /dev/null @@ -1,164 +0,0 @@ -package jdk.concurrent.AQS; - -import sun.misc.Unsafe; - -import java.io.Serializable; -import java.util.concurrent.locks.AbstractQueuedLongSynchronizer; - -/** - * Created by nibnait on 2019-08-27 - */ -public class ConditionObject implements Serializable { - - private static final long serialVersionUID = 1173984872572414699L; - /** First node of condition queue. */ - private transient Node firstWaiter; - /** Last node of condition queue. */ - private transient Node lastWaiter; - - /** - * Creates a new {@code ConditionObject} instance. - */ - public ConditionObject() { } - - // Internal methods - - /** - * Adds a new waiter to wait queue. - * @return its new wait node - */ - private Node addConditionWaiter() { - Node t = lastWaiter; - // 尾节点已经Cancel, 直接进行清除. - - /** - * 当Condition进行 awiat 超时或被中断时, Condition里面的节点是没有被删除掉的, - * 需要其他await 在将线程加入 Condition Queue 时调用addConditionWaiter而进而删除, - * 或 await 操作差不多结束时, 调用 "node.nextWaiter != null" 进行判断而删除 - * (PS: 通过 signal 进行唤醒时 node.nextWaiter 会被置空, 而中断和超时时不会) - */ - if (t != null && t.waitStatus != Node.CONDITION) { - /** - * 调用 unlinkCancelledWaiters 对 "waitStatus != Node.CONDITION" 的节点进行删除 - * (在Condition里面的Node的waitStatus 要么是CONDITION(正常), 要么就是 0 - * (signal/timeout/interrupt)) - */ - unlinkCancelledWaiters(); - t = lastWaiter; - } - // 将线程封装成 node 准备放入 Condition Queue 里面 - Node node = new Node(Thread.currentThread(), Node.CONDITION); - if (t == null) { - //Condition Queue 是空的 - firstWaiter = node; - } else { - // 追加到 queue 尾部 - t.nextWaiter = node; - } - lastWaiter = node; - return node; - } - - - /** - * Unlinks cancelled waiter nodes from condition queue. - * Called only while holding concurrent. This is called when - * cancellation occurred during condition wait, and upon - * insertion of a new waiter when lastWaiter is seen to have - * been cancelled. This method is needed to avoid garbage - * retention in the absence of signals. So even though it may - * require a full traversal, it comes into play only when - * timeouts or cancellations occur in the absence of - * signals. It traverses all nodes rather than stopping at a - * particular target to unlink all pointers to garbage nodes - * without requiring many re-traversals during cancellation - * storms. - */ - private void unlinkCancelledWaiters() { - Node t = firstWaiter; - Node trail = null; - while (t != null) { - Node next = t.nextWaiter; - if (t.waitStatus != Node.CONDITION) { - t.nextWaiter = null; - if (trail == null) { - firstWaiter = next; - } else { - trail.nextWaiter = next; - } - if (next == null) { - lastWaiter = trail; - } - } else { - trail = t; - } - t = next; - } - } - - - /** - * Setup to support compareAndSet. We need to natively implement - * this here: For the sake of permitting future enhancements, we - * cannot explicitly subclass AtomicLong, which would be - * efficient and useful otherwise. So, as the lesser of evils, we - * natively implement using hotspot intrinsics API. And while we - * are at it, we do the same for other CASable fields (which could - * otherwise be done with atomic field updaters). - */ - private static final Unsafe unsafe = Unsafe.getUnsafe(); - private static final long stateOffset; - private static final long headOffset; - private static final long tailOffset; - private static final long waitStatusOffset; - private static final long nextOffset; - - static { - try { - stateOffset = unsafe.objectFieldOffset - (AbstractQueuedLongSynchronizer.class.getDeclaredField("state")); - headOffset = unsafe.objectFieldOffset - (AbstractQueuedLongSynchronizer.class.getDeclaredField("head")); - tailOffset = unsafe.objectFieldOffset - (AbstractQueuedLongSynchronizer.class.getDeclaredField("tail")); - waitStatusOffset = unsafe.objectFieldOffset - (Node.class.getDeclaredField("waitStatus")); - nextOffset = unsafe.objectFieldOffset - (Node.class.getDeclaredField("next")); - - } catch (Exception ex) { throw new Error(ex); } - } - - /** - * CAS head field. Used only by enq. - */ - private final boolean compareAndSetHead(Node update) { - return unsafe.compareAndSwapObject(this, headOffset, null, update); - } - - /** - * CAS tail field. Used only by enq. - */ - private final boolean compareAndSetTail(Node expect, Node update) { - return unsafe.compareAndSwapObject(this, tailOffset, expect, update); - } - - /** - * CAS waitStatus field of a node. - */ - private static final boolean compareAndSetWaitStatus(Node node, - int expect, - int update) { - return unsafe.compareAndSwapInt(node, waitStatusOffset, - expect, update); - } - - /** - * CAS next field of a node. - */ - private static final boolean compareAndSetNext(Node node, - Node expect, - Node update) { - return unsafe.compareAndSwapObject(node, nextOffset, expect, update); - } -} diff --git a/src/main/java/jdk/concurrent/AQS/Node.java b/src/main/java/jdk/concurrent/AQS/Node.java deleted file mode 100644 index ceeb95fe..00000000 --- a/src/main/java/jdk/concurrent/AQS/Node.java +++ /dev/null @@ -1,103 +0,0 @@ -package jdk.concurrent.AQS; - -/** - * Node 节点,用于存放获取线程的节点,存在于 Sync Queue, Condition Queue - * 而其主要就是 nextWaiter 标记共享还是独占, - * waitStatus 标记node的状态。 - * Created by nibnait on 2019-08-27 - */ -public class Node { - /** - * 标识节点是否是 共享的节点(这样的节点只存在于 Sync Queue 里面) - */ - static final Node SHARED = new Node(); - /** - * 独占节点标识 - */ - static final Node EXCLUSIVE = null; - - /** - * CANCELLED 说明节点已经被取消获取 concurrent 了(一般是由于 interrupt 或 timeout 导致的) - * 很多时候是在 cancelAcquire 里面进行设置这个标识 - */ - static final int CANCELLED = 1; - /** - * waitStatus value to indicate successor's thread needs unparking - * SIGNAL 标识当前节点的后继节点需要唤醒(PS: 这个通常是在 独占模式下使用, 在共享模式下有时用 PROPAGATE) - */ - static final int SIGNAL = -1; - /** - * waitStatus value to indicate thread is waiting on condition - * 当前节点在 Condition Queue 里面 - */ - static final int CONDITION = -2; - /** - * waitStatus value to indicate the next acquireShared should - * unconditionally propagate - * 当前节点获取到 concurrent 或进行 release concurrent 时, 共享模式的最终状态是 PROPAGATE - * (PS: 有可能共享模式的节点变成 PROPAGATE 之前就被其后继节点抢占 head 节点, 而从Sync Queue中被踢出掉) - */ - static final int PROPAGATE = -3; - - volatile int waitStatus; - - /** - * 节点在 Sync Queue 里面时的前继节点(主要来进行 skip CANCELLED 的节点) - * 注意: 根据 addWaiter方法: - * 1. prev节点在队列里面, 则 prev != null 肯定成立 - * 2. prev != null 成立, 不一定 node 就在 Sync Queue 里面 - */ - volatile Node prev; - - /** - * Node 在 Sync Queue 里面的后继节点, 主要是在release concurrent 时进行后继节点的唤醒 - * 而后继节点在前继节点上打上 SIGNAL 标识, 来提醒他 release concurrent 时需要唤醒 - */ - volatile Node next; - - /** - * 获取 concurrent 的引用 - */ - volatile Thread thread; - - /** - * 作用分成两种: - * 1. 在 Sync Queue 里面, nextWaiter用来判断节点是 共享模式, 还是独占模式 - * 2. 在 Condition queue 里面, 节点主要是链接且后继节点 (Condition queue是一个单向的, 不支持并发的 list) - */ - Node nextWaiter; - - /** - * 当前节点是否是共享模式 - */ - final boolean isShared() { - return nextWaiter == SHARED; - } - - /** - * 获取 node 的前继节点 - */ - final Node predecessor() throws NullPointerException { - Node p = prev; - if (p == null) { - throw new NullPointerException(); - } else { - return p; - } - } - - Node() { // Used to establish initial head or SHARED marker - } - - // 初始化 Node 用于 Sync Queue 里面 - Node(Thread thread, Node mode) { // Used by addWaiter - this.nextWaiter = mode; - this.thread = thread; - } - - //初始化 Node 用于 Condition Queue 里面 - Node(Thread thread, int waitStatus) { // Used by Condition - this.waitStatus = waitStatus; - this.thread = thread; - } -} diff --git a/src/main/java/jdk/concurrent/AQS/README.md b/src/main/java/jdk/concurrent/AQS/README.md deleted file mode 100644 index 5c9439a8..00000000 --- a/src/main/java/jdk/concurrent/AQS/README.md +++ /dev/null @@ -1,24 +0,0 @@ -整个 AQS 分为以下几部分: - -- **Node** 节点, 用于存放获取线程的节点, 存在于 Sync Queue, Condition Queue, 这些节点主要的区分在于 waitStatus 的值(下面会详细叙述) -- **Condition Queue**,这个队列是用于独占模式中,只有用到 Condition.awaitXX 时才会将 node加到 tail 上(PS: 在使用 Condition的前提是已经获取 Lock) -- **Sync Queue**,独占共享的模式中均会使用到的存放 Node 的 CLH queue(主要特点是队列中总有一个 dummy 节点,后继节点获取锁的条件由前继节点决定,前继节点在释放 lock 时会唤醒sleep中的后继节点) -- **ConditionObject**,用于独占的模式,主要是线程释放lock,加入Condition Queue, 并进行相应的 signal 操作 - -独占的获取lock (acquire release) 例如 ReentrantLock。 - -共享的获取lock (acquireShared releaseShared)。 例如 ReeantrantReadWriteLock, Semaphore, CountDownLatch - -# Node - -waitStatus 变化过程: - -1. 独占模式下: 0(初始) -> SIGNAL(被后继节点标记为release需要唤醒后继节点) -> 0 (等释放好lock, 会恢复到0) -2. 独占模式 + 使用 Condition情况下: 0(初始) -> SIGNAL(被后继节点标记为release需要唤醒后继节点) -> 0 (等释放好lock, 会恢复到0)其上可能涉及 中断与超时, 只是多了一个 CANCELLED, 当节点变成 CANCELLED, 后就等着被清除。 -3. 共享模式下: 0(初始) -> PROPAGATE(获取 lock 或release lock 时) (获取 lock 时会调用 setHeadAndPropagate 来进行 传递式的唤醒后继节点, 直到碰到 独占模式的节点) -4. 共享模式 + 独占模式下: 0(初始) -> SIGNAL(被后继节点标记为release需要唤醒后继节点) -> 0 (等释放好lock, 会恢复到0) - -其上的这些状态变化主要在: doReleaseShared , shouldParkAfterFailedAcquire 里面。 - -# Condition Queue - diff --git a/src/main/java/jdk/concurrent/AQS/ReentrantLock.java b/src/main/java/jdk/concurrent/AQS/ReentrantLock.java deleted file mode 100644 index 425867fd..00000000 --- a/src/main/java/jdk/concurrent/AQS/ReentrantLock.java +++ /dev/null @@ -1,269 +0,0 @@ -package jdk.concurrent.AQS; - -import java.util.concurrent.TimeUnit; -import java.util.Collection; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; - -public class ReentrantLock implements Lock, java.io.Serializable { - private static final long serialVersionUID = 7373984872572414699L; - /** 同步器提供所有的实现机制 */ - private final Sync sync; - - abstract static class Sync extends AbstractQueuedSynchronizer { - private static final long serialVersionUID = -5179523762034025860L; - - abstract void lock(); - - final boolean nonfairTryAcquire(int acquires) { - final Thread current = Thread.currentThread(); - int c = getState(); - if (c == 0) { - if (compareAndSetState(0, acquires)) { - setExclusiveOwnerThread(current); - return true; - } - } - else if (current == getExclusiveOwnerThread()) { - int nextc = c + acquires; - if (nextc < 0) // overflow - { - throw new Error("Maximum concurrent count exceeded"); - } - setState(nextc); - return true; - } - return false; - } - - @Override - protected final boolean tryRelease(int releases) { - int c = getState() - releases; - if (Thread.currentThread() != getExclusiveOwnerThread()) { - throw new IllegalMonitorStateException(); - } - boolean free = false; - if (c == 0) { - free = true; - setExclusiveOwnerThread(null); - } - setState(c); - return free; - } - - @Override - protected final boolean isHeldExclusively() { - return getExclusiveOwnerThread() == Thread.currentThread(); - } - - final ConditionObject newCondition() { - return new ConditionObject(); - } - - final Thread getOwner() { - return getState() == 0 ? null : getExclusiveOwnerThread(); - } - - final int getHoldCount() { - return isHeldExclusively() ? getState() : 0; - } - - final boolean isLocked() { - return getState() != 0; - } - - /** - * 从流中重构实例(即反序列化它)。 - */ - private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException { - s.defaultReadObject(); - setState(0); // reset to unlocked state - } - } - - /** - * 同步对象,用于非公平锁 - */ - static final class NonfairSync extends Sync { - private static final long serialVersionUID = 7316153563782823691L; - - @Override - final void lock() { - if (compareAndSetState(0, 1)) { - setExclusiveOwnerThread(Thread.currentThread()); - } else { - acquire(1); - } - } - - @Override - protected final boolean tryAcquire(int acquires) { - return nonfairTryAcquire(acquires); - } - } - - /** - * 公平锁的同步器 - */ - static final class FairSync extends Sync { - private static final long serialVersionUID = -3000897897090466540L; - - @Override - final void lock() { - acquire(1); - } - - /** - * 设置state - * @param acquires - * @return - */ - @Override - protected final boolean tryAcquire(int acquires) { - final Thread current = Thread.currentThread(); - int c = getState(); - if (c == 0) { - /** - * 当前队列中没有线程被加锁 - * 判断如果此时还没有其他线程运行,则将state设为1。(CAS 是为了防止多个线程初始化state) - * 设置当前线程为独占锁 - */ - if (!hasQueuedPredecessors() && - compareAndSetState(0, acquires)) { - setExclusiveOwnerThread(current); - return true; - } - } - /** - * 如果state不为0,说明已经有线程独占锁了 - * 如果独占锁是当前线程(ReentrantLock是可重入锁) - * 则state ++ - * - */ - else if (current == getExclusiveOwnerThread()) { - int nextc = c + acquires; - if (nextc < 0) { - throw new Error("Maximum concurrent count exceeded"); - } - setState(nextc); - return true; - } - return false; - } - } - - public ReentrantLock() { - sync = new NonfairSync(); - } - - public ReentrantLock(boolean fair) { - sync = fair ? new FairSync() : new NonfairSync(); - } - - @Override - public void lock() { - sync.lock(); - } - - @Override - public void lockInterruptibly() throws InterruptedException { - sync.acquireInterruptibly(1); - } - - @Override - public boolean tryLock() { - return sync.nonfairTryAcquire(1); - } - - @Override - public boolean tryLock(long timeout, TimeUnit unit) - throws InterruptedException { - return sync.tryAcquireNanos(1, unit.toNanos(timeout)); - } - - @Override - public void unlock() { - sync.release(1); - } - - @Override - public Condition newCondition() { - return sync.newCondition(); - } - - public int getHoldCount() { - return sync.getHoldCount(); - } - - public boolean isHeldByCurrentThread() { - return sync.isHeldExclusively(); - } - - public boolean isLocked() { - return sync.isLocked(); - } - - public final boolean isFair() { - return sync instanceof FairSync; - } - - protected Thread getOwner() { - return sync.getOwner(); - } - - public final boolean hasQueuedThreads() { - return sync.hasQueuedThreads(); - } - - public final boolean hasQueuedThread(Thread thread) { - return sync.isQueued(thread); - } - - public final int getQueueLength() { - return sync.getQueueLength(); - } - - protected Collection getQueuedThreads() { - return sync.getQueuedThreads(); - } - - public boolean hasWaiters(Condition condition) { - if (condition == null) { - throw new NullPointerException(); - } - if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) { - throw new IllegalArgumentException("not owner"); - } - return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition); - } - - public int getWaitQueueLength(Condition condition) { - if (condition == null) { - throw new NullPointerException(); - } - if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) { - throw new IllegalArgumentException("not owner"); - } - return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition); - } - - protected Collection getWaitingThreads(Condition condition) { - if (condition == null) { - throw new NullPointerException(); - } - if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) { - throw new IllegalArgumentException("not owner"); - } - return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition); - } - - @Override - public String toString() { - Thread o = sync.getOwner(); - return super.toString() + ((o == null) ? - "[Unlocked]" : - "[Locked by thread " + o.getName() + "]"); - } -} - diff --git a/src/main/java/jdk/concurrent/AQS/Semaphore.java b/src/main/java/jdk/concurrent/AQS/Semaphore.java deleted file mode 100644 index ca8c07af..00000000 --- a/src/main/java/jdk/concurrent/AQS/Semaphore.java +++ /dev/null @@ -1,208 +0,0 @@ -package jdk.concurrent.AQS; - -import java.util.Collection; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.AbstractQueuedSynchronizer; - -public class Semaphore implements java.io.Serializable { - private static final long serialVersionUID = -3222578661600680210L; - private final Sync sync; - - abstract static class Sync extends AbstractQueuedSynchronizer { - private static final long serialVersionUID = 1192457210091910933L; - - Sync(int permits) { - setState(permits); - } - - final int getPermits() { - return getState(); - } - - final int nonfairTryAcquireShared(int acquires) { - for (;;) { - int available = getState(); - int remaining = available - acquires; - if (remaining < 0 || - compareAndSetState(available, remaining)) { - return remaining; - } - } - } - - @Override - protected final boolean tryReleaseShared(int releases) { - for (;;) { - int current = getState(); - int next = current + releases; - if (next < current) // overflow - { - throw new Error("Maximum permit count exceeded"); - } - if (compareAndSetState(current, next)) { - return true; - } - } - } - - final void reducePermits(int reductions) { - for (;;) { - int current = getState(); - int next = current - reductions; - if (next > current) // underflow - { - throw new Error("Permit count underflow"); - } - if (compareAndSetState(current, next)) { - return; - } - } - } - - final int drainPermits() { - for (;;) { - int current = getState(); - if (current == 0 || compareAndSetState(current, 0)) { - return current; - } - } - } - } - - static final class NonfairSync extends Sync { - private static final long serialVersionUID = -2694183684443567898L; - - NonfairSync(int permits) { - super(permits); - } - - @Override - protected int tryAcquireShared(int acquires) { - return nonfairTryAcquireShared(acquires); - } - } - - static final class FairSync extends Sync { - private static final long serialVersionUID = 2014338818796000944L; - - FairSync(int permits) { - super(permits); - } - - @Override - protected int tryAcquireShared(int acquires) { - for (;;) { - if (hasQueuedPredecessors()) { - return -1; - } - int available = getState(); - int remaining = available - acquires; - if (remaining < 0 || - compareAndSetState(available, remaining)) { - return remaining; - } - } - } - } - - public Semaphore(int permits) { - sync = new NonfairSync(permits); - } - - public Semaphore(int permits, boolean fair) { - sync = fair ? new FairSync(permits) : new NonfairSync(permits); - } - - public void acquire() throws InterruptedException { - sync.acquireSharedInterruptibly(1); - } - - public void acquireUninterruptibly() { - sync.acquireShared(1); - } - - public boolean tryAcquire() { - return sync.nonfairTryAcquireShared(1) >= 0; - } - - public boolean tryAcquire(long timeout, TimeUnit unit) - throws InterruptedException { - return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); - } - - public void release() { - sync.releaseShared(1); - } - - public void acquire(int permits) throws InterruptedException { - if (permits < 0) { - throw new IllegalArgumentException(); - } - sync.acquireSharedInterruptibly(permits); - } - - public void acquireUninterruptibly(int permits) { - if (permits < 0) { - throw new IllegalArgumentException(); - } - sync.acquireShared(permits); - } - - public boolean tryAcquire(int permits) { - if (permits < 0) { - throw new IllegalArgumentException(); - } - return sync.nonfairTryAcquireShared(permits) >= 0; - } - - public boolean tryAcquire(int permits, long timeout, TimeUnit unit) - throws InterruptedException { - if (permits < 0) { - throw new IllegalArgumentException(); - } - return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout)); - } - - public void release(int permits) { - if (permits < 0) { - throw new IllegalArgumentException(); - } - sync.releaseShared(permits); - } - - public int availablePermits() { - return sync.getPermits(); - } - - public int drainPermits() { - return sync.drainPermits(); - } - - protected void reducePermits(int reduction) { - if (reduction < 0) { - throw new IllegalArgumentException(); - } - sync.reducePermits(reduction); - } - - public boolean isFair() { - return sync instanceof FairSync; - } - - public final boolean hasQueuedThreads() { - return sync.hasQueuedThreads(); - } - - public final int getQueueLength() { - return sync.getQueueLength(); - } - - protected Collection getQueuedThreads() { - return sync.getQueuedThreads(); - } - - @Override - public String toString() { - return super.toString() + "[Permits = " + sync.getPermits() + "]"; - } -} diff --git a/src/main/java/jdk/concurrent/demo/ObjectPool.java b/src/main/java/jdk/concurrent/demo/ObjectPool.java deleted file mode 100644 index 30aafaf3..00000000 --- a/src/main/java/jdk/concurrent/demo/ObjectPool.java +++ /dev/null @@ -1,59 +0,0 @@ -package jdk.concurrent.demo; - -import jdk.concurrent.AQS.Semaphore; -import junit.framework.TestCase; -import org.junit.Test; - -import java.util.List; -import java.util.Vector; -import java.util.function.Function; - -/** - * 一个对象池 - * 使用Semaphore 信号量来做限流 - * - * @author nibnait - * @version $Id: ObjectPool.java, v 0.1 2019-08-29 下午4:48 nibnait Exp $$ - */ -public class ObjectPool extends TestCase { - - class Pool { - List pool; - - Semaphore semaphore; - - public Pool(int size, T t) { - this.pool = new Vector() { - }; - for (int i = 0; i < size; i++) { - pool.add(t); - } - this.semaphore = new Semaphore(size); - } - - // 利用对象池的对象,调用func - R exec(Function func) throws InterruptedException { - T t = null; - try { - semaphore.acquire(); - t = pool.remove(0); - return func.apply(t); - } finally { - pool.add(t); - semaphore.release(); - } - } - } - - // 创建对象池 - @Test - public void testMain() throws InterruptedException { - Pool pool = new Pool(10, 2); - // 通过对象池获取t,之后执行 - pool.exec(t -> { - System.out.println("方法开始执行。。。。入参:" + t); - return t.toString(); - }); - } - -} \ No newline at end of file