diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/30. \346\234\272\345\231\250\344\272\272\347\232\204\350\277\220\345\212\250\350\214\203\345\233\264.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/30. \346\234\272\345\231\250\344\272\272\347\232\204\350\277\220\345\212\250\350\214\203\345\233\264.md" new file mode 100644 index 0000000..0c562eb --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/30. \346\234\272\345\231\250\344\272\272\347\232\204\350\277\220\345\212\250\350\214\203\345\233\264.md" @@ -0,0 +1,85 @@ +### Title +地上有一个 m 行和 n 列的方格,横纵坐标范围分别是 0∼m−1 和 0∼n−1。 + +一个机器人从坐标 (0,0) 的格子开始移动,每一次只能向左,右,上,下四个方向移动一格。 + +但是不能进入行坐标和列坐标的数位之和大于 k 的格子。 + +请问该机器人能够达到多少个格子? + +注意: +1. 0<=m<=50 +2. 0<=n<=50 +3. 0<=k<=100 + +### Demo +``` +输入:k=18, m=40, n=40 + +输出:1484 + +解释:当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。 + 但是,它不能进入方格(35,38),因为3+5+3+8 = 19。 +``` + +### Analysis +用标记法,对已走过的路径进行标记,然后递归判断上下左右是否满足下一个字符,以此类推 + + +### Coding +```java +class Solution { + private static class Node { + int first; + int second; + + public Node(int first, int second) { + this.first = first; + this.second = second; + } + } + + private boolean[][] visited = new boolean[55][55]; + private static final int[][] nxt = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + public int movingCount(int threshold, int rows, int cols) { + if (rows < 1 || cols < 1) + return 0; + + Queue que = new LinkedList<>(); + visited[0][0] = true; + que.add(new Node(0, 0)); + int res = 0; + + while (!que.isEmpty()) { + Node popNode = que.poll(); + res++; + + for (int i = 0; i < nxt.length; i++) { + int fx = popNode.first + nxt[i][0], fy = popNode.second + nxt[i][1]; + if (fx >= 0 && fy >= 0 && fx < rows && fy < cols && getSum(fx, fy) <= threshold && !visited[fx][fy]) { + que.add(new Node(fx, fy)); + visited[fx][fy] = true; + } + } + } + + return res; + } + + private int getSum(int rows, int cols) { + int s = 0; + while (rows > 0) { + s += rows % 10; + rows /= 10; + } + + while (cols > 0) { + s += cols % 10; + cols /= 10; + } + + return s; + } +} +``` \ No newline at end of file diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/31. \347\237\251\351\230\265\344\270\255\347\232\204\350\267\257\345\276\204.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/31. \347\237\251\351\230\265\344\270\255\347\232\204\350\267\257\345\276\204.md" new file mode 100644 index 0000000..2a679b5 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/31. \347\237\251\351\230\265\344\270\255\347\232\204\350\267\257\345\276\204.md" @@ -0,0 +1,78 @@ +### Title +请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。 + +路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。 + +如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 + +注意: + +输入的路径不为空; +所有出现的字符均为大写英文字母; +数据范围 +矩阵中元素的总个数 [0,900]。 +路径字符串的总长度 [0,900]。 + +### Demo +``` +matrix= +[ + ["A","B","C","E"], + ["S","F","C","S"], + ["A","D","E","E"] +] + + +str="BCCE" , return "true" + +str="ASAE" , return "false" +``` + + +### Analysis +用标记法,对已走过的路径进行标记,然后递归判断上下左右是否满足下一个字符,以此类推 + +### Coding +```java +class Solution { + public boolean hasPath(char[][] matrix, String str) { + if(matrix == null || str == "" || matrix.length == 0){ + return false; + } + int rows = matrix.length; + int cols = matrix[0].length; + boolean [][] visited = new boolean[rows][cols]; + int length = 0; + for(int i = 0; i< rows; i++){ + for(int j = 0; j < cols; j++){ + if(dfs(matrix, visited, str, rows, cols, i, j, length)){ + return true; + } + } + } + return false; + } + + public boolean dfs(char[][] matrix, boolean[][] visited, String str, int rows, int cols, int row, int col, int length){ + // 如果未读且 + int strLength = str.length(); + boolean flag = false; + if(row>=0 && row < rows && col >=0 && col < cols && visited[row][col] == false && str.charAt(length) == matrix[row][col]){ + length++; + visited[row][col] = true; + if(length == strLength){ + return true; + } + flag = dfs(matrix, visited, str, rows, cols, row+1, col, length)|| + dfs(matrix, visited, str, rows, cols, row, col-1, length)|| + dfs(matrix, visited, str, rows, cols, row, col+1, length)|| + dfs(matrix, visited, str, rows, cols, row-1, col, length); + if(!flag){ + length--; + visited[row][col] = false; + } + } + return flag; + } +} +``` \ No newline at end of file diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/32. \345\211\252\347\273\263\345\255\220.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/32. \345\211\252\347\273\263\345\255\220.md" new file mode 100644 index 0000000..e63a107 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/32. \345\211\252\347\273\263\345\255\220.md" @@ -0,0 +1,70 @@ +### Title +给你一根长度为 n 绳子,请把绳子剪成 m 段(m、n 都是整数,2≤n≤58 并且 m≥2)。 + +每段的绳子的长度记为 k[1]、k[2]、……、k[m]。 + +k[1]k[2]…k[m] 可能的最大乘积是多少? + +例如当绳子的长度是 8 时,我们把它剪成长度分别为 2、3、3 的三段,此时得到最大的乘积 18。 +### Demo +``` +输入:8 + +输出:18 +``` + + +### Analysis + +```aidl +2: 1x1 +3: 1x2 +4: 2x2 +5: 2x3 +6: 2x2x2 +7: 2x2x3 +``` +根据上面来看,拆分成2和3肯定是乘积最大. +可以网上查一下数学推导,也可以用动态规划 + +### Coding +```java +class Solution { + public int maxProductAfterCutting(int length) + { + + if(length <= 3){ + return 1*(length-1); + } + int result = 1; + if(length % 3 == 1){ + result = 4; + length -= 4; + }else if(length % 3 == 2){ + result = 2; + length -= 2; + } + + while(length != 0){ + result *= 3; + length -= 3; + } + + return result; + } +} +``` + +动态规划: +``` +public int maxProductAfterCutting(int target) { + int[] dp = new int[target + 1]; + dp[1] = 1; + for (int i = 2; i <= target; i++) { + for (int j = 1; j < i; j++) { + dp[i] = Math.max(dp[i], (Math.max(j, dp[j])) * (Math.max(i - j, dp[i - j]))); + } + } + return dp[target]; + } +``` \ No newline at end of file diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/33. \346\211\223\345\215\260\344\273\2161\345\210\260\346\234\200\345\244\247\347\232\204n\344\275\215\346\225\260.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/33. \346\211\223\345\215\260\344\273\2161\345\210\260\346\234\200\345\244\247\347\232\204n\344\275\215\346\225\260.md" new file mode 100644 index 0000000..c5baf4f --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/33. \346\211\223\345\215\260\344\273\2161\345\210\260\346\234\200\345\244\247\347\232\204n\344\275\215\346\225\260.md" @@ -0,0 +1,34 @@ +### Title +输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。 +1. 用返回一个整数列表来代替打印 +2. n 为正整数,0 < n <= 5 +### Demo +```aidl +输入:1 +返回值:[1,2,3,4,5,6,7,8,9] +``` + + + +### Analysis + + + +### Coding +```java +class Solution { + public int[] printNumbers (int n) { + // write code here + int high = 1; + while(n>0){ + high*=10; + n--; + } + int res[] = new int[high-1]; + for(int i=1;i 1 -> 9 +``` + + + +### Analysis + + + +### Coding +```java +class Solution { + public int[] printNumbers (int n) { + // write code here + int high = 1; + while(n>0){ + high*=10; + n--; + } + int res[] = new int[high-1]; + for(int i=1;ij){ + //这区间整体向后移动一位 + array[k] = array[k-1]; + k--; + } + //移位之后将对应的值赋值 + array[k] = temp; + j++; + } + } + //返回结果数数组 + return array; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/36. \351\223\276\350\241\250\344\270\255\347\216\257\347\232\204\345\205\245\345\217\243\347\273\223\347\202\271.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/36. \351\223\276\350\241\250\344\270\255\347\216\257\347\232\204\345\205\245\345\217\243\347\273\223\347\202\271.md" new file mode 100644 index 0000000..b1f679e --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/36. \351\223\276\350\241\250\344\270\255\347\216\257\347\232\204\345\205\245\345\217\243\347\273\223\347\202\271.md" @@ -0,0 +1,64 @@ +### Title +给定一个链表,若其中包含环,则输出环的入口节点。 + +若其中不包含环,则输出null。 + +数据范围 +节点 val 值取值范围 [1,1000]。 +链表长度 [0,500]。 + + +### Demo +``` +[1, 2, 3, 4, 5, 6] +2 +注意,这里的2表示编号是2的节点,节点编号从0开始。所以编号是2的节点就是val等于3的节点。 + +则输出环的入口节点3. +``` + +### Analysis +假设快指针在环中走了nnn圈,慢指针在环中走了mmm圈,它们才相遇,而进入环之前的距离为xxx,环入口到相遇点的距离为yyy,相遇点到环入口的距离为zzz。快指针一共走了x+n(y+z)+yx+n(y+z)+yx+n(y+z)+y步,慢指针一共走了x+m(y+z)+yx+m(y+z)+yx+m(y+z)+y,这个时候快指针走的倍数是慢指针的两倍,则x+n(y+z)+y=2(x+m(y+z)+y)x+n(y+z)+y=2(x+m(y+z)+y)x+n(y+z)+y=2(x+m(y+z)+y),这时候x+y=(n−2m)(y+z)x+y=(n-2m)(y+z)x+y=(n−2m)(y+z),因为环的大小是y+zy+zy+z,说明从链表头经过环入口到达相遇地方经过的距离等于整数倍环的大小:那我们从头开始遍历到相遇位置,和从相遇位置开始在环中遍历,会使用相同的步数,而双方最后都会经过入口到相遇位置这yyy个节点,那说明这yyy个节点它们就是重叠遍历的,那它们从入口位置就相遇了 + +### Code + +```java +class Solution { + //判断有没有环,返回相遇的地方 + public ListNode hasCycle(ListNode head) { + //先判断链表为空的情况 + if(head == null) + return null; + //快慢双指针 + ListNode fast = head; + ListNode slow = head; + //如果没环快指针会先到链表尾 + while(fast != null && fast.next != null){ + //快指针移动两步 + fast = fast.next.next; + //慢指针移动一步 + slow = slow.next; + //相遇则有环,返回相遇的位置 + if(fast == slow) + return slow; + } + //到末尾说明没有环,返回null + return null; + } + + public ListNode EntryNodeOfLoop(ListNode pHead) { + ListNode slow = hasCycle(pHead); + //没有环 + if(slow == null) + return null; + //快指针回到表头 + ListNode fast = pHead; + //再次相遇即是环入口 + while(fast != slow){ + fast = fast.next; + slow = slow.next; + } + return slow; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/36\350\277\233\345\210\266\346\225\260\347\232\204\345\212\240\346\263\225\350\277\220\347\256\227.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/36\350\277\233\345\210\266\346\225\260\347\232\204\345\212\240\346\263\225\350\277\220\347\256\227.md" new file mode 100644 index 0000000..124b291 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/36\350\277\233\345\210\266\346\225\260\347\232\204\345\212\240\346\263\225\350\277\220\347\256\227.md" @@ -0,0 +1,97 @@ +### Title +36进制由`0-9`,`a-z`,共36个字符表示,最小为`'0'` + +`0 9`对应十进制的`09`,`'a''z'`对应十进制的`10 35` + +要求按照加法规则计算出任意两个36进制正整数的和 + +**要求**:不允许把36进制数字整体转为10进制数字,计算出10进制数字的相加结果再转回为36进制 +### Demo +`'1b' + '2x' = '48'` + +### Analysis +这个题的主要思路是巧妙的用一个字符串来实现36进制与10进制的映射关系。 + +`String symbol = "0123456789abcdefghijklmnopqrstuvwxyz";` + +比如z, z所在位置是`symbol.charAt(35);`也就是10进制的35。 + +相反, 已知36进制后, 在这个字符串中找到相应的index位置, 这个index就是10进制。 + +然后从低位数开始将10进制数进行运算并处理好36进位1. + +### Coding +```java +class Solution { + public String addFunWithStr(String param1, String param2){ + StringBuffer stringBuffer = new StringBuffer(); + String symbol = "0123456789abcdefghijklmnopqrstuvwxyz"; + int param1Len = param1.length(); + int param2Len = param2.length(); + + int i = param1Len - 1; + int j = param2Len - 1; + + if (i < 0 || j < 0) { + return null; + } + + int temp = 0; + while (i >= 0 && j >= 0) { + // 获取低位数 + char ch_1 = param1.charAt(i); + char ch_2 = param2.charAt(j); + // 根据0-z字符串获取index + int v1 = getIntFromChar(ch_1); + int v2 = getIntFromChar(ch_2); + + int ret = v1 + v2; + // 如果结果>36 则需要处理进位 + if (ret >= 36) { + int index = ret - 36 + temp; + char sv = symbol.charAt(index); + stringBuffer.append(sv); + temp = 1; //进位 + } else { + int index = ret + temp; + char sv = symbol.charAt(index); + stringBuffer.append(sv); + temp = 0; + } + + i--; + j--; + + } + + // 与归并排序类似, 需要将剩余的位数加上 + while (i >= 0) { + char ch_1 = param1.charAt(i); + stringBuffer.append(ch_1); + i--; + } + + while (j >= 0) { + char ch_2 = param2.charAt(i); + stringBuffer.append(ch_2); + j--; + } + + // 因为一直是append操作,低位在前面,所以需要逆序 + StringBuffer result = stringBuffer.reverse(); + return result.toString(); + } + public static int getIntFromChar(char ch) { + + int ret = -1; + // 如果字母是 0-9, 则直接-0 + if (ch >='0' && ch <= '9') { + ret = ch - '0'; + // 如果是大于9,则算一下是第几个index,等价于-'a'+10(数字0-9占用10个) + } else if (ch >= 'a' && ch <= 'z') { + ret = (ch - 'a') + 10; + } + return ret; + } +} +``` \ No newline at end of file diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\344\270\255\345\272\217\351\201\215\345\216\206\351\235\236\351\200\222\345\275\222\350\247\243\346\263\225.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\344\270\255\345\272\217\351\201\215\345\216\206\351\235\236\351\200\222\345\275\222\350\247\243\346\263\225.md" new file mode 100644 index 0000000..0ed7bd9 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\344\270\255\345\272\217\351\201\215\345\216\206\351\235\236\351\200\222\345\275\222\350\247\243\346\263\225.md" @@ -0,0 +1,24 @@ + +``` + public static List midOrderTraverse(TreeNode root) { + LinkedList res = new LinkedList(); + + if (root == null) + return res; + + Stack aux = new Stack(); + TreeNode node = root;//node指向待处理节点 + + while (node != null || !aux.isEmpty()) { + while (node != null) { + //当前节点不为null,将当前节点入栈等到该节点的左子树全部处理完后在处理当前节点 + aux.add(node); + node = node.left;//先处理左孩子节点 + } + TreeNode temp = aux.pop(); + res.add(temp.val);//node没有左孩子,则输出当前node节点 + node = temp.right;//处理node的右子树 + } + return res; + } +``` \ No newline at end of file diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\347\232\204\345\217\263\350\247\206\345\233\276.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\347\232\204\345\217\263\350\247\206\345\233\276.md" new file mode 100644 index 0000000..517e2ec --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\347\232\204\345\217\263\350\247\206\345\233\276.md" @@ -0,0 +1,50 @@ +### Title +二叉树的右视图 + + +### 分析 + +思路可以参考按行打印二叉树。 + +按层放到queue中, 然后取queue每层最右边的节点, 也就是每行的最后一个节点。 + +### Coding +```java +class Main{ + public int[] getRightNode(TreeNode node){ + // check null + if (node == null){ + return new int[0]; + } + // 借助queue + Queue queue = new LinkedList<>(); + List result = new ArrayList<>(); + List path = new ArrayList<>(); + // 定义每行的节点个数 + int curCount = 0; + int nextCount = 1; + queue.offer(node); + + while(!queue.isEmpty()){ + curCount = nextCount; + nextCount = 0; + for (int i = 0; i< curCount; i++){ + TreeNode temp = queue.poll(); + path.add(temp.val); + if(temp.left != null){ + queue.offer(temp.left); + nextCount++; + } + if(temp.right != null){ + queue.offer(temp.right); + nextCount++; + } + } + // 记录path的最后一个节点并重置 + result.add(path.get(path.size()-1)); + path = new ArrayList<>(); + } + return result; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\345\233\276\347\232\204\351\201\215\345\216\206.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\345\233\276\347\232\204\351\201\215\345\216\206.md" new file mode 100644 index 0000000..58f8e84 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\345\233\276\347\232\204\351\201\215\345\216\206.md" @@ -0,0 +1,86 @@ +### Title + 1 + / \ + 2 3 + / \ / \ + 4 5 6 7 + \ | / \ / + 8 9 + +遍历该图的所有节点。 + +### Analysis +深度遍历需要借助Stack、Set来完成回归和记录。当然也可以用标记法, 那样的时间复杂度高, 是递归的做法。 + +广度遍历需要借助Queue、Set来完成, 比深度遍历简单。思想一样。 +### Coding + +#### DFS +```java +public class Main { + + public static void dfs(Node node) { + if (node == null) { + return; + } + Stack stack = new Stack<>(); + Set set = new HashSet<>(); + // 首先先放入头节点, 并打印 + stack.push(node); + set.add(node); + System.out.println(node); + + while(!stack.isEmpty()){ + // 进入到这里,我们需要弹出一个节点 + Node cur = stack.pop(); + // 遍历当前节点的下一个节点 + while(cur.next != null){ + // 如果set中没有这个节点, 证明没走过 + // 如果走过的话, 跳过遍历另一个next节点 + if (!set.contains(cur)){ + stack.push(cur); + stack.push(cur.next); + set.add(cur); + System.out.println(cur.next.val); + // 为什么要break, 因为是深度遍历, 先往深处走 + break; + } + } + } + } +} +``` + +#### BFS + +```java +public class Main { + + public static void bfs(Node node) { + if (node == null) { + return; + } + Queue queue = new LinkedList<>(); + Set set = new HashSet<>(); + // 首先先放入头节点, 并打印 + queue.offer(node); + set.add(node); + System.out.println(node); + + while(!queue.isEmpty()){ + // 进入到这里,我们需要弹出一个节点 + Node cur = queue.poll(); + System.out.println(cur); + // 遍历当前节点的下一个节点 + while(cur.next != null){ + // 如果set中没有这个节点, 证明没走过 + // 如果走过的话, 跳过遍历另一个next节点 + if (!set.contains(cur)){ + queue.push(cur.next); + set.add(cur); + } + } + } + } +} +``` \ No newline at end of file diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\345\257\271\345\215\225\351\223\276\350\241\250\347\232\204\346\216\222\345\272\217.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\345\257\271\345\215\225\351\223\276\350\241\250\347\232\204\346\216\222\345\272\217.md" new file mode 100644 index 0000000..9db5244 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\345\257\271\345\215\225\351\223\276\350\241\250\347\232\204\346\216\222\345\272\217.md" @@ -0,0 +1,53 @@ +### Title +给定一个单链表,对这个单链表进行排序,要求时间复杂度O(nlogn),空间复杂度O(1)。 + +### Demo + +`2->1->3->5->4` 经过排序后链表结构为`1->2->3->4->5` + +### Analysis +这个题的主要思路是如果发现cur>pre节点, 那么如何将cur放到正确的位置。 +1. 如何去找正确的位置, 从头遍历判断与cur的大小关系 +2. 如何去存放的正确位置, 需要在遍历第一点的时候将前后节点临时缓存。 + +### Coding +```java +class Solution { + public ListNode sortList(ListNode node){ + // check null + if(node == null || node.next == null){ + return node; + } + // 记录链表头位置 + ListNode result = new ListNode(-1); + result.next = node; + + // 记录pre 和 cur节点 + ListNode pre = node; + ListNode cur = node.next; + while(cur != null){ + // 如果pre大于cur, 则需要将cur放到正确的位置上去 + if (pre.val > cur.val){ + pre.next = cur.next; + // 拿到头节点 + ListNode temp1 = result; + ListNode temp2 = result.next; + // 找到cur应该放的位置 + while(cur.val > temp2.val){ + // temp1用来记录cur应该放的位置的前一个位置 + temp1 = temp2; + temp2 = temp2.next; + } + // 将cur放到temp1与temp2之间 + temp1.next = cur; + cur.next = temp2; + cur = cur.next; + }else{ + pre = cur; + cur = cur.next; + } + } + return result.next; + } +} +``` \ No newline at end of file diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/1. JDK\343\200\201JRE\343\200\201JVM\347\232\204\345\214\272\345\210\253\344\270\216\350\201\224\347\263\273.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/1. JDK\343\200\201JRE\343\200\201JVM\347\232\204\345\214\272\345\210\253\344\270\216\350\201\224\347\263\273.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/1. JDK\343\200\201JRE\343\200\201JVM\347\232\204\345\214\272\345\210\253\344\270\216\350\201\224\347\263\273.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/1. JDK\343\200\201JRE\343\200\201JVM\347\232\204\345\214\272\345\210\253\344\270\216\350\201\224\347\263\273.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/10. JVM-\345\210\206\346\236\220\345\267\245\345\205\267\346\246\202\350\277\260.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/10. JVM-\345\210\206\346\236\220\345\267\245\345\205\267\346\246\202\350\277\260.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/10. JVM-\345\210\206\346\236\220\345\267\245\345\205\267\346\246\202\350\277\260.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/10. JVM-\345\210\206\346\236\220\345\267\245\345\205\267\346\246\202\350\277\260.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/11. JVM-\345\210\235\344\275\223\351\252\214\357\274\232\345\240\206\346\272\242\345\207\272\345\244\204\347\220\206.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/11. JVM-\345\210\235\344\275\223\351\252\214\357\274\232\345\240\206\346\272\242\345\207\272\345\244\204\347\220\206.md" similarity index 92% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/11. JVM-\345\210\235\344\275\223\351\252\214\357\274\232\345\240\206\346\272\242\345\207\272\345\244\204\347\220\206.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/11. JVM-\345\210\235\344\275\223\351\252\214\357\274\232\345\240\206\346\272\242\345\207\272\345\244\204\347\220\206.md" index 9892dbb..ffe3195 100644 --- "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/11. JVM-\345\210\235\344\275\223\351\252\214\357\274\232\345\240\206\346\272\242\345\207\272\345\244\204\347\220\206.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/11. JVM-\345\210\235\344\275\223\351\252\214\357\274\232\345\240\206\346\272\242\345\207\272\345\244\204\347\220\206.md" @@ -25,4 +25,3 @@ public class Test { ``` ![控制台报错](https://upload-images.jianshu.io/upload_images/5786888-999e6eb041639420.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -12G的ROM让我等了好久才报错! diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/2. JVM-\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/2. JVM-\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/2. JVM-\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/2. JVM-\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/3. JVM-\345\206\205\345\255\230\347\256\241\347\220\206.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/3. JVM-\345\206\205\345\255\230\347\256\241\347\220\206.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/3. JVM-\345\206\205\345\255\230\347\256\241\347\220\206.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/3. JVM-\345\206\205\345\255\230\347\256\241\347\220\206.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/4. JVM-\347\250\213\345\272\217\350\256\241\346\225\260\345\231\250.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/4. JVM-\347\250\213\345\272\217\350\256\241\346\225\260\345\231\250.md" new file mode 100644 index 0000000..47dc52e --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/4. JVM-\347\250\213\345\272\217\350\256\241\346\225\260\345\231\250.md" @@ -0,0 +1,19 @@ +### 什么是程序计数器 +>程序计数器(program counter register)只占用了一块比较小的内存空间,至于小到什么程度呢,这样说吧,有时可以忽略不计的。 + + +程序计数器可以看作是当前线程所执行的字节码文件(class)的行号指示器。也就是我们javap -c xxx.class 反编译生成的指令行号。 + +在虚拟机的世界中,字节码解释器就是通过改变计数器的值来选取下一条执行的字节码指令,分支、循环、跳转、异常处理、线程恢复都需要借助它来实现的。 + +java虚拟机多线程是通过线程间轮流切换来分配给处理器执行时间;在确定时间节点,一个处理器(一核)只会执行一个线程的指令;为保证线程切换回来后能恢复到原执行位置,各个线程间计数器互相不影响,独立存储(称之为 线程私有 的内存); + +### 谁在操作程序计数器 +jvm中有字节码执行引擎,它会在执行代码的时候对程序计数器进行记录。 + + +### 程序计数器特点 +1. 线程隔离。每个线程都有属于自己的程序计数器。 +2. 执行native方法的时候,程序计数器值为空。也很好理解,因为它直接通过JNI调用本地C/C++库,没有经过字节码引擎处理。 +3. 占用内存很小,可以忽略不计。 +4. 永远不会发生OOM(因为设计就没有规定) \ No newline at end of file diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/5. JVM-\350\277\220\350\241\214\346\227\266\346\240\210\345\270\247\347\273\223\346\236\204.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/5. JVM-\350\277\220\350\241\214\346\227\266\346\240\210\345\270\247\347\273\223\346\236\204.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/5. JVM-\350\277\220\350\241\214\346\227\266\346\240\210\345\270\247\347\273\223\346\236\204.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/5. JVM-\350\277\220\350\241\214\346\227\266\346\240\210\345\270\247\347\273\223\346\236\204.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/6. JVM-\346\226\271\346\263\225\350\260\203\347\224\250.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/6. JVM-\346\226\271\346\263\225\350\260\203\347\224\250.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/6. JVM-\346\226\271\346\263\225\350\260\203\347\224\250.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/6. JVM-\346\226\271\346\263\225\350\260\203\347\224\250.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/7. JVM-\350\247\206\350\247\222\347\234\213\345\257\271\350\261\241\345\210\233\345\273\272.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/7. JVM-\350\247\206\350\247\222\347\234\213\345\257\271\350\261\241\345\210\233\345\273\272.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/7. JVM-\350\247\206\350\247\222\347\234\213\345\257\271\350\261\241\345\210\233\345\273\272.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/7. JVM-\350\247\206\350\247\222\347\234\213\345\257\271\350\261\241\345\210\233\345\273\272.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/8. JVM-\345\236\203\345\234\276\345\233\236\346\224\266\346\234\272\345\210\266.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/8. JVM-\345\236\203\345\234\276\345\233\236\346\224\266\346\234\272\345\210\266.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/8. JVM-\345\236\203\345\234\276\345\233\236\346\224\266\346\234\272\345\210\266.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/8. JVM-\345\236\203\345\234\276\345\233\236\346\224\266\346\234\272\345\210\266.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/9. JVM-Class\346\226\207\344\273\266\347\273\223\346\236\204&\345\255\227\350\212\202\347\240\201\346\214\207\344\273\244.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/9. JVM-Class\346\226\207\344\273\266\347\273\223\346\236\204&\345\255\227\350\212\202\347\240\201\346\214\207\344\273\244.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/9. JVM-Class\346\226\207\344\273\266\347\273\223\346\236\204&\345\255\227\350\212\202\347\240\201\346\214\207\344\273\244.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/9. JVM-Class\346\226\207\344\273\266\347\273\223\346\236\204&\345\255\227\350\212\202\347\240\201\346\214\207\344\273\244.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/JVM-\346\226\260\344\270\200\344\273\243GC\344\271\213\344\275\216\345\273\266\350\277\237\345\236\203\345\234\276\346\224\266\351\233\206\345\231\250.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-ZGC.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/JVM-\346\226\260\344\270\200\344\273\243GC\344\271\213\344\275\216\345\273\266\350\277\237\345\236\203\345\234\276\346\224\266\351\233\206\345\231\250.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-ZGC.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-\344\270\211\347\247\215\345\270\270\351\207\217\346\261\240.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-\344\270\211\347\247\215\345\270\270\351\207\217\346\261\240.md" new file mode 100644 index 0000000..b2d88ee --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-\344\270\211\347\247\215\345\270\270\351\207\217\346\261\240.md" @@ -0,0 +1,35 @@ +### 常量池 + +> 我的理解是Class文件常量池先被加载到堆里,然后解析完后放到运行时常量池中,然后将运行时常量池存放到元空间。 + +Java中的常量池分为三种类型: + +- 静态常量池(也称class文件常量池)(The Constant Pool) +- 运行时常量池(The Run-Time Constant Pool) +- String常量池 + +##### 静态常量池 + +> 存在于class文件中 + +所处区域:堆 + +诞生时间:编译时 + +内容概要:符号引用和字面量 + +class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放**编译期**生成的各种字面量和符号引用 + +#### 运行时常量池 + +> 存在于**元空间**中 + +诞生时间:JVM运行时 + +内容概要:class文件元信息描述,**编译后的代码数据**,引用类型数据,类文件常量池。 + +#### String常量池 + +> 存在于堆中 + +在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个哈希表,里面存的是驻留字符串(也就是我们常说的用双引号括起来的)的引用(而不是驻留字符串实例本身),也就是说在堆中的某些字符串实例被这个StringTable引用之后就等同被赋予了”驻留字符串”的身份。这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。 \ No newline at end of file diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-\345\206\205\345\255\230\346\263\204\346\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-\345\206\205\345\255\230\346\263\204\346\274\217.md" new file mode 100644 index 0000000..fd9147d --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-\345\206\205\345\255\230\346\263\204\346\274\217.md" @@ -0,0 +1,13 @@ +首先OOM原因有很多: + +- java.lang.OutOfMemoryError: Java heap space 。如果没有代码无限制new对象,一般可通过JVM调参解决。 +- java.lang.OutOfMemoryError: PermGen space 。 可采用-XX:MaxPermSize调节大小 +- java.lang.OutOfMemoryError: Requested array size exceeds VM limit 尝试分配比堆大的数组 +- java.lang.OutOfMemoryError: request bytes for . Out of swap space? 本机swap空间不足 +- java.lang.OutOfMemoryError: (Native method) + +1. 先根据报错确定原因。初步定位是不是调参可以解决的。 +2. 调参解决不了, 分析thread dump、heap dump。 `jmap -dump:live打印堆日志` `jstack -l 打印线程日志` 。`jstat -gc `查看gc信息 +3. 使用MAT、jhat、等分析堆日志。 也可以借助jconsole 分析线程死锁、内存使用等。 + +元空间:1.8后取消了永久代(方法区),改为元空间,类的信息存放在元空间,元空间不使用堆内存,使用的是本地内存,理论上讲本地内存有多大,元空间就有多大。 \ No newline at end of file diff --git "a/2. Java\345\237\272\347\241\200/2.3 \351\235\242\350\257\225\347\257\207/JVM\351\235\242\350\257\225.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.3 \351\235\242\350\257\225\347\257\207/JVM\351\235\242\350\257\225.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/arthas\345\221\275\344\273\244\345\210\227\350\241\250.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/arthas\345\221\275\344\273\244\345\210\227\350\241\250.md" new file mode 100644 index 0000000..bfcb6ce --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/arthas\345\221\275\344\273\244\345\210\227\350\241\250.md" @@ -0,0 +1,29 @@ +### 命令列表 +https://arthas.aliyun.com/doc/commands.html + +### 个人总结 +#### 监控类 +* `monitor`:监控方法的执行情况 +* `watch`:检测函数入参、返回值 +* `trace`:根据路径追踪,并记录消耗时间 +* `stack`:输出当前方法被调用的调用路径 +* `tt`:时间隧道,记录多个请求 +* `jad`:反编译耗时代码 + +#### JVM类 +* `dashboard`:实时数据面板 +* `Thread`:线程相关堆栈信息 +* `sysprop`:查看/修改属性 +* `sysenv`:查看JVM环境属性 +* `vmpotion`:查看JVM中选项 +* `getstatic`:获取静态成员变量 +* `ognl`:执行ognl表达式 +* `dump`:保存已加载字节码文件到本地 + +#### 类加载器 +* `sc`:查看类信息 search class +* `sm`:查看已加载方法信息 search method +* `classloader`:获取类加载器的信息 + + + diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/\344\273\216JVM\350\247\206\350\247\222\345\210\206\346\236\220try-catch\346\200\247\350\203\275.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\344\273\216JVM\350\247\206\350\247\222\345\210\206\346\236\220try-catch\346\200\247\350\203\275.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/\344\273\216JVM\350\247\206\350\247\222\345\210\206\346\236\220try-catch\346\200\247\350\203\275.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\344\273\216JVM\350\247\206\350\247\222\345\210\206\346\236\220try-catch\346\200\247\350\203\275.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\345\255\227\350\212\202\347\240\201\346\214\207\344\273\244.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\345\255\227\350\212\202\347\240\201\346\214\207\344\273\244.md" new file mode 100644 index 0000000..4052fbb --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\345\255\227\350\212\202\347\240\201\346\214\207\344\273\244.md" @@ -0,0 +1,58 @@ +### 1.将局部变量表中的变量压入操作数栈中 + +xload_n or xload n 将数据压入栈 +iload_1:将局部变量表中下标为 1 的 int 变量压入操作数栈中。 +aload_2:将局部变量表中下标为 2 的引用数据类型变量(此时为 String)压入操作数栈中。 +lload_3:将局部变量表中下标为 3 的 long 型变量压入操作数栈中。 +iload 5:将局部变量表中下标为 5 的 int 变量(实际为 boolean)压入操作数栈中。 +### 2.将常量池中的常量压入操作数栈中 + +根据数据类型和入栈内容的不同,此类又可以细分为 const 系列、push 系列和 Idc 指令。 + +### 3.将栈顶的数据出栈并装入局部变量表中 + +xstore_(x 为 i、l、f、d、a,n 默认为 0 到 3) +xstore(x 为 i、l、f、d、a) + +### 4.算术指令 + +加法指令:iadd、ladd、fadd、dadd +减法指令:isub、lsub、fsub、dsub +乘法指令:imul、lmul、fmul、dmul +除法指令:idiv、ldiv、fdiv、ddiv +求余指令:irem、lrem、frem、drem +自增指令:iinc +### 5.对象的创建和访问指令 + +#### 5.1创建指令 + +数组也是一种对象,但它创建的字节码指令和普通的对象不同。创建数组的指令有三种: + +newarray:创建基本数据类型的数组 +anewarray:创建引用类型的数组 +multianewarray:创建多维数组 +普通对象的创建指令只有一个,就是 new,它会接收一个操作数,指向常量池中的一个索引,表示要创建的类型。 + +#### 5.2字段访问指令 + +字段可以分为两类,一类是成员变量,一类是静态变量(static 关键字修饰的),所以字段访问指令可以分为两类: + +访问静态变量:getstatic、putstatic。 +访问成员变量:getfield、putfield,需要创建对象后才能访问。 +### 6.方法调用和返回指令 + +方法调用指令有 5 个,分别用于不同的场景: + +invokevirtual:用于调用对象的成员方法,根据对象的实际类型进行分派,支持多态。 +invokeinterface:用于调用接口方法,会在运行时搜索由特定对象实现的接口方法进行调用。 +invokespecial:用于调用一些需要特殊处理的方法,包括构造方法、私有方法和父类方法。 +invokestatic:用于调用静态方法。 +invokedynamic:用于在运行时动态解析出调用点限定符所引用的方法,并执行。 +### 7.操作数栈管理指令 + +常见的操作数栈管理指令有 pop、dup 和 swap。 + +将一个或两个元素从栈顶弹出,并且直接废弃,比如 pop,pop2; +复制栈顶的一个或两个数值并将其重新压入栈顶,比如 dup,dup2,dup_×1,dup2_×1,dup_×2,dup2_×2; +将栈最顶端的两个槽中的数值交换位置,比如 swap。 +这些指令不需要指明数据类型,因为是按照位置压入和弹出的。 \ No newline at end of file diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\345\257\271\350\261\241\347\232\204\345\210\233\345\273\272\350\277\207\347\250\213.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\345\257\271\350\261\241\347\232\204\345\210\233\345\273\272\350\277\207\347\250\213.md" new file mode 100644 index 0000000..e3824ed --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\345\257\271\350\261\241\347\232\204\345\210\233\345\273\272\350\277\207\347\250\213.md" @@ -0,0 +1,55 @@ +### 对象的创建过程 + +首先,写一段代码编译一下字节码,参考之前的指令知识来看一下对象创建的过程。 + +```java +public class Main { + public static void main(String[] args) { + Entry entry = new Entry(); + } +} + +public class Entry { + private int total = 10; + + public int getTotal() { + return total; + } + + public Entry setTotal(int total) { + this.total = total; + return this; + } +} + +``` +把上面的代码进行编译、解析 + +``` +fantj@FantJdeMacBook-Pro src % javap -c Main.class +Compiled from "Main.java" +public class Main { + public Main(); + Code: + 0: aload_0 + 1: invokespecial #1 // Method java/lang/Object."":()V + 4: return + + public static void main(java.lang.String[]); + Code: + 0: new #2 // class Entry + 3: dup + 4: invokespecial #3 // Method Entry."":()V + 7: astore_1 + 8: return +} + +``` + +#### 字节码解读: +* aload_0: 读取方法的第一个引用参数(或更一般地说,第一个本地引用变量)并将其推送到堆栈. +* invokespecial #1 : 调用Main的super方法,也就是Object的构造方法 +* new: 它会接收一个操作数,指向常量池中的一个索引,表示要创建的类型 +* dup: 压入栈顶 +* invokespecial #3 :调用Entry的构造方法 (半初始化状态,因为此时的变量还是默认值) +* astore_1:将栈顶值保存到局部变量 (真正初始化完成) \ No newline at end of file diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/1. Java\345\271\266\345\217\221\347\274\226\347\250\213-\347\272\277\347\250\213\347\232\204\345\220\204\347\247\215\345\210\233\345\273\272\346\226\271\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/1. \347\272\277\347\250\213\347\232\204\345\220\204\347\247\215\345\210\233\345\273\272\346\226\271\345\274\217.md" similarity index 93% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/1. Java\345\271\266\345\217\221\347\274\226\347\250\213-\347\272\277\347\250\213\347\232\204\345\220\204\347\247\215\345\210\233\345\273\272\346\226\271\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/1. \347\272\277\347\250\213\347\232\204\345\220\204\347\247\215\345\210\233\345\273\272\346\226\271\345\274\217.md" index 6b09c62..4a73f3a 100644 --- "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/1. Java\345\271\266\345\217\221\347\274\226\347\250\213-\347\272\277\347\250\213\347\232\204\345\220\204\347\247\215\345\210\233\345\273\272\346\226\271\345\274\217.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/1. \347\272\277\347\250\213\347\232\204\345\220\204\347\247\215\345\210\233\345\273\272\346\226\271\345\274\217.md" @@ -33,11 +33,14 @@ public class CreatThreadDemo1 extends Thread{ } ``` 常规方法,不多做介绍了,interrupted方法,是来判断该线程是否被中断。(终止线程不允许用stop方法,该方法不会施放占用的资源。所以我们在设计程序的时候,要按照中断线程的思维去设计,就像上面的代码一样)。 + ###### 让线程等待的方法 +>sleep不会释放锁、wait会释放锁然后进入等待队列。 * Thread.sleep(200); //线程休息2ms * Object.wait(); //让线程进入等待,直到调用Object的notify或者notifyAll时,线程停止休眠 #### 方法二:实现runnable接口,作为线程任务存在 +>Thread类也是Runnable的实现类。start方法最终会调用Runnable的run方法。 ``` public class CreatThreadDemo2 implements Runnable { @Override @@ -58,6 +61,7 @@ public class CreatThreadDemo2 implements Runnable { Runnable 只是来修饰线程所执行的任务,它不是一个线程对象。想要启动Runnable对象,必须将它放到一个线程对象里。 #### 方法三:匿名内部类创建线程对象 +>用lambda写更好。 ``` public class CreatThreadDemo3 extends Thread{ public static void main(String[] args) { @@ -192,20 +196,20 @@ public class CreatThreadDemo7 { //parallel 平行的,并行的 int result = values.parallelStream().mapToInt(p -> p*2).sum(); System.out.println(result); - //怎么证明它是并发处理呢 - values.parallelStream().forEach(p-> System.out.println(p)); + //证明它是并发处理 + values.parallelStream().forEach(p-> System.out.println( Thread.currentThread().getName() +" "+p)); } } ``` ``` 200 -40 -10 -20 -30 +ForkJoinPool.commonPool-worker-3 20 +ForkJoinPool.commonPool-worker-1 40 +ForkJoinPool.commonPool-worker-2 10 +main 30 ``` -怎么证明它是并发处理呢,他们并不是按照顺序输出的 。 + diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/10. Java\345\271\266\345\217\221\347\274\226\347\250\213-AQS.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/10. AQS.md" similarity index 88% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/10. Java\345\271\266\345\217\221\347\274\226\347\250\213-AQS.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/10. AQS.md" index 3658430..0a0b8aa 100644 --- "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/10. Java\345\271\266\345\217\221\347\274\226\347\250\213-AQS.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/10. AQS.md" @@ -21,13 +21,12 @@ ##### getState ``` - protected final int getState() { - return state; - } +protected final int getState() { + return state; +} ``` 返回同步状态的当前值。此操作具有 volatile 读的内存语义。 -返回: -当前状态值 + ##### setState ``` @@ -42,80 +41,57 @@ newState - 新的状态值 ##### compareAndSetState ``` - protected final boolean compareAndSetState(int expect, int update) { - // See below for intrinsics setup to support this - return unsafe.compareAndSwapInt(this, stateOffset, expect, update); - } +protected final boolean compareAndSetState(int expect, int update) { + // See below for intrinsics setup to support this + return unsafe.compareAndSwapInt(this, stateOffset, expect, update); +} ``` compareAndSwap即CAS,详细可查找[Java并发编程 -- Atomic包](https://www.jianshu.com/p/288bdd29ec06)文章。 如果当前状态值等于预期值,则以原子方式将同步状态设置为给定的更新值。此操作具有 volatile 读和写的内存语义。 -###### 参数: -expect - 预期值 -update - 新值 -###### 返回: + 如果成功,则返回 true。返回 false 指示实际值与预期值不相等。 + ##### tryAcquire ``` - protected boolean tryAcquire(int arg) { - throw new UnsupportedOperationException(); - } +protected boolean tryAcquire(int arg) { + throw new UnsupportedOperationException(); +} ``` 试图在独占模式下获取对象状态。此方法应该查询是否允许它在独占模式下获取对象状态,如果允许,则获取它。 此方法总是由执行 acquire 的线程来调用。如果此方法报告失败,则 acquire 方法可以将线程加入队列(如果还没有将它加入队列),直到获得其他某个线程释放了该线程的信号。可以用此方法来实现 lock.tryLock()方法。默认实现将抛出[UnsupportedOperationException](http://tool.oschina.net/uploads/apidocs/jdk-zh/java/lang/UnsupportedOperationException.html "java.lang 中的类")。 -###### 参数: -arg - acquire 参数。该值总是传递给 acquire 方法的那个值,或者是因某个条件等待而保存在条目上的值。该值是不间断的,并且可以表示任何内容。 -###### 返回: + 如果成功,则返回 true。在成功的时候,此对象已经被获取。 -###### 抛出: -[IllegalMonitorStateException](http://tool.oschina.net/uploads/apidocs/jdk-zh/java/lang/IllegalMonitorStateException.html "java.lang 中的类")- 如果正在进行的获取操作将在非法状态下放置此同步器。必须以一致的方式抛出此异常,以便同步正确运行。 -[UnsupportedOperationException](http://tool.oschina.net/uploads/apidocs/jdk-zh/java/lang/UnsupportedOperationException.html "java.lang 中的类")- 如果不支持独占模式 ##### tryRelease ``` - protected boolean tryRelease(int arg) { - throw new UnsupportedOperationException(); - } +protected boolean tryRelease(int arg) { + throw new UnsupportedOperationException(); +} ``` 试图设置状态来反映独占模式下的一个释放。 此方法总是由正在执行释放的线程调用。 默认实现将抛出 UnsupportedOperationException。 -###### 参数: -arg - release 参数。该值总是传递给 release 方法的那个值,或者是因某个条件等待而保存在条目上的当前状态值。该值是不间断的,并且可以表示任何内容。 -###### 返回: -如果此对象现在处于完全释放状态,从而使等待的线程都可以试图获得此对象,则返回 true;否则返回 false。 -###### 抛出: -IllegalMonitorStateException - 如果正在进行的释放操作将在非法状态下放置此同步器。必须以一致的方式抛出此异常,以便同步正确运行。 -UnsupportedOperationException - 如果不支持独占模式 - ##### tryAcquireShared ``` - protected int tryAcquireShared(int arg) { - throw new UnsupportedOperationException(); - } +protected int tryAcquireShared(int arg) { + throw new UnsupportedOperationException(); +} ``` 试图在共享模式下获取对象状态。此方法应该查询是否允许它在共享模式下获取对象状态,如果允许,则获取它。 此方法总是由执行 acquire 线程来调用。如果此方法报告失败,则 acquire 方法可以将线程加入队列(如果还没有将它加入队列),直到获得其他某个线程释放了该线程的信号。 默认实现将抛出 UnsupportedOperationException。 -参数: -arg - acquire 参数。该值总是传递给 acquire 方法的那个值,或者是因某个条件等待而保存在条目上的值。该值是不间断的,并且可以表示任何内容。 -返回: -在失败时返回负值;如果共享模式下的获取成功但其后续共享模式下的获取不能成功,则返回 0;如果共享模式下的获取成功并且其后续共享模式下的获取可能够成功,则返回正值,在这种情况下,后续等待线程必须检查可用性。(对三种返回值的支持使得此方法可以在只是有时候以独占方式获取对象的上下文中使用。)在成功的时候,此对象已被获取。 -抛出: -IllegalMonitorStateException - 如果正在进行的获取操作将在非法状态下放置此同步器。必须以一致的方式抛出此异常,以便同步正确运行。 -UnsupportedOperationException - 如果不支持共享模式 - ##### tryReleaseShared ``` - protected boolean tryReleaseShared(int arg) { - throw new UnsupportedOperationException(); - } +protected boolean tryReleaseShared(int arg) { + throw new UnsupportedOperationException(); +} ``` 试图设置状态来反映共享模式下的一个释放。 此方法总是由正在执行释放的线程调用。 diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/11. Java\345\271\266\345\217\221\347\274\226\347\250\213-AQS\345\205\245\351\227\250&\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245\351\224\201.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/11. AQS\345\205\245\351\227\250&\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245\351\224\201.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/11. Java\345\271\266\345\217\221\347\274\226\347\250\213-AQS\345\205\245\351\227\250&\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245\351\224\201.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/11. AQS\345\205\245\351\227\250&\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245\351\224\201.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/12. Java\345\271\266\345\217\221\347\274\226\347\250\213-Condition.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/12. Condition.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/12. Java\345\271\266\345\217\221\347\274\226\347\250\213-Condition.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/12. Condition.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/13. Java\345\244\232\347\272\277\347\250\213-\344\272\222\346\226\245\351\224\201-\345\205\261\344\272\253\351\224\201-\350\257\273\345\206\231\351\224\201-\345\277\253\351\200\237\345\205\245\351\227\250.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/13. \344\272\222\346\226\245\351\224\201-\345\205\261\344\272\253\351\224\201-\350\257\273\345\206\231\351\224\201.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/13. Java\345\244\232\347\272\277\347\250\213-\344\272\222\346\226\245\351\224\201-\345\205\261\344\272\253\351\224\201-\350\257\273\345\206\231\351\224\201-\345\277\253\351\200\237\345\205\245\351\227\250.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/13. \344\272\222\346\226\245\351\224\201-\345\205\261\344\272\253\351\224\201-\350\257\273\345\206\231\351\224\201.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/14. Java\345\244\232\347\272\277\347\250\213-\351\224\201\351\231\215\347\272\247.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/14. \351\224\201\351\231\215\347\272\247.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/14. Java\345\244\232\347\272\277\347\250\213-\351\224\201\351\231\215\347\272\247.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/14. \351\224\201\351\231\215\347\272\247.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/15. Java\345\271\266\345\217\221\347\274\226\347\250\213-\345\205\254\345\271\263\351\224\201\345\222\214\351\235\236\345\205\254\345\271\263\351\224\201\347\232\204\344\270\200\344\272\233\346\200\235\350\200\203.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/15. \345\205\254\345\271\263\351\224\201\345\222\214\351\235\236\345\205\254\345\271\263\351\224\201\347\232\204\344\270\200\344\272\233\346\200\235\350\200\203.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/15. Java\345\271\266\345\217\221\347\274\226\347\250\213-\345\205\254\345\271\263\351\224\201\345\222\214\351\235\236\345\205\254\345\271\263\351\224\201\347\232\204\344\270\200\344\272\233\346\200\235\350\200\203.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/15. \345\205\254\345\271\263\351\224\201\345\222\214\351\235\236\345\205\254\345\271\263\351\224\201\347\232\204\344\270\200\344\272\233\346\200\235\350\200\203.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/16. Java\345\271\266\345\217\221\347\274\226\347\250\213-\346\211\213\345\212\250\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245Lock.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/16. \346\211\213\345\212\250\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245Lock.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/16. Java\345\271\266\345\217\221\347\274\226\347\250\213-\346\211\213\345\212\250\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245Lock.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/16. \346\211\213\345\212\250\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245Lock.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/17. Java\345\271\266\345\217\221\347\274\226\347\250\213-\345\215\225\344\276\213\346\250\241\345\274\217\347\272\277\347\250\213\345\256\211\345\205\250\351\227\256\351\242\230.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/17. \345\215\225\344\276\213\346\250\241\345\274\217\347\272\277\347\250\213\345\256\211\345\205\250\351\227\256\351\242\230.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/17. Java\345\271\266\345\217\221\347\274\226\347\250\213-\345\215\225\344\276\213\346\250\241\345\274\217\347\272\277\347\250\213\345\256\211\345\205\250\351\227\256\351\242\230.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/17. \345\215\225\344\276\213\346\250\241\345\274\217\347\272\277\347\250\213\345\256\211\345\205\250\351\227\256\351\242\230.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/\345\271\266\345\217\221\345\267\245\345\205\267\347\261\273CountDownLatch\343\200\201CyclicBarrier\343\200\201Semaphore\345\256\236\350\267\265\345\217\212\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/18. \345\271\266\345\217\221\345\267\245\345\205\267\347\261\273CountDownLatch\343\200\201CyclicBarrier\343\200\201Semaphore\345\256\236\350\267\265\345\217\212\346\272\220\347\240\201\345\210\206\346\236\220.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/\345\271\266\345\217\221\345\267\245\345\205\267\347\261\273CountDownLatch\343\200\201CyclicBarrier\343\200\201Semaphore\345\256\236\350\267\265\345\217\212\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/18. \345\271\266\345\217\221\345\267\245\345\205\267\347\261\273CountDownLatch\343\200\201CyclicBarrier\343\200\201Semaphore\345\256\236\350\267\265\345\217\212\346\272\220\347\240\201\345\210\206\346\236\220.md" diff --git "a/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\347\257\207/4. ThreadPoolExector\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/19. ThreadPoolExector\346\272\220\347\240\201\345\210\206\346\236\220.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\347\257\207/4. ThreadPoolExector\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/19. ThreadPoolExector\346\272\220\347\240\201\345\210\206\346\236\220.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/3. Java\345\271\266\345\217\221\347\274\226\347\250\213-wait\343\200\201notify\344\275\277\347\224\250\345\205\245\351\227\250.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/2. wait\343\200\201notify\344\275\277\347\224\250\345\205\245\351\227\250.md" similarity index 90% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/3. Java\345\271\266\345\217\221\347\274\226\347\250\213-wait\343\200\201notify\344\275\277\347\224\250\345\205\245\351\227\250.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/2. wait\343\200\201notify\344\275\277\347\224\250\345\205\245\351\227\250.md" index c82d19f..a7c7eb4 100644 --- "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/3. Java\345\271\266\345\217\221\347\274\226\347\250\213-wait\343\200\201notify\344\275\277\347\224\250\345\205\245\351\227\250.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/2. wait\343\200\201notify\344\275\277\347\224\250\345\205\245\351\227\250.md" @@ -5,31 +5,18 @@ wait,notify,notifyAll 是定义在Object类的实例方法,用于控制线程状态。 -### 文档分析 - -我们找到Object类,下载它的文档,翻译每个方法的注释。 - -总结如下: - -1. wait() 和 notify() 必须由对象持有者去调用,有三种方式: -1️⃣执行该对象的synchronized实例方法 -2️⃣执行synchronized代码块 -3️⃣执行该类的synchronized静态方法 +### 使用总结: +1. wait() 和 notify() 必须由对象持有者去调用,也就是说必须进入monitor监控,有三种方式: + 1. 执行该对象的synchronized实例方法 + 2. 执行synchronized代码块 + 3. 执行该类的synchronized静态方法 2. 当想要调用wait( )进行线程等待时,必须要取得这个锁对象的控制权(对象监视器),一般是放到synchronized(obj)代码中。 - 3. 在while循环里用wait操作性能更好(比if判断) - -4. 调用obj.wait( )释放了obj的锁,否则其他线程也无法获得obj的锁,也就无法在synchronized(obj){ obj.notify() } 代码段内唤醒A。 - -5. notify( )方法只会通知等待队列中的第一个相关线程(不会通知优先级比较高的线程) - +4. 调用obj.wait()方法会释放obj的锁,并将锁放入锁池队列。(锁池队列标识在对象头) +5. obj.notify( )方法只会通知等待队列中的第一个相关线程去锁池拿obj锁, 然后进入就绪状态(不会通知优先级比较高的线程) 6. notifyAll( )通知所有等待该竞争资源的线程(也不会按照线程的优先级来执行) -7. 如果是synchronized声明的方法,wait()操作后会施放synchronized锁,相反notify()触发后会重拿起synchronized锁。 - -8. 如果当前线程不是当前对象所持有,则会报异常IllegalMonitorStateException - ### 实例 ##### 1. 通过调用对象的wait和notify实现 diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/20. wait()\344\270\216sleep()\346\226\271\346\263\225\345\214\272\345\210\253.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/20. wait()\344\270\216sleep()\346\226\271\346\263\225\345\214\272\345\210\253.md" new file mode 100644 index 0000000..b08b57c --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/20. wait()\344\270\216sleep()\346\226\271\346\263\225\345\214\272\345\210\253.md" @@ -0,0 +1,10 @@ +本质上的区别: sleep是对线程状态的控制, wait是线程间通讯。一个是Thread类, 一个是Object类。Thread不会影响锁的行为,锁相关的方法都定义在Object类中 + +1. sleep是Thread的方法, wait是Object的方法。 +2. sleep不会释放锁, wait会施放锁并将锁添加到一个等待队列。 +3. sleep不依赖monitor, wait依赖monitor。 +4. sleep不需要唤醒、wait需要。 + +![img](https://img-blog.csdn.net/20150309140927553) + +**注意**:锁池是对象头中的一个标记,用于存放等待锁的线程。调用wait()方法, 意味着当前线程需要释放自己的所有锁放入锁池, 然后进入该对象的等待队列。当调用notify()时, 就通知等待队列中的一个线程出列, 然后进入锁池去拿到锁。 \ No newline at end of file diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/4. Java\345\271\266\345\217\221\347\274\226\347\250\213-\347\272\277\347\250\213\345\256\211\345\205\250\343\200\201\344\274\230\345\205\210\347\272\247\350\256\276\345\256\232.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/3. \347\272\277\347\250\213\345\256\211\345\205\250\343\200\201\344\274\230\345\205\210\347\272\247\350\256\276\345\256\232.md" similarity index 95% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/4. Java\345\271\266\345\217\221\347\274\226\347\250\213-\347\272\277\347\250\213\345\256\211\345\205\250\343\200\201\344\274\230\345\205\210\347\272\247\350\256\276\345\256\232.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/3. \347\272\277\347\250\213\345\256\211\345\205\250\343\200\201\344\274\230\345\205\210\347\272\247\350\256\276\345\256\232.md" index 35ae3f8..eddb293 100644 --- "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/4. Java\345\271\266\345\217\221\347\274\226\347\250\213-\347\272\277\347\250\213\345\256\211\345\205\250\343\200\201\344\274\230\345\205\210\347\272\247\350\256\276\345\256\232.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/3. \347\272\277\347\250\213\345\256\211\345\205\250\343\200\201\344\274\230\345\205\210\347\272\247\350\256\276\345\256\232.md" @@ -44,11 +44,10 @@ public class Task{ public int value = 0; - // 没有处理线程安全 public int getValue() { - return value++; - } + return value++; + } public static void main(String[] args) { Task task = new Task(); @@ -96,4 +95,4 @@ public class Task{ 既在修饰符后加上synchronized关键字。 ![image.png](http://upload-images.jianshu.io/upload_images/5786888-e0841cdc7b961279.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -但是又有一个问题,这样的话,其实原理上是串行处理的,那我们该如果解决这个问题呢。 +但是又有一个问题,这样的话,其实原理上是串行处理的, 性能比较低, 那我们该如何更好的解决这个问题呢。下一章分析。 diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/5. Java\345\271\266\345\217\221\347\274\226\347\250\213-\346\267\261\345\205\245\345\211\226\346\236\220volatile\345\205\263\351\224\256\345\255\227.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/4. \346\267\261\345\205\245\345\211\226\346\236\220volatile\345\205\263\351\224\256\345\255\227.md" similarity index 54% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/5. Java\345\271\266\345\217\221\347\274\226\347\250\213-\346\267\261\345\205\245\345\211\226\346\236\220volatile\345\205\263\351\224\256\345\255\227.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/4. \346\267\261\345\205\245\345\211\226\346\236\220volatile\345\205\263\351\224\256\345\255\227.md" index 630c16d..cdc9306 100644 --- "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/5. Java\345\271\266\345\217\221\347\274\226\347\250\213-\346\267\261\345\205\245\345\211\226\346\236\220volatile\345\205\263\351\224\256\345\255\227.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/4. \346\267\261\345\205\245\345\211\226\346\236\220volatile\345\205\263\351\224\256\345\255\227.md" @@ -1,5 +1,5 @@ ### 1.volatile关键字的两层语义 -一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: +>一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1. 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。 2. 禁止进行指令重排序。 @@ -17,20 +17,19 @@ while(!stop){ //线程2 stop = true; ``` -事实上,这段代码会真的先执行线程1,然后再执行2吗,答案是肯定的:不是。两个线程各干各的事情,没有绝对的先后问题,所以会出现两种答案(一个线程用stop=false跑线程1,一个线程用stop=true跑线程二,互不相关)。 +事实上,这段代码会真的先执行线程1,然后再执行2吗,答案是肯定的:不是。两个线程各干各的事情,没有绝对的先后问题,所以会出现两种答案(一个线程用stop=false跑线程1,一个线程用stop=true跑线程二,互不相关), 那如何保证stop在两个线程中变化可见呢?将该变量用volatile修饰。 -###### 但是用volatile修饰之后就变得不一样了: +#### volatile修饰使得变量多线程可见原理: +>这个涉及到jvm的内存模型, 这个内存模型也就是下面三点描述的: -第一:使用volatile关键字会强制将修改的值立即写入主存; +1. 使用volatile关键字会强制将修改的值立即写入主存; +2. 使用volatile关键字的话,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效); +3. 由于线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。 -第二:使用volatile关键字的话,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效); - -第三:由于线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。 - -所以肯定能保证不管 多少个线程跑的时候,stop的值是相同的。这是利用了volatile的线程可见性原理。 +所以不管多少个线程跑的时候,stop的值是各线程保持同步的。这是利用了volatile的线程可见性原理。 ### 2.volatile保证原子性吗? -从上面知道volatile关键字保证了操作的可见性,但是volatile能保证对变量的操作是原子性吗?看一段代码: +>从上面知道volatile关键字保证了操作的可见性,但是volatile不能保证对变量的操作是原子性。 ``` public class Test { public volatile int inc = 0; @@ -62,11 +61,11 @@ public class Test { * 假如线程1从住内存中获取到变量值,在执行自增的时候是阻塞性质的,这时候线程2也拿到一个相同的值,然后也进行自增,那么这两个线程最终写入的值是一样的。 -##### 那如何修改呢?有三种方式 -1. 给自增方法加上同步锁synchronized +#### 那如何保证原子性呢?有三种方式 +##### 1. 给自增方法加上同步锁synchronized ``` public class Test { - public int inc = 0; + public volatile int inc = 0; public synchronized void increase() { inc++; @@ -89,10 +88,10 @@ public class Test { } } ``` -2. 采用Lock +##### 2. 采用Lock ``` public class Test { - public int inc = 0; + public volatile int inc = 0; Lock lock = new ReentrantLock(); public void increase() { @@ -121,7 +120,9 @@ public class Test { } } ``` -3. 采用AtomicInteger +##### 3. 采用AtomicInteger +>在java 1.5的java.util.concurrent.atomic包下提供了一些原子操作类,即对基本数据类型的 自增(加1操作),自减(减1操作)、以及加法操作(加一个数),减法操作(减一个数)进行了封装,保证这些操作是原子性操作。atomic是利用CAS来实现原子性操作的(Compare And Swap),CAS实际上是利用处理器提供的CMPXCHG指令实现的,而处理器执行CMPXCHG指令是一个原子性操作。 + ``` public class Test { public AtomicInteger inc = new AtomicInteger(); @@ -147,25 +148,48 @@ public class Test { } } ``` -在java 1.5的java.util.concurrent.atomic包下提供了一些原子操作类,即对基本数据类型的 自增(加1操作),自减(减1操作)、以及加法操作(加一个数),减法操作(减一个数)进行了封装,保证这些操作是原子性操作。atomic是利用CAS来实现原子性操作的(Compare And Swap),CAS实际上是利用处理器提供的CMPXCHG指令实现的,而处理器执行CMPXCHG指令是一个原子性操作。 -### 3.volatile能保证有序性吗? -在前面提到volatile关键字能禁止指令重排序,所以volatile能在一定程度上保证有序性。 +### 3.volatile有序性 +>volatile保证有序性通过两点:也就是Happen-Before原则: +1. 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行; +2. 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。 -### 4.volatile的原理和实现机制 -前面讲述了源于volatile关键字的一些使用,下面我们来探讨一下volatile到底如何保证可见性和禁止指令重排序的。 +##### 有序性例子 +``` +//x、y为非volatile变量 +//flag为volatile变量 + +x = 2; //语句1 +y = 0; //语句2 +flag = true; //语句3 +x = 4; //语句4 +y = -1; //语句5 +``` +由于flag变量为volatile变量,那么在进行指令重排序的过程的时候,不会将语句3放到语句1、语句2前面,也不会讲语句3放到语句4、语句5后面。但是要注意语句1和语句2的顺序、语句4和语句5的顺序是不作任何保证的。同时volatile也能保证执行顺序与前面编译顺序一样。 -下面这段话摘自《深入理解Java虚拟机》: +##### 应该保证有序性而没有使用volatile会怎样呢 +``` +//线程1: +context = loadContext(); //语句1 +inited = true; //语句2 + +//线程2: +while(!inited ){ + sleep() +} +doSomethingwithconfig(context); +``` +前面举这个例子的时候,提到有可能语句2会在语句1之前执行,那么久可能导致context还没被初始化,而线程2中就使用未初始化的context去进行操作,导致程序报空指针异常。如果用volatile关键字对inited变量进行修饰,就不会出现这种问题了。 -“观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令” +### 4.volatile的原理和实现机制 +>前面讲述了源于volatile关键字的一些使用,下面我们来探讨一下volatile到底如何保证可见性和禁止指令重排序的。参考:《深入理解Java虚拟机》: -lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能: +观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令. +lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能: 1. 它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成; - 2. 它会强制将对缓存的修改操作立即写入主存; - 3. 如果是写操作,它会导致其他CPU中对应的缓存行无效。 diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/6. Java\345\271\266\345\217\221\347\274\226\347\250\213-synchronized\344\277\235\350\257\201\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\216\237\347\220\206.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/5. synchronized\344\277\235\350\257\201\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\216\237\347\220\206.md" similarity index 69% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/6. Java\345\271\266\345\217\221\347\274\226\347\250\213-synchronized\344\277\235\350\257\201\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\216\237\347\220\206.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/5. synchronized\344\277\235\350\257\201\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\216\237\347\220\206.md" index 73ce122..6012bbe 100644 --- "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/6. Java\345\271\266\345\217\221\347\274\226\347\250\213-synchronized\344\277\235\350\257\201\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\216\237\347\220\206.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/5. synchronized\344\277\235\350\257\201\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\216\237\347\220\206.md" @@ -1,18 +1,25 @@ +### 什么时候会发生线程安全问题: +>线程安全是并发编程中的重要关注点,应该注意到的是,造成线程安全问题的主要诱因有两点: +1. 存在共享数据(也称临界资源)。 +2. 存在多条线程共同操作共享数据。 -线程安全是并发编程中的重要关注点,应该注意到的是,造成线程安全问题的主要诱因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据。因此为了解决这个问题,我们可能需要这样一个方案,当存在多个线程操作共享数据时,需要保证同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再进行,这种方式有个高尚的名称叫互斥锁,即能达到互斥访问目的的锁,也就是说当一个共享数据被当前正在访问的线程加上互斥锁后,在同一个时刻,其他线程只能处于等待的状态,直到当前线程处理完毕释放该锁。在 Java 中,关键字 synchronized可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),同时我们还应该注意到synchronized另外一个重要的作用,synchronized可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以替代Volatile功能),这点确实也是很重要的。 +那如何保证线程安全呢?java提供了一些线程安全的工具, 这里主要介绍synchronized锁。 +### synchronized的三种使用方式 +>synchronized关键字最主要有以下3种使用方式,下面分别介绍 -### synchronized的三种应用方式 -synchronized关键字最主要有以下3种应用方式,下面分别介绍 +1. 修饰实例方法。作用于当前实例加锁,进入同步代码前要获得当前实例的锁 +2. 修饰静态方法。作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁 +3. 修饰代码块。指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。 -* 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁 +### synchronized与this锁(对象锁)、class锁(类锁)的关系 -* 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁 - -* 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。 +1. 如果修饰的是静态方法,则拿到的是类锁。因为此时对象实例还没有生成。如果修饰普通方法,则拿到的是对象锁,同一个实例会拦截。 +2. 只要采用类锁`synchronized(xxx.class)`, 就会拦截所有线程,同时只能有一个线程访问。 +3. 只要采用对象锁`sychronized(this)`, 如果是同一个实例就会拦截, 如果是不同实例, 则可以同时访问。 ##### synchronized作用于实例方法 -所谓的实例对象锁就是用synchronized修饰实例对象中的实例方法,注意是实例方法不包括静态方法,如下 +>所谓的实例对象锁就是用synchronized修饰实例对象中的实例方法,注意是实例方法不包括静态方法,如下 ``` public class AccountingSync implements Runnable{ //共享资源(临界资源) @@ -72,11 +79,13 @@ public class AccountingSyncBad implements Runnable{ System.out.println(i); } } + +控制台结果:1452317 ``` 上述代码与前面不同的是我们同时创建了两个新实例AccountingSyncBad,然后启动两个不同的线程对共享变量i进行操作,但很遗憾操作结果是1452317而不是期望结果2000000,因为上述代码犯了严重的错误,虽然我们使用synchronized修饰了increase方法,但却new了两个不同的实例对象,这也就意味着存在着两个不同的实例对象锁,因此t1和t2都会进入各自的对象锁,也就是说t1和t2线程使用的是不同的锁,因此线程安全是无法保证的。解决这种困境的的方式是将synchronized作用于静态的increase方法,这样的话,对象锁就当前类对象,由于无论创建多少个实例对象,但对于的类对象拥有只有一个,所有在这样的情况下对象锁就是唯一的。下面我们看看如何使用将synchronized作用于静态的increase方法。 ##### synchronized作用于静态方法 -当synchronized作用于静态方法时,其锁就是当前类的class对象锁。由于静态成员不专属于任何一个实例对象,是类成员,因此通过class对象锁可以控制静态 成员的并发操作。需要注意的是如果一个线程A调用一个实例对象的非static synchronized方法,而线程B需要调用这个实例对象所属类的静态 synchronized方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的class对象,而访问非静态 synchronized 方法占用的锁是当前实例对象锁,看如下代码 +>当synchronized作用于静态方法时,其锁就是当前类的class对象锁。由于静态成员不专属于任何一个实例对象,是类成员,因此通过class对象锁可以控制静态 成员的并发操作。需要注意的是如果一个线程A调用一个实例对象的非static synchronized方法,而线程B需要调用这个实例对象所属类的静态 synchronized方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的class对象,而访问非静态 synchronized 方法占用的锁是当前实例对象锁,看如下代码 ``` public class AccountingSyncClass implements Runnable{ static int i=0; @@ -118,7 +127,7 @@ public class AccountingSyncClass implements Runnable{ 由于synchronized关键字修饰的是静态increase方法,与修饰实例方法不同的是,其锁对象是当前类的class对象。注意代码中的increase4Obj方法是实例方法,其对象锁是当前实例对象,如果别的线程调用该方法,将不会产生互斥现象,毕竟锁对象不同,但我们应该意识到这种情况下可能会发现线程安全问题(操作了共享静态变量i)。 ##### synchronized同步代码块 -除了使用关键字修饰实例方法和静态方法外,还可以使用同步代码块,在某些情况下,我们编写的方法体可能比较大,同时存在一些比较耗时的操作,而需要同步的代码又只有一小部分,如果直接对整个方法进行同步操作,可能会得不偿失,此时我们可以使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作了,同步代码块的使用示例如下: +>除了使用关键字修饰实例方法和静态方法外,还可以使用同步代码块,在某些情况下,我们编写的方法体可能比较大,同时存在一些比较耗时的操作,而需要同步的代码又只有一小部分,如果直接对整个方法进行同步操作,可能会得不偿失,此时我们可以使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作了,同步代码块的使用示例如下: ``` public class AccountingSync implements Runnable{ static AccountingSync instance=new AccountingSync(); @@ -163,7 +172,7 @@ synchronized(AccountingSync.class){ ### synchronized底层语义原理 Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现, 无论是显式同步(有明确的 monitorenter 和 monitorexit 指令,即同步代码块)还是隐式同步都是如此。在 Java 语言中,同步用的最多的地方可能是被 synchronized 修饰的同步方法。同步方法 并不是由 monitorenter 和 monitorexit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的,关于这点,稍后详细分析。下面先来了解一个概念Java对象头,这对深入理解synchronized实现原理非常关键。 -  如果对上面的执行结果还有疑问,也先不用急,我们先来了解Synchronized的原理,再回头上面的问题就一目了然了。我们先通过反编译下面的代码来看看Synchronized是如何实现对代码块进行同步的: +如果对上面的执行结果还有疑问,也先不用急,我们先来了解Synchronized的原理,再回头上面的问题就一目了然了。我们先通过反编译下面的代码来看看Synchronized是如何实现对代码块进行同步的: ``` public class SynchronizedDemo { public void method() { @@ -180,15 +189,7 @@ public class SynchronizedDemo { 关于这两条指令的作用,我们直接参考JVM规范中描述: ##### monitorenter : -``` -Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows: -• If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor. -• If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count. -• If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership. -``` -这段话的大概意思为: - -每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下: +>每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下: 1. 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。 @@ -197,17 +198,11 @@ Each object is associated with a monitor. A monitor is locked if and only if it 3. 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。 ##### monitorexit: -``` -The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref. -The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so. -``` -这段话的大概意思为: - -执行monitorexit的线程必须是objectref所对应的monitor的所有者。 +>执行monitorexit的线程必须是objectref所对应的monitor的所有者。 指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。 -  通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。 +通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。 我们再来看一下同步方法的反编译结果: @@ -435,26 +430,34 @@ synchronized (obj) { ## Java虚拟机对synchronized的优化 -锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁,但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级,关于重量级锁,前面我们已详细分析过,下面我们将介绍偏向锁和轻量级锁以及JVM的其他优化手段,这里并不打算深入到每个锁的实现和转换过程更多地是阐述Java虚拟机所提供的每个锁的核心优化思想,毕竟涉及到具体过程比较繁琐,如需了解详细过程可以查阅《深入理解Java虚拟机原理》。 +**锁主要存在四中状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。**他们会随着竞争的激烈而逐渐升级。注意锁可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率。 + +###### **锁粗化** + +就是将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁。 + +如上面实例:vector每次add的时候都需要加锁操作,JVM检测到对同一个对象(vector)连续加锁、解锁操作,会合并一个更大范围的加锁、解锁操作,即加锁解锁操作会移到for循环之外。 + +##### 锁消除 + +如果JVM不可能存在资源竞争,变量不可能逃逸到方法外,则将锁消除。 -### 偏向锁 +##### 自旋锁 -偏向锁是Java 6之后加入的新锁,它是一种针对加锁操作的优化手段,经过研究发现,在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,因此为了减少同一线程获取锁(会涉及到一些CAS操作,耗时)的代价而引入偏向锁。偏向锁的核心思想是,如果一个线程获得了锁,那么锁就进入偏向模式,此时Mark Word 的结构也变为偏向锁结构,当这个线程再次请求锁时,无需再做任何同步操作,即获取锁的过程,这样就省去了大量有关锁申请的操作,从而也就提供程序的性能。所以,对于没有锁竞争的场合,偏向锁有很好的优化效果,毕竟极有可能连续多次是同一个线程申请相同的锁。但是对于锁竞争比较激烈的场合,偏向锁就失效了,因为这样场合极有可能每次申请锁的线程都是不相同的,因此这种场合下不应该使用偏向锁,否则会得不偿失,需要注意的是,偏向锁失败后,并不会立即膨胀为重量级锁,而是先升级为轻量级锁。下面我们接着了解轻量级锁。 +对象锁的锁状态只会持续很短一段时间**,**为了这一段很短的时间频繁地阻塞和唤醒线程是非常不值得的。 -### 轻量级锁 +##### 偏向锁 -倘若偏向锁失败,虚拟机并不会立即升级为重量级锁,它还会尝试使用一种称为轻量级锁的优化手段(1.6之后加入的),此时Mark Word 的结构也变为轻量级锁的结构。轻量级锁能够提升程序性能的依据是“对绝大部分的锁,在整个同步周期内都不存在竞争”,注意这是经验数据。需要了解的是,轻量级锁所适应的场景是线程交替执行同步块的场合,如果存在同一时间访问同一锁的场合,就会导致轻量级锁膨胀为重量级锁。 +为了在非多线程的情况下减少线程间锁的交换消耗资源(CAS操作)。偏向锁只能被动释放噢,等锁有了竞争 -### 自旋锁 +##### 轻量级锁 -轻量级锁失败后,虚拟机为了避免线程真实地在操作系统层面挂起,还会进行一项称为自旋锁的优化手段。这是基于在大多数情况下,线程持有锁的时间都不会太长,如果直接挂起操作系统层面的线程可能会得不偿失,毕竟操作系统实现线程之间的切换时需要从用户态转换到核心态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,因此自旋锁会假设在不久将来,当前的线程可以获得锁,因此虚拟机会让当前想要获取锁的线程做几个空循环(这也是称为自旋的原因),一般不会太久,可能是50个循环或100循环,在经过若干次循环后,如果得到锁,就顺利进入临界区。如果还不能获得锁,那就会将线程在操作系统层面挂起,这就是自旋锁的优化方式,这种方式确实也是可以提升效率的。最后没办法也就只能升级为重量级锁了。 +当多个线程竞争偏向锁,会升级为轻量级锁。(利用CAS操作更新对象的Mark Word,如果失败则升级为重量级锁) -### 锁消除 +##### 重量级锁 -消除锁是虚拟机另外一种锁的优化,这种优化更彻底,Java虚拟机在JIT编译时(可以简单理解为当某段代码即将第一次被执行时进行编译,又称即时编译),通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过这种方式消除没有必要的锁,可以节省毫无意义的请求锁时间,如下StringBuffer的append是一个同步方法,但是在add方法中的StringBuffer属于一个局部变量,并且不会被其他线程所使用,因此StringBuffer不可能存在共享资源竞争的情景,JVM会自动将其锁消除。 +重量级锁通过对象内部的监视器(monitor)实现,其中monitor的本质是依赖于底层操作系统的Mutex Lock实现,操作系统实现线程之间的切换需要从用户态到内核态的切换,切换成本非常高。 -### 重量级锁 -即synchronized,一直等待线程施放锁后才可以拿到资源。 >本篇的主要参考资料: 《Java编程思想》 《深入理解Java虚拟机》 diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/7. Java\345\271\266\345\217\221\347\274\226\347\250\213-join\346\226\271\346\263\225.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/6. Join\346\226\271\346\263\225.md" similarity index 63% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/7. Java\345\271\266\345\217\221\347\274\226\347\250\213-join\346\226\271\346\263\225.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/6. Join\346\226\271\346\263\225.md" index 3bae674..3a274c1 100644 --- "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/7. Java\345\271\266\345\217\221\347\274\226\347\250\213-join\346\226\271\346\263\225.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/6. Join\346\226\271\346\263\225.md" @@ -40,35 +40,32 @@ a方法执行完毕 ### 源码分析 ``` - public final synchronized void join(long millis) - throws InterruptedException { - long base = System.currentTimeMillis(); - long now = 0; +public final synchronized void join(long millis) +throws InterruptedException { + long base = System.currentTimeMillis(); + long now = 0; - if (millis < 0) { - throw new IllegalArgumentException("timeout value is negative"); - } + if (millis < 0) { + throw new IllegalArgumentException("timeout value is negative"); + } - if (millis == 0) { - while (isAlive()) { - //wait操作挂起 调用join方法的线程锁 - wait(0); - } - } else { - while (isAlive()) { - long delay = millis - now; - if (delay <= 0) { - break; - } - wait(delay); - now = System.currentTimeMillis() - base; + if (millis == 0) { + while (isAlive()) { + //wait操作挂起 调用join方法的线程锁 + wait(0); + } + } else { + while (isAlive()) { + long delay = millis - now; + if (delay <= 0) { + break; } + wait(delay); + now = System.currentTimeMillis() - base; } } +} ``` 其中参数`long millis`是来规范join执行的时间,默认为0. `isAlive():` `A thread is alive if it has been started and has not yet died` -如果当前线程活着,就将其挂起(wait)。 - -##### 那什么时候才会施放线程锁呢? -每当线程执行完毕后,默认会调用notifyAll方法。 +如果当前线程活着,就将其挂起(wait)。每当线程执行完毕后,默认会调用notifyAll方法。 diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/8. Java\345\271\266\345\217\221\347\274\226\347\250\213-\346\234\254\345\234\260\347\272\277\347\250\213ThreadLocal.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/7. \346\234\254\345\234\260\347\272\277\347\250\213ThreadLocal.md" similarity index 88% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/8. Java\345\271\266\345\217\221\347\274\226\347\250\213-\346\234\254\345\234\260\347\272\277\347\250\213ThreadLocal.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/7. \346\234\254\345\234\260\347\272\277\347\250\213ThreadLocal.md" index 2f6a6a8..0ee8825 100644 --- "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/8. Java\345\271\266\345\217\221\347\274\226\347\250\213-\346\234\254\345\234\260\347\272\277\347\250\213ThreadLocal.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/7. \346\234\254\345\234\260\347\272\277\347\250\213ThreadLocal.md" @@ -61,6 +61,7 @@ Thread-0 :2 #### 1. get() +>先获取到当前线程, 然后根据当前线程获取ThreadLocalMap, 然后返回其value值。 ``` public T get() { Thread t = Thread.currentThread(); @@ -113,16 +114,20 @@ Thread-0 :2 ``` ##### 1.4 Entry ``` - static class Entry extends WeakReference> { - /** The value associated with this ThreadLocal. */ - Object value; +static class Entry extends WeakReference> { + /** The value associated with this ThreadLocal. */ + Object value; - Entry(ThreadLocal k, Object v) { - super(k); - value = v; - } - } + Entry(ThreadLocal k, Object v) { + super(k); + value = v; + } +} ``` +注意这里继承了WeakReference, 标明了它是一个软引用。 +>为什么用软引用呢? + +假如每个key都强引用指向threadlocal,也就是上图虚线那里是个强引用,那么这个threadlocal就会因为和entry存在强引用无法被回收!造成内存泄漏 ,除非线程结束,线程被回收了,map也跟着回收。 ##### 1.5 ThreadLocal和ThreadLocalMap对应关系 ![ThreadLocal和ThreadLocalMap对应关系](https://upload-images.jianshu.io/upload_images/5786888-ea551311d7b3cf2f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/8. Java\345\271\266\345\217\221\347\274\226\347\250\213-ThreadLocal\346\267\261\345\205\245.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/8. ThreadLocal\345\206\205\345\255\230\346\263\204\346\274\217\351\227\256\351\242\230.md" similarity index 88% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/8. Java\345\271\266\345\217\221\347\274\226\347\250\213-ThreadLocal\346\267\261\345\205\245.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/8. ThreadLocal\345\206\205\345\255\230\346\263\204\346\274\217\351\227\256\351\242\230.md" index b25f082..1d17b48 100644 --- "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/8. Java\345\271\266\345\217\221\347\274\226\347\250\213-ThreadLocal\346\267\261\345\205\245.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/8. ThreadLocal\345\206\205\345\255\230\346\263\204\346\274\217\351\227\256\351\242\230.md" @@ -1,10 +1,11 @@ ### ThreadLocal +>我们知道ThreadLocal可以用来做线程隔离, 但如果使用不当也会产生内存泄漏的问题。这篇文章主要通过内存泄漏来讲述。 -#### 内存泄漏 +#### 内存泄漏原因 -ThreadLocal内存泄露,最主要的原因在于它的内部类ThreadLocalMap中的Entry的设计。Entry继承了WeakReference>,即Entry的key是弱引用,所以key'会在垃圾回收的时候被回收掉, 而key对应的value则不会被回收, 这样会导致一种现象:key为null,value有值。key为空的话value是无效数据,久而久之,value累加就会导致内存泄漏。 +ThreadLocal内存泄露,最主要的原因在于它的内部类ThreadLocalMap中的Entry的设计。Entry继承了WeakReference>,即Entry的key是弱引用: -```java +``` static class ThreadLocalMap { static class Entry extends WeakReference> { Object value; @@ -17,6 +18,11 @@ static class ThreadLocalMap { ... } ``` +#### 为什么要设计为软连接 +假如每个key都强引用指向ThreadLocal,也就是上图虚线那里是个强引用,那么这个ThreadLocal就会因为和entry存在强引用无法被回收!造成内存泄漏 ,除非线程结束,线程被回收了,map也跟着回收。 + +#### 为什么已经是软连接了还是会内存泄漏 +因为我们都用线程池来提高性能,Thread并没有像预期那样执行完就销毁,所以key'会在垃圾回收的时候被回收掉, 而key对应的value则不会被回收, 这样会导致一种现象:key为null,value有值。key为空的话value是无效数据,久而久之,value累加就会导致内存泄漏。 ##### 怎么解决这个内存泄漏问题 @@ -26,7 +32,7 @@ static class ThreadLocalMap { ThreadLocal提供的get()方法中,调用了ThreadLocalMap#getEntry()方法,对key进行了校验和对null key进行擦除。 -```java +``` private Entry getEntry(ThreadLocal key) { // 拿到索引位置 int i = key.threadLocalHashCode & (table.length - 1); diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/9. Java\345\271\266\345\217\221\347\274\226\347\250\213-Atomic\345\214\205.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/9. Atomic\345\214\205.md" similarity index 93% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/9. Java\345\271\266\345\217\221\347\274\226\347\250\213-Atomic\345\214\205.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/9. Atomic\345\214\205.md" index 284c726..d58317f 100644 --- "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/9. Java\345\271\266\345\217\221\347\274\226\347\250\213-Atomic\345\214\205.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/9. Atomic\345\214\205.md" @@ -1,8 +1,9 @@ Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作。原子变量的底层使用了处理器提供的原子指令,但是不同的CPU架构可能提供的原子指令不一样,也有可能需要某种形式的内部锁,所以该方法不能绝对保证线程不被阻塞。 ### Atomic包介绍 -官方解释:一个小型工具包,支持单变量上的无锁线程安全编程。 +>一个小型工具包,支持单变量上的无锁线程安全编程。 + ![image.png](http://upload-images.jianshu.io/upload_images/5786888-98aaa28314c8db01.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -可以看到,它里面有17个java类。 +它里面有17个java类。 ### 原子更新基本类型类 用于通过原子的方式更新基本类型,Atomic包提供了以下三个类: @@ -77,15 +78,15 @@ public class AtomicIntegerTest { } ``` -用的lambda表达式,不懂的可以看我的文章:https://www.jianshu.com/p/3a08dc78a05f +用的lambda表达式, 没有学过的可以先看看这些文章:https://www.jianshu.com/p/3a08dc78a05f -### 餐后甜点 +### 那如何更新double类型的数据呢 Atomic包提供了三种基本类型的原子更新,但是Java的基本类型里还有char,float和double等。那么问题来了,如何原子的更新其他的基本类型呢?Atomic包里的类基本都是使用Unsafe实现的,让我们一起看下[Unsafe的源码](http://www.docjar.com/html/api/sun/misc/Unsafe.java.html),发现Unsafe只提供了三种CAS方法,compareAndSwapObject,compareAndSwapInt和compareAndSwapLong,再看AtomicBoolean源码,发现其是先把Boolean转换成整型,再使用compareAndSwapInt进行CAS,所以原子更新double也可以用类似的思路来实现。 ### 原子更新数组类 -通过原子的方式更新数组里的某个元素,Atomic包提供了以下三个类: +>通过原子的方式更新数组里的某个元素,Atomic包提供了以下三个类: * AtomicIntegerArray:原子更新整型数组里的元素。 * AtomicLongArray:原子更新长整型数组里的元素。 @@ -110,7 +111,7 @@ public class AtomicIntegerArrayTest { ### 原子更新引用类型 -原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子的更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic包提供了以下三个类: +>原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子的更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic包提供了以下三个类: AtomicReference:原子更新引用类型。 AtomicReferenceFieldUpdater:原子更新引用类型里的字段。 @@ -170,7 +171,7 @@ public class AtomicReferenceTest { ``` ### 原子更新字段类 -**如果我们只需要某个类里的某个字段,那么就需要使用原子更新字段类**,Atomic包提供了以下三个类: +>**如果我们只需要某个类里的某个字段,那么就需要使用原子更新字段类**,Atomic包提供了以下三个类: * AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。 * AtomicLongFieldUpdater:原子更新长整型字段的更新器。 @@ -236,7 +237,7 @@ public class AtomicIntegerFieldUpdaterTest { ``` -那最后,再来解一下CAS的疑惑 +那最后,聊一聊CAS ##### 什么是CAS diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/1. Java\345\217\215\345\260\204---\345\211\215\350\250\200.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/1. \345\211\215\350\250\200.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/1. Java\345\217\215\345\260\204---\345\211\215\350\250\200.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/1. \345\211\215\350\250\200.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/10. Java\345\217\215\345\260\204---\346\263\233\345\236\213.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/10. \346\263\233\345\236\213.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/10. Java\345\217\215\345\260\204---\346\263\233\345\236\213.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/10. \346\263\233\345\236\213.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/11. Java\345\217\215\345\260\204---\345\212\250\346\200\201\347\261\273\345\212\240\350\275\275\345\222\214\351\207\215\350\275\275.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/11. \345\212\250\346\200\201\347\261\273\345\212\240\350\275\275\345\222\214\351\207\215\350\275\275.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/11. Java\345\217\215\345\260\204---\345\212\250\346\200\201\347\261\273\345\212\240\350\275\275\345\222\214\351\207\215\350\275\275.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/11. \345\212\250\346\200\201\347\261\273\345\212\240\350\275\275\345\222\214\351\207\215\350\275\275.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/12. Java\345\217\215\345\260\204---\345\212\250\346\200\201\344\273\243\347\220\206.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/12. \345\212\250\346\200\201\344\273\243\347\220\206.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/12. Java\345\217\215\345\260\204---\345\212\250\346\200\201\344\273\243\347\220\206.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/12. \345\212\250\346\200\201\344\273\243\347\220\206.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/2. Java\345\217\215\345\260\204---\345\255\227\346\256\265.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/2. \345\255\227\346\256\265.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/2. Java\345\217\215\345\260\204---\345\255\227\346\256\265.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/2. \345\255\227\346\256\265.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/3. Java\345\217\215\345\260\204---\346\225\260\347\273\204.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/3. \346\225\260\347\273\204.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/3. Java\345\217\215\345\260\204---\346\225\260\347\273\204.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/3. \346\225\260\347\273\204.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/4. Java\345\217\215\345\260\204---\347\247\201\346\234\211\345\255\227\346\256\265\345\222\214\346\226\271\346\263\225.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/4. \347\247\201\346\234\211\345\255\227\346\256\265\345\222\214\346\226\271\346\263\225.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/4. Java\345\217\215\345\260\204---\347\247\201\346\234\211\345\255\227\346\256\265\345\222\214\346\226\271\346\263\225.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/4. \347\247\201\346\234\211\345\255\227\346\256\265\345\222\214\346\226\271\346\263\225.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/5. Java\345\217\215\345\260\204---\346\226\271\346\263\225-Methods.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/5. \346\226\271\346\263\225-Methods.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/5. Java\345\217\215\345\260\204---\346\226\271\346\263\225-Methods.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/5. \346\226\271\346\263\225-Methods.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/6. Java\345\217\215\345\260\204---\350\216\267\345\217\226Getters-and-Setters.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/6. \350\216\267\345\217\226Getters-and-Setters.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/6. Java\345\217\215\345\260\204---\350\216\267\345\217\226Getters-and-Setters.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/6. \350\216\267\345\217\226Getters-and-Setters.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/7. Java\345\217\215\345\260\204---\346\236\204\351\200\240\345\207\275\346\225\260.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/7. \346\236\204\351\200\240\345\207\275\346\225\260.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/7. Java\345\217\215\345\260\204---\346\236\204\351\200\240\345\207\275\346\225\260.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/7. \346\236\204\351\200\240\345\207\275\346\225\260.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/8. Java\345\217\215\345\260\204---\346\263\250\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/8. \346\263\250\350\247\243.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/8. Java\345\217\215\345\260\204---\346\263\250\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/8. \346\263\250\350\247\243.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/9. Java\345\217\215\345\260\204---\347\261\273\345\257\271\350\261\241.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/9. \347\261\273\345\257\271\350\261\241.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/3. \345\217\215\345\260\204\347\257\207/9. Java\345\217\215\345\260\204---\347\261\273\345\257\271\350\261\241.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/9. \347\261\273\345\257\271\350\261\241.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/1. JDBC-\346\246\202\350\277\260.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/1. \346\246\202\350\277\260.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/1. JDBC-\346\246\202\350\277\260.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/1. \346\246\202\350\277\260.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/2. JDBC-\351\251\261\345\212\250\347\250\213\345\272\217\347\261\273\345\236\213\345\217\221\345\261\225\345\216\206\347\250\213.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/2. JDBC-\351\251\261\345\212\250\347\250\213\345\272\217\347\261\273\345\236\213\345\217\221\345\261\225\345\216\206\347\250\213.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/2. JDBC-\351\251\261\345\212\250\347\250\213\345\272\217\347\261\273\345\236\213\345\217\221\345\261\225\345\216\206\347\250\213.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/2. JDBC-\351\251\261\345\212\250\347\250\213\345\272\217\347\261\273\345\236\213\345\217\221\345\261\225\345\216\206\347\250\213.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/3. JDBC-\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\345\222\214\346\225\260\346\215\256\345\242\236\345\210\240\346\224\271\346\237\245.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/3. \346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\345\222\214\346\225\260\346\215\256\345\242\236\345\210\240\346\224\271\346\237\245.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/3. JDBC-\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\345\222\214\346\225\260\346\215\256\345\242\236\345\210\240\346\224\271\346\237\245.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/3. \346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\345\222\214\346\225\260\346\215\256\345\242\236\345\210\240\346\224\271\346\237\245.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/4. JDBC-ResultSet-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/4. ResultSet-\350\257\246\350\247\243.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/4. JDBC-ResultSet-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/4. ResultSet-\350\257\246\350\247\243.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/5. JDBC-PreparedStatement-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/5. PreparedStatement-\350\257\246\350\247\243.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/5. JDBC-PreparedStatement-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/5. PreparedStatement-\350\257\246\350\247\243.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/6. JDBC-\346\211\271\351\207\217\345\244\204\347\220\206sql.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/6. \346\211\271\351\207\217\345\244\204\347\220\206sql.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/6. JDBC-\346\211\271\351\207\217\345\244\204\347\220\206sql.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/6. \346\211\271\351\207\217\345\244\204\347\220\206sql.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/7. JDBC-\344\272\213\345\212\241Transaction.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/7. \344\272\213\345\212\241Transaction.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/7. JDBC-\344\272\213\345\212\241Transaction.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/7. \344\272\213\345\212\241Transaction.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/8. JDBC-CallableStatement-\345\255\230\345\202\250\350\277\207\347\250\213\350\260\203\347\224\250.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/8. CallableStatement-\345\255\230\345\202\250\350\277\207\347\250\213\350\260\203\347\224\250.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/8. JDBC-CallableStatement-\345\255\230\345\202\250\350\277\207\347\250\213\350\260\203\347\224\250.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/8. CallableStatement-\345\255\230\345\202\250\350\277\207\347\250\213\350\260\203\347\224\250.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/9. JDBC-DatabaseMetaData-\346\225\260\346\215\256\345\272\223\345\205\203\346\225\260\346\215\256.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/9. DatabaseMetaData-\346\225\260\346\215\256\345\272\223\345\205\203\346\225\260\346\215\256.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/4. JDBC\347\257\207/9. JDBC-DatabaseMetaData-\346\225\260\346\215\256\345\272\223\345\205\203\346\225\260\346\215\256.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/9. DatabaseMetaData-\346\225\260\346\215\256\345\272\223\345\205\203\346\225\260\346\215\256.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/1. Java-Servlet-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/1. Servlet-\350\257\246\350\247\243.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/1. Java-Servlet-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/1. Servlet-\350\257\246\350\247\243.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/10. Java-ServletContext-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/10. ServletContext-\350\257\246\350\247\243.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/10. Java-ServletContext-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/10. ServletContext-\350\257\246\350\247\243.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/11. Java-Servlet-web-xml-\351\205\215\347\275\256\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/11. Servlet-web-xml-\351\205\215\347\275\256\350\257\246\350\247\243.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/11. Java-Servlet-web-xml-\351\205\215\347\275\256\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/11. Servlet-web-xml-\351\205\215\347\275\256\350\257\246\350\247\243.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/12. JavaEE-Servlet-\345\271\266\345\217\221\351\227\256\351\242\230.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/12. JavaEE-Servlet-\345\271\266\345\217\221\351\227\256\351\242\230.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/12. JavaEE-Servlet-\345\271\266\345\217\221\351\227\256\351\242\230.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/12. JavaEE-Servlet-\345\271\266\345\217\221\351\227\256\351\242\230.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/2. Java-HttpServlet-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/2. HttpServlet-\350\257\246\350\247\243.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/2. Java-HttpServlet-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/2. HttpServlet-\350\257\246\350\247\243.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/3. Java-HttpRequest-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/3. HttpRequest-\350\257\246\350\247\243.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/3. Java-HttpRequest-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/3. HttpRequest-\350\257\246\350\247\243.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/4. Java-HttpResponse-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/4. HttpResponse-\350\257\246\350\247\243.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/4. Java-HttpResponse-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/4. HttpResponse-\350\257\246\350\247\243.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/5. Java-HttpSession-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/5. HttpSession-\350\257\246\350\247\243.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/5. Java-HttpSession-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/5. HttpSession-\350\257\246\350\247\243.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/6. Java-RequestDispatcher-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/6. RequestDispatcher-\350\257\246\350\247\243.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/6. Java-RequestDispatcher-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/6. RequestDispatcher-\350\257\246\350\247\243.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/7. Java-Servlet-Cookie-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/7. Servlet-Cookie-\350\257\246\350\247\243.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/7. Java-Servlet-Cookie-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/7. Servlet-Cookie-\350\257\246\350\247\243.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/8. Java-Servlet-Filter-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/8. Servlet-Filter-\350\257\246\350\247\243.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/8. Java-Servlet-Filter-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/8. Servlet-Filter-\350\257\246\350\247\243.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/9. Java-Servlet-GZip-Servlet-Filter-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/9. Servlet-GZip-Servlet-Filter-\350\257\246\350\247\243.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/5. Servlet\347\257\207/9. Java-Servlet-GZip-Servlet-Filter-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/9. Servlet-GZip-Servlet-Filter-\350\257\246\350\247\243.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/1. Java-NIO-\346\216\242\347\264\242.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/1. Java-NIO-\346\216\242\347\264\242.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/1. Java-NIO-\346\216\242\347\264\242.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/1. Java-NIO-\346\216\242\347\264\242.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/10. Java-NIO-ServerSocketChannel.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/10. Java-NIO-ServerSocketChannel.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/10. Java-NIO-ServerSocketChannel.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/10. Java-NIO-ServerSocketChannel.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/11. Java-NIO-Non-blocking-Server.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/11. Java-NIO-Non-blocking-Server.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/11. Java-NIO-Non-blocking-Server.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/11. Java-NIO-Non-blocking-Server.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/12. Java-NIO-DatagramChannel.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/12. Java-NIO-DatagramChannel.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/12. Java-NIO-DatagramChannel.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/12. Java-NIO-DatagramChannel.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/13. Java-NIO-Pipe(\347\256\241\351\201\223).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/13. Java-NIO-Pipe(\347\256\241\351\201\223).md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/13. Java-NIO-Pipe(\347\256\241\351\201\223).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/13. Java-NIO-Pipe(\347\256\241\351\201\223).md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/14. Java-NIO-NIO\345\222\214IO\347\232\204\345\214\272\345\210\253\345\222\214\351\200\202\347\224\250\345\234\272\346\231\257\345\210\206\346\236\220.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/14. Java-NIO-NIO\345\222\214IO\347\232\204\345\214\272\345\210\253\345\222\214\351\200\202\347\224\250\345\234\272\346\231\257\345\210\206\346\236\220.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/14. Java-NIO-NIO\345\222\214IO\347\232\204\345\214\272\345\210\253\345\222\214\351\200\202\347\224\250\345\234\272\346\231\257\345\210\206\346\236\220.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/14. Java-NIO-NIO\345\222\214IO\347\232\204\345\214\272\345\210\253\345\222\214\351\200\202\347\224\250\345\234\272\346\231\257\345\210\206\346\236\220.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/15. Java-NIO-Path\347\232\204\347\224\250\346\263\225.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/15. Java-NIO-Path\347\232\204\347\224\250\346\263\225.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/15. Java-NIO-Path\347\232\204\347\224\250\346\263\225.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/15. Java-NIO-Path\347\232\204\347\224\250\346\263\225.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/16. Java-NIO-Files.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/16. Java-NIO-Files.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/16. Java-NIO-Files.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/16. Java-NIO-Files.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/17. Java-NIO-AsynchronousFileChannel\345\274\202\346\255\245\346\226\207\344\273\266\351\200\232\351\201\223.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/17. Java-NIO-AsynchronousFileChannel\345\274\202\346\255\245\346\226\207\344\273\266\351\200\232\351\201\223.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/17. Java-NIO-AsynchronousFileChannel\345\274\202\346\255\245\346\226\207\344\273\266\351\200\232\351\201\223.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/17. Java-NIO-AsynchronousFileChannel\345\274\202\346\255\245\346\226\207\344\273\266\351\200\232\351\201\223.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/18. Java-NIO-\345\256\236\344\276\213.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/18. Java-NIO-\345\256\236\344\276\213.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/18. Java-NIO-\345\256\236\344\276\213.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/18. Java-NIO-\345\256\236\344\276\213.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/19. BIO\343\200\201NIO\343\200\201AIO\345\214\272\345\210\253.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/19. BIO\343\200\201NIO\343\200\201AIO\345\214\272\345\210\253.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/19. BIO\343\200\201NIO\343\200\201AIO\345\214\272\345\210\253.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/19. BIO\343\200\201NIO\343\200\201AIO\345\214\272\345\210\253.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/2. Java-NIO-\346\246\202\345\277\265.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/2. Java-NIO-\346\246\202\345\277\265.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/2. Java-NIO-\346\246\202\345\277\265.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/2. Java-NIO-\346\246\202\345\277\265.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/2. Java-NIO-\347\256\200\344\273\213.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/2. Java-NIO-\347\256\200\344\273\213.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/2. Java-NIO-\347\256\200\344\273\213.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/2. Java-NIO-\347\256\200\344\273\213.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/3. Java-NIO-Channel-\351\200\232\351\201\223.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/3. Java-NIO-Channel-\351\200\232\351\201\223.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/3. Java-NIO-Channel-\351\200\232\351\201\223.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/3. Java-NIO-Channel-\351\200\232\351\201\223.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/4. Java-NIO-Buffer.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/4. Java-NIO-Buffer.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/4. Java-NIO-Buffer.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/4. Java-NIO-Buffer.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/5. Java-NIO-Scatter---Gather.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/5. Java-NIO-Scatter---Gather.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/5. Java-NIO-Scatter---Gather.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/5. Java-NIO-Scatter---Gather.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/6. Java-NIO-Channel-\344\271\213\351\227\264\347\232\204\350\275\254\346\215\242(\344\274\240\350\276\223).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/6. Java-NIO-Channel-\344\271\213\351\227\264\347\232\204\350\275\254\346\215\242(\344\274\240\350\276\223).md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/6. Java-NIO-Channel-\344\271\213\351\227\264\347\232\204\350\275\254\346\215\242(\344\274\240\350\276\223).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/6. Java-NIO-Channel-\344\271\213\351\227\264\347\232\204\350\275\254\346\215\242(\344\274\240\350\276\223).md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/7. Java-NIO-Selector.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/7. Java-NIO-Selector.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/7. Java-NIO-Selector.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/7. Java-NIO-Selector.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/8. Java-NIO-FileChannel.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/8. Java-NIO-FileChannel.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/8. Java-NIO-FileChannel.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/8. Java-NIO-FileChannel.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/9. Java-NIO-SocketChannel.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/9. Java-NIO-SocketChannel.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java-NIO/9. Java-NIO-SocketChannel.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/9. Java-NIO-SocketChannel.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java8/java8-LocalDate\347\261\273.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java8/java8-LocalDate\347\261\273.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java8/java8-LocalDate\347\261\273.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java8/java8-LocalDate\347\261\273.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java8/java8-lambda.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java8/java8-lambda.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java8/java8-lambda.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java8/java8-lambda.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java8/java8-stream.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java8/java8-stream.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java8/java8-stream.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java8/java8-stream.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/1. Java-\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213-\345\210\235\350\257\206\347\257\207.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/1. \345\210\235\350\257\206\347\257\207.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/1. Java-\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213-\345\210\235\350\257\206\347\257\207.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/1. \345\210\235\350\257\206\347\257\207.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/2. Java-\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213-Lambda\350\241\250\350\276\276\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/2. Lambda\350\241\250\350\276\276\345\274\217.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/2. Java-\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213-Lambda\350\241\250\350\276\276\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/2. Lambda\350\241\250\350\276\276\345\274\217.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/3. Java-\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213-Stream\346\265\201.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/3. Stream\346\265\201.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/3. Java-\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213-Stream\346\265\201.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/3. Stream\346\265\201.md" diff --git "a/2. Java\345\237\272\347\241\200/2.3 \351\235\242\350\257\225\347\257\207/Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223\344\271\213Java\345\237\272\347\241\200(\344\270\200).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\237\272\347\241\200\346\200\273\347\273\223(\344\270\200).md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.3 \351\235\242\350\257\225\347\257\207/Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223\344\271\213Java\345\237\272\347\241\200(\344\270\200).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\237\272\347\241\200\346\200\273\347\273\223(\344\270\200).md" diff --git "a/2. Java\345\237\272\347\241\200/2.3 \351\235\242\350\257\225\347\257\207/Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223\344\271\213Java\345\237\272\347\241\200(\344\270\211).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\237\272\347\241\200\346\200\273\347\273\223(\344\270\211).md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.3 \351\235\242\350\257\225\347\257\207/Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223\344\271\213Java\345\237\272\347\241\200(\344\270\211).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\237\272\347\241\200\346\200\273\347\273\223(\344\270\211).md" diff --git "a/2. Java\345\237\272\347\241\200/2.3 \351\235\242\350\257\225\347\257\207/Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223\344\271\213Java\345\237\272\347\241\200(\344\272\214).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\237\272\347\241\200\346\200\273\347\273\223(\344\272\214).md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.3 \351\235\242\350\257\225\347\257\207/Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223\344\271\213Java\345\237\272\347\241\200(\344\272\214).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\237\272\347\241\200\346\200\273\347\273\223(\344\272\214).md" diff --git "a/2. Java\345\237\272\347\241\200/2.3 \351\235\242\350\257\225\347\257\207/\347\261\273\345\212\240\350\275\275\345\231\250\351\235\242\350\257\225(\344\270\200).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\347\261\273\345\212\240\350\275\275\345\231\250/\347\261\273\345\212\240\350\275\275\345\231\250.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.3 \351\235\242\350\257\225\347\257\207/\347\261\273\345\212\240\350\275\275\345\231\250\351\235\242\350\257\225(\344\270\200).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\347\261\273\345\212\240\350\275\275\345\231\250/\347\261\273\345\212\240\350\275\275\345\231\250.md" diff --git "a/2. Java\345\237\272\347\241\200/2.3 \351\235\242\350\257\225\347\257\207/\347\275\221\347\273\234(\344\270\200).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/\347\275\221\347\273\234(\344\270\200).md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.3 \351\235\242\350\257\225\347\257\207/\347\275\221\347\273\234(\344\270\200).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/\347\275\221\347\273\234(\344\270\200).md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/1. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\345\267\245\345\216\202\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/1. \345\267\245\345\216\202\346\250\241\345\274\217.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/1. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\345\267\245\345\216\202\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/1. \345\267\245\345\216\202\346\250\241\345\274\217.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/10. \351\200\202\351\205\215\345\231\250\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/10. \351\200\202\351\205\215\345\231\250\346\250\241\345\274\217.md" new file mode 100644 index 0000000..e69de29 diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/2. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/2. \346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/2. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/2. \346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/3. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\345\215\225\344\276\213\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/3. \345\215\225\344\276\213\346\250\241\345\274\217.md" similarity index 92% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/3. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\345\215\225\344\276\213\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/3. \345\215\225\344\276\213\346\250\241\345\274\217.md" index 56678fe..69e0e4e 100644 --- "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/3. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\345\215\225\344\276\213\346\250\241\345\274\217.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/3. \345\215\225\344\276\213\346\250\241\345\274\217.md" @@ -42,8 +42,8 @@ public class Singleton { ``` 可以在多线程环境下使用,但是效率太低。 -**优点:**一个对象初始化一次,节省内存。 -**缺点:**必须用synchronized来维持单例,没效率。 +**优点**:一个对象初始化一次,节省内存。 +**缺点**:必须用synchronized来维持单例,没效率。 ### 实现方式三:饿汉式(线程安全) ``` @@ -65,7 +65,7 @@ public class Singleton { ``` public class Singleton { - private static Singleton instance; + private static volatile Singleton instance; public static Singleton getInstance(){ if (instance == null){ @@ -81,6 +81,7 @@ public class Singleton { ``` 采用双锁机制,安全且在多线程情况下能保持高性能。详细了解请点击:[Java并发编程 -- 单例模式线程安全问题](https://www.jianshu.com/p/3707bc0fc6f0) +注意这个volatile,一方面保证了instance的可见性、一方面保证了jvm不对其编译重排序优化,避免NullPointException。 ### 实现方式五:登记式/静态内部类(线程安全) ``` diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/4. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\345\273\272\351\200\240\350\200\205\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/4. \345\273\272\351\200\240\350\200\205\346\250\241\345\274\217.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/4. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\345\273\272\351\200\240\350\200\205\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/4. \345\273\272\351\200\240\350\200\205\346\250\241\345\274\217.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/5. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\345\216\237\345\236\213\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/5. \345\216\237\345\236\213\346\250\241\345\274\217.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/5. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\345\216\237\345\236\213\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/5. \345\216\237\345\236\213\346\250\241\345\274\217.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/6. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\346\250\241\346\235\277\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/6. \346\250\241\346\235\277\346\250\241\345\274\217.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/6. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\346\250\241\346\235\277\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/6. \346\250\241\346\235\277\346\250\241\345\274\217.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/7. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\347\255\226\347\225\245\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/7. \347\255\226\347\225\245\346\250\241\345\274\217.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/7. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\347\255\226\347\225\245\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/7. \347\255\226\347\225\245\346\250\241\345\274\217.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/8. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\345\247\224\346\264\276\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/8. \345\247\224\346\264\276\346\250\241\345\274\217.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/8. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-\345\247\224\346\264\276\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/8. \345\247\224\346\264\276\346\250\241\345\274\217.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/9. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-Java\344\270\244\347\247\215\344\273\243\347\220\206\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/9. \344\273\243\347\220\206\346\250\241\345\274\217.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/\350\256\276\350\256\241\346\250\241\345\274\217/9. \350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240-Java\344\270\244\347\247\215\344\273\243\347\220\206\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/9. \344\273\243\347\220\206\346\250\241\345\274\217.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/\345\205\255\345\244\247\350\256\276\350\256\241\345\216\237\345\210\231.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/\345\205\255\345\244\247\350\256\276\350\256\241\345\216\237\345\210\231.md" new file mode 100644 index 0000000..14b08ba --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/\345\205\255\345\244\247\350\256\276\350\256\241\345\216\237\345\210\231.md" @@ -0,0 +1,18 @@ +1. 单一职责 + + 类的话,一个类尽量做同一类型的事情。对于方法的话,一个方法尽量做一件事情。降低维护成本。 +2. 里氏替换 + + 子类对象可以替换其父类对象,而程序执行效果不变。继承体系 +3. 开闭原则 + + 对外扩展开发,对内修改关闭。抽象类 +4. 依赖倒置 + + 高层模块不能依赖低层模块,二者都应该依赖抽象。接口编程 +5. 接口隔离 + + 多个特定的客户端接口要好于一个通用性的总接口。客户端不应该依赖它不需要实现的接口。应尽量细化接口,接口中的方法应尽量少。需要注意的是接口的力度也不能太小,如果过小,则会造成接口数量过多,使设计复杂化。 +6. 迪米特 + + 一个对象应该对尽可能少的对象有接触,也就是只接触那些真正需要接触的对象。迪米特法则也叫做最少知道原则,一个类应该只和它的成员变量,方法的输入,返回参数中的类作交流,而不应该引入其他的类(间接交流)。可以良好地降低类与类之间的耦合。 \ No newline at end of file diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/4. JVM-\347\250\213\345\272\217\350\256\241\346\225\260\345\231\250.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/4. JVM-\347\250\213\345\272\217\350\256\241\346\225\260\345\231\250.md" deleted file mode 100644 index aef955e..0000000 --- "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/1. JVM\347\257\207/4. JVM-\347\250\213\345\272\217\350\256\241\346\225\260\345\231\250.md" +++ /dev/null @@ -1,33 +0,0 @@ - - -######在网上挑选了这么几段话,精简易懂 - ->程序计数器(program counter register)只占用了一块比较小的内存空间,至于小到什么程度呢,这样说吧,有时可以忽略不计的。 - - ->可以看作是当前线程所执行的字节码文件(class)的行号指示器。在虚拟机的世界中,字节码解释器就是通过改变计数器的值来选取下一条执行的字节码指令,分支、循环、跳转、异常处理、线程恢复都需要这玩意来实现的。 - -上面提到线程恢复,那下面介绍下其详细流程: ->java虚拟机多线程是通过线程间轮流切换来分配给处理器执行时间;在确定时间节点,一个处理器(一核)只会执行一个线程的指令;为保证 线程切换 回来后能恢复到原执行位置,各个线程间计数器互相不影响,独立存储(称之为 线程私有 的内存); - ->如果执行的是java方法,那么记录的是正在执行的虚拟机字节码指令的地址的地址,如果是native方法,计数器的值为空(undefined)。 - -最后,动动自己的大脑,想下面的问题: ->这个内存区域是唯一一个在java虚拟界规范中没有规定任何OutOfMemoryError的情况的区域。至于为什么没有这个异常呢,要是一个计数的功能在出这个异常,那么我也是醉了。 - - -####介绍下我的所有文集: -###### 流行框架 -[SpringCloud](https://www.jianshu.com/nb/18726057) -[springboot](https://www.jianshu.com/nb/19053594) -[nginx](https://www.jianshu.com/nb/18436827) -[redis](https://www.jianshu.com/nb/21461220) - -######底层实现原理: -[Java NIO教程](https://www.jianshu.com/nb/21635138) -[Java reflection 反射详解](https://www.jianshu.com/nb/21989596) -[Java并发学习笔录](https://www.jianshu.com/nb/22549959) -[Java Servlet教程](https://www.jianshu.com/nb/22065472) -[jdbc组件详解](https://www.jianshu.com/nb/22774157) -[Java NIO教程](https://www.jianshu.com/nb/21635138) -[Java语言/版本 研究](https://www.jianshu.com/nb/19137666) diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/2. Java\345\271\266\345\217\221\347\274\226\347\250\213-\344\274\230\345\212\277\345\222\214\351\243\216\351\231\251.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/2. Java\345\271\266\345\217\221\347\274\226\347\250\213-\344\274\230\345\212\277\345\222\214\351\243\216\351\231\251.md" deleted file mode 100644 index 88e20c2..0000000 --- "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200\347\257\207/2. \345\271\266\345\217\221\347\274\226\347\250\213\347\257\207/2. Java\345\271\266\345\217\221\347\274\226\347\250\213-\344\274\230\345\212\277\345\222\214\351\243\216\351\231\251.md" +++ /dev/null @@ -1,40 +0,0 @@ -##### 一、优势 -并发编程之所以让人迷惑是因为有不止一种问题的解决需要使用并发,也有不止一种方法去实现并发,而且他们之间也没有清晰的映射。 - -使用并发编程来解决的问题可以划分为两类,即“speed”和“designmanageability”。 - - - - 1. 速度优势: -* 多处理器:多处理器上面并发变成无疑会让程序运行很快。 -* 单处理器:如果是单处理器的机器,那么并发编程可能相对于顺序编程没有什么变化。但是,如果其中某一个任务也许会发生阻塞的话,那么即使是单处理器,使用并发编程也会带来很大的好处,这样,某个任务阻塞的时候,其他任务也可以继续运行了。 -* 反应灵敏的用户界面:在单处理器上面性能提升最典型的列子就是“事件驱动的编程”,比如创建一个有反应的用户界面,其中有个按钮,如果我们不使用并发编程,那么我们需要在我们编写的每一个代码片段中都要有对用户输入的检测,如果我们使用并发编程,我们只需要重新开启一个线程去监听用户的输入即可。 -并发的实现:实现并发的最直接的方式是在操作系统级别,使用进程,进程一种自包含的程序,使用自己的地址空间,操作系统会让进程之间相互隔离,所以进程编程相对容易一些,不需要考虑共享资源的同步等问题。但是在Java中的并发编程,由于线程之间共享相同的memory或者IO等资源,所以Java多线程编程中需要考虑共享资源的同步问题。 -进程和Java线程之间的选择:进程的确是一种实现并发的方式,butunfortunately there are generally quantity and overhead limitations toprocesses that prevent their applicability across the concurrency spectrum. - - -2. 设计上的优势: -* 一般来说,线程使得你能够创建更加松耦合的设计。 -* 单处理器:尽管单处理器上面的并发编程在同一时刻处理器仍然只能做一件事情,但是带来一个组织上面的重要优势:就是你的设计(design)会极大地简化。比如仿真。 -* 仿真举例:如果没有并发,仿真将变得非常困难。 一般来说仿真涉及到多个交互元素,其中每一个都有“自己的想法”,尽管从程序员的角度来看每一个仿真元素都是被同一个处理器所驱动,但是设计上来看,每一个仿真元素都假装有自己的处理器以及运行独立的任务。 - -##### 二、风险 -1. 安全性问题 - -主要是多个线程共享数据时可能会产生于期望不相符的结果 - - - -2. 活跃性问题(liveness) - -当某个操作无法继续进行下去时,就会发生活跃性问题。比如死锁、饥饿、活锁等问题。(死锁、饥饿、活锁可自行百度) - - - -3. 性能问题 - -a. 线程过多时会使得CPU频繁切换,花在调度上时间太多。 - -b. 多线程环境必须使用同步机制,导致很多编译器想做的优化被抑制。 - -c. 线程过多还会消耗过多内存。 diff --git "a/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\347\257\207/5. \344\270\272\344\273\200\344\271\210\350\246\201\347\273\247\346\211\277Serializable\347\261\273\357\274\237.md" "b/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\347\257\207/5. \344\270\272\344\273\200\344\271\210\350\246\201\347\273\247\346\211\277Serializable\347\261\273\357\274\237.md" deleted file mode 100644 index fb31323..0000000 --- "a/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\347\257\207/5. \344\270\272\344\273\200\344\271\210\350\246\201\347\273\247\346\211\277Serializable\347\261\273\357\274\237.md" +++ /dev/null @@ -1,47 +0,0 @@ -####为什么要实现Serializable? - -最重要的两个原因是: -  1、将对象的状态保存在存储媒体中以便可以在以后重新创建出完全相同的副本; -  2、按值将对象从一个应用程序域发送至另一个应用程序域。 -        通俗的说:在分布式应用中,你就得实现序列化,如果你不需要分布式应用,那就没那个必要实现序列化。 - -拓展: -`Serializable`是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。 - ->>什么情况下需要序列化 ->* a)当你**想把的内存中的对象写入到硬盘**的时候; ->* b)当你想用套接字在**网络上传送对象**的时候; ->* c)当你想通过RMI传输对象的时候;再稍微解释一下: -> * a)比如说你的内存不够用了,那计算机就要将内存里面*的一部分对象暂时的保存到硬盘中,等到要用的时候再读入到内存中*,硬盘的那部分存储空间就是所谓的[虚拟内存](https://www.baidu.com/s?wd=%E8%99%9A%E6%8B%9F%E5%86%85%E5%AD%98&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1YvmhDYmvndrHR4nj-BnWD30ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6KdThsqpZwYTjCEQLGCpyw9Uz4Bmy-bIi4WUvYETgN-TLwGUv3EPHmknjfkn1cd)。在比如过你要将某个特定的对象保存到文件中,我隔几天在把它拿出来用,那么这时候就要实现Serializable接口; - > -* b)在进行java的**Socket**编程的时候,你有时候可能要传输某一类的对象,那么也就要实现Serializable接口;最常见的你传输一个字符串,它是JDK里面的类,也实现了Serializable接口,所以可以在网络上传输。 -> ** c)如果要通过远程的方法调用(RMI)去调用一个远程对象的方法,如在计算机A中调用另一台计算机B的对象的方法,那么你需要通过JNDI服务获取计算机B目标对象的引用,将对象从B传送到A,就需要实现序列化接口。 -####底层实现原理 - ``` -Foo myFoo = new Foo(); -myFoo .setWidth(37); -myFoo.setHeight(70); - ``` - 当 通过下面的代码序列化之后,MyFoo对象中的width和Height实例变量的值(37,70)都被保存到foo.ser文件中,这样以后又可以把它 从文件中读出来,重新在堆中创建原来的对象。当然保存时候不仅仅是保存对象的实例变量的值,JVM还要保存一些小量信息,比如类的类型等以便恢复原来的对象。 -``` -FileOutputStream fs = new FileOutputStream("foo.ser"); -ObjectOutputStream os = new ObjectOutputStream(fs); -os.writeObject(myFoo); -``` -######大概步骤 -1. Make a FileOutputStream -`FileOutputStream fs = new FileOutputStream("foo.ser"); ` -2. Make a ObjectOutputStream -`ObjectOutputStream os = new ObjectOutputStream(fs); ` -3. write the object -`os.writeObject(myObject1); -os.writeObject(myObject2); -os.writeObject(myObject3); ` -4. close the ObjectOutputStream -`os.close();` - -> serialVersionUID适用于Java的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。 - -* serialVersionUID有两种显示的生成方式: - * 一是默认的1L,比如:private static final long serialVersionUID = 1L; - * 二是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如: -private static final long serialVersionUID = xxxxL; diff --git "a/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\347\257\207/1. ArrayList\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/1. ArrayList\346\272\220\347\240\201\345\210\206\346\236\220.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\347\257\207/1. ArrayList\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/1. ArrayList\346\272\220\347\240\201\345\210\206\346\236\220.md" diff --git "a/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\347\257\207/2. HashMap\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/2. HashMap\346\272\220\347\240\201\345\210\206\346\236\220.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\347\257\207/2. HashMap\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/2. HashMap\346\272\220\347\240\201\345\210\206\346\236\220.md" diff --git "a/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\347\257\207/3. LinkedList\346\272\220\347\240\201\345\210\206\346\236\220-md.md" "b/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/3. LinkedList\346\272\220\347\240\201\345\210\206\346\236\220-md.md" similarity index 100% rename from "2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\347\257\207/3. LinkedList\346\272\220\347\240\201\345\210\206\346\236\220-md.md" rename to "2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/3. LinkedList\346\272\220\347\240\201\345\210\206\346\236\220-md.md" diff --git "a/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/4. ThreadPoolExector\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/4. ThreadPoolExector\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..f16c39e --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/4. ThreadPoolExector\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,434 @@ +>线程池源码也是面试经常被提问到的点,我会将全局源码做一分析,然后告诉你面试考啥,怎么答。 + +### 为什么要用线程池? +>简洁的答两点就行。 + +1. 降低系统资源消耗。 +2. 提高线程可控性。 + +### 如何创建使用线程池? + +JDK8提供了五种创建线程池的方法: + +1. 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 +``` +public static ExecutorService newFixedThreadPool(int nThreads) { + return new ThreadPoolExecutor(nThreads, nThreads, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue()); +} +``` +2. (JDK8新增)会根据所需的并发数来动态创建和关闭线程。能够合理的使用CPU进行对任务进行并发操作,所以适合使用在很耗时的任务。 +>注意返回的是ForkJoinPool对象。 +``` +public static ExecutorService newWorkStealingPool(int parallelism) { + return new ForkJoinPool + (parallelism, + ForkJoinPool.defaultForkJoinWorkerThreadFactory, + null, true); +} + +什么是ForkJoinPool: + +public ForkJoinPool(int parallelism, + ForkJoinWorkerThreadFactory factory, + UncaughtExceptionHandler handler, + boolean asyncMode) { + this(checkParallelism(parallelism), + checkFactory(factory), + handler, + asyncMode ? FIFO_QUEUE : LIFO_QUEUE, + "ForkJoinPool-" + nextPoolId() + "-worker-"); + checkPermission(); + } +使用一个无限队列来保存需要执行的任务,可以传入线程的数量, +不传入,则默认使用当前计算机中可用的cpu数量, +使用分治法来解决问题,使用fork()和join()来进行调用 +``` +3. 创建一个可缓存的线程池,可灵活回收空闲线程,若无可回收,则新建线程。 +``` +public static ExecutorService newCachedThreadPool() { + return new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue()); +} +``` +4. 创建一个单线程的线程池。 +``` +public static ExecutorService newSingleThreadExecutor() { + return new FinalizableDelegatedExecutorService + (new ThreadPoolExecutor(1, 1, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue())); +} +``` +5. 创建一个定长线程池,支持定时及周期性任务执行。 +``` +public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { + return new ScheduledThreadPoolExecutor(corePoolSize); +} +``` + +### 上层源码结构分析 + +>Executor结构: +![](https://upload-images.jianshu.io/upload_images/5786888-3c7c75e9b406f155.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +#### Executor +>一个运行新任务的简单接口 +``` +public interface Executor { + + void execute(Runnable command); +} +``` + +#### ExecutorService +>扩展了Executor接口。添加了一些用来管理执行器生命周期和任务生命周期的方法 +![](https://upload-images.jianshu.io/upload_images/5786888-3d8ddd3eeb25b148.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +#### AbstractExecutorService +>对ExecutorService接口的抽象类实现。不是我们分析的重点。 + + +#### ThreadPoolExecutor +>Java线程池的核心实现。 + + +### ThreadPoolExecutor源码分析 + +#### 属性解释 + +``` +// AtomicInteger是原子类 ctlOf()返回值为RUNNING; +private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); +// 高3位表示线程状态 +private static final int COUNT_BITS = Integer.SIZE - 3; +// 低29位表示workerCount容量 +private static final int CAPACITY = (1 << COUNT_BITS) - 1; + +// runState is stored in the high-order bits +// 能接收任务且能处理阻塞队列中的任务 +private static final int RUNNING = -1 << COUNT_BITS; +// 不能接收新任务,但可以处理队列中的任务。 +private static final int SHUTDOWN = 0 << COUNT_BITS; +// 不接收新任务,不处理队列任务。 +private static final int STOP = 1 << COUNT_BITS; +// 所有任务都终止 +private static final int TIDYING = 2 << COUNT_BITS; +// 什么都不做 +private static final int TERMINATED = 3 << COUNT_BITS; + +// 存放任务的阻塞队列 +private final BlockingQueue workQueue; + +``` +值的注意的是状态值越大线程越不活跃。 +##### 线程池状态的转换模型: + +![](https://upload-images.jianshu.io/upload_images/5786888-97fe7aee4be7bb4c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +#### 构造器 +``` +public ThreadPoolExecutor(int corePoolSize,//线程池初始启动时线程的数量 + int maximumPoolSize,//最大线程数量 + long keepAliveTime,//空闲线程多久关闭? + TimeUnit unit,// 计时单位 + BlockingQueue workQueue,//放任务的阻塞队列 + ThreadFactory threadFactory,//线程工厂 + RejectedExecutionHandler handler// 拒绝策略) { + if (corePoolSize < 0 || + maximumPoolSize <= 0 || + maximumPoolSize < corePoolSize || + keepAliveTime < 0) + throw new IllegalArgumentException(); + if (workQueue == null || threadFactory == null || handler == null) + throw new NullPointerException(); + this.acc = System.getSecurityManager() == null ? + null : + AccessController.getContext(); + this.corePoolSize = corePoolSize; + this.maximumPoolSize = maximumPoolSize; + this.workQueue = workQueue; + this.keepAliveTime = unit.toNanos(keepAliveTime); + this.threadFactory = threadFactory; + this.handler = handler; +} +``` + +在向线程池提交任务时,会通过两个方法:execute和submit。 + +**本文着重讲解execute方法**。submit方法放在下次和Future、Callable一起分析。 +#### execute方法: +``` +public void execute(Runnable command) { + if (command == null) + throw new NullPointerException(); + // clt记录着runState和workerCount + int c = ctl.get(); + //workerCountOf方法取出低29位的值,表示当前活动的线程数 + //然后拿线程数和 核心线程数做比较 + if (workerCountOf(c) < corePoolSize) { + // 如果活动线程数<核心线程数 + // 添加到 + //addWorker中的第二个参数表示限制添加线程的数量是根据corePoolSize来判断还是maximumPoolSize来判断 + if (addWorker(command, true)) + // 如果成功则返回 + return; + // 如果失败则重新获取 runState和 workerCount + c = ctl.get(); + } + // 如果当前线程池是运行状态并且任务添加到队列成功 + if (isRunning(c) && workQueue.offer(command)) { + // 重新获取 runState和 workerCount + int recheck = ctl.get(); + // 如果不是运行状态并且 + if (! isRunning(recheck) && remove(command)) + reject(command); + else if (workerCountOf(recheck) == 0) + //第一个参数为null,表示在线程池中创建一个线程,但不去启动 + // 第二个参数为false,将线程池的有限线程数量的上限设置为maximumPoolSize + addWorker(null, false); + } + //再次调用addWorker方法,但第二个参数传入为false,将线程池的有限线程数量的上限设置为maximumPoolSize + else if (!addWorker(command, false)) + //如果失败则拒绝该任务 + reject(command); +} +``` +总结一下它的工作流程: + +1. 当`workerCount < corePoolSize`,创建线程执行任务。 + +2. 当`workerCount >= corePoolSize`&&阻塞队列`workQueue`未满,把新的任务放入阻塞队列。 + +3. 当`workQueue`已满,并且`workerCount >= corePoolSize`,并且`workerCount < maximumPoolSize`,创建线程执行任务。 + +4. 当workQueue已满,`workerCount >= maximumPoolSize`,采取拒绝策略,默认拒绝策略是直接抛异常。 + + +![](https://upload-images.jianshu.io/upload_images/5786888-654f22886b8240b3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +通过上面的execute方法可以看到,最主要的逻辑还是在addWorker方法中实现的,那我们就看下这个方法: + +#### addWorker方法 +>主要工作是在线程池中创建一个新的线程并执行 + +参数定义: +* `firstTask` the task the new thread should run first (or null if none). (指定新增线程执行的第一个任务或者不执行任务) +* `core` if true use corePoolSize as bound, else maximumPoolSize.(core如果为true则使用corePoolSize绑定,否则为maximumPoolSize。 (此处使用布尔指示符而不是值,以确保在检查其他状态后读取新值)。) +``` +private boolean addWorker(Runnable firstTask, boolean core) { + retry: + for (;;) { + + int c = ctl.get(); + // 获取运行状态 + int rs = runStateOf(c); + + // Check if queue empty only if necessary. + // 如果状态值 >= SHUTDOWN (不接新任务&不处理队列任务) + // 并且 如果 !(rs为SHUTDOWN 且 firsTask为空 且 阻塞队列不为空) + if (rs >= SHUTDOWN && + ! (rs == SHUTDOWN && + firstTask == null && + ! workQueue.isEmpty())) + // 返回false + return false; + + for (;;) { + //获取线程数wc + int wc = workerCountOf(c); + // 如果wc大与容量 || core如果为true表示根据corePoolSize来比较,否则为maximumPoolSize + if (wc >= CAPACITY || + wc >= (core ? corePoolSize : maximumPoolSize)) + return false; + // 增加workerCount(原子操作) + if (compareAndIncrementWorkerCount(c)) + // 如果增加成功,则跳出 + break retry; + // wc增加失败,则再次获取runState + c = ctl.get(); // Re-read ctl + // 如果当前的运行状态不等于rs,说明状态已被改变,返回重新执行 + if (runStateOf(c) != rs) + continue retry; + // else CAS failed due to workerCount change; retry inner loop + } + } + + boolean workerStarted = false; + boolean workerAdded = false; + Worker w = null; + try { + // 根据firstTask来创建Worker对象 + w = new Worker(firstTask); + // 根据worker创建一个线程 + final Thread t = w.thread; + if (t != null) { + // new一个锁 + final ReentrantLock mainLock = this.mainLock; + // 加锁 + mainLock.lock(); + try { + // Recheck while holding lock. + // Back out on ThreadFactory failure or if + // shut down before lock acquired. + // 获取runState + int rs = runStateOf(ctl.get()); + // 如果rs小于SHUTDOWN(处于运行)或者(rs=SHUTDOWN && firstTask == null) + // firstTask == null证明只新建线程而不执行任务 + if (rs < SHUTDOWN || + (rs == SHUTDOWN && firstTask == null)) { + // 如果t活着就抛异常 + if (t.isAlive()) // precheck that t is startable + throw new IllegalThreadStateException(); + // 否则加入worker(HashSet) + //workers包含池中的所有工作线程。仅在持有mainLock时访问。 + workers.add(w); + // 获取工作线程数量 + int s = workers.size(); + //largestPoolSize记录着线程池中出现过的最大线程数量 + if (s > largestPoolSize) + // 如果 s比它还要大,则将s赋值给它 + largestPoolSize = s; + // worker的添加工作状态改为true + workerAdded = true; + } + } finally { + mainLock.unlock(); + } + // 如果worker的添加工作完成 + if (workerAdded) { + // 启动线程 + t.start(); + // 修改线程启动状态 + workerStarted = true; + } + } + } finally { + if (! workerStarted) + addWorkerFailed(w); + } + // 返回线启动状态 + return workerStarted; + +``` +###### 为什么需要持有mainLock? +因为workers是HashSet类型的,不能保证线程安全。 + + + +那`w = new Worker(firstTask);`如何理解呢 + +#### Worker.java +``` +private final class Worker + extends AbstractQueuedSynchronizer + implements Runnable +``` +可以看到它继承了AQS并发框架还实现了Runnable。证明它还是一个线程任务类。那我们调用t.start()事实上就是调用了该类重写的run方法. + +##### Worker为什么不使用ReentrantLock来实现呢? +tryAcquire方法它是不允许重入的,而ReentrantLock是允许重入的。对于线程来说,如果线程正在执行是不允许其它锁重入进来的。 + +线程只需要两个状态,一个是独占锁,表明正在执行任务;一个是不加锁,表明是空闲状态。 +``` +public void run() { + runWorker(this); +} +``` +run方法又调用了runWorker方法: +``` +final void runWorker(Worker w) { + // 拿到当前线程 + Thread wt = Thread.currentThread(); + // 拿到当前任务 + Runnable task = w.firstTask; + // 将Worker.firstTask置空 并且释放锁 + w.firstTask = null; + w.unlock(); // allow interrupts + boolean completedAbruptly = true; + try { + // 如果task或者getTask不为空,则一直循环 + while (task != null || (task = getTask()) != null) { + // 加锁 + w.lock(); + // If pool is stopping, ensure thread is interrupted; + // if not, ensure thread is not interrupted. This + // requires a recheck in second case to deal with + // shutdownNow race while clearing interrupt + // return ctl.get() >= stop + // 如果线程池状态>=STOP 或者 (线程中断且线程池状态>=STOP)且当前线程没有中断 + // 其实就是保证两点: + // 1. 线程池没有停止 + // 2. 保证线程没有中断 + if ((runStateAtLeast(ctl.get(), STOP) || + (Thread.interrupted() && + runStateAtLeast(ctl.get(), STOP))) && + !wt.isInterrupted()) + // 中断当前线程 + wt.interrupt(); + try { + // 空方法 + beforeExecute(wt, task); + Throwable thrown = null; + try { + // 执行run方法(Runable对象) + task.run(); + } catch (RuntimeException x) { + thrown = x; throw x; + } catch (Error x) { + thrown = x; throw x; + } catch (Throwable x) { + thrown = x; throw new Error(x); + } finally { + afterExecute(task, thrown); + } + } finally { + // 执行完后, 将task置空, 完成任务++, 释放锁 + task = null; + w.completedTasks++; + w.unlock(); + } + } + completedAbruptly = false; + } finally { + // 退出工作 + processWorkerExit(w, completedAbruptly); + } + +``` +总结一下runWorker方法的执行过程: + +1. while循环中,不断地通过getTask()方法从workerQueue中获取任务 +2. 如果线程池正在停止,则中断线程。否则调用3. +3. 调用task.run()执行任务; +4. 如果task为null则跳出循环,执行processWorkerExit()方法,销毁线程`workers.remove(w);` + +这个流程图非常经典: +![](https://upload-images.jianshu.io/upload_images/5786888-4c08a0bb6a78abe1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +除此之外,`ThreadPoolExector`还提供了`tryAcquire`、`tryRelease`、`shutdown`、`shutdownNow`、`tryTerminate`、等涉及的一系列线程状态更改的方法有兴趣可以自己研究。大体思路是一样的,这里不做介绍。 + +##### Worker为什么不使用ReentrantLock来实现呢? +tryAcquire方法它是不允许重入的,而ReentrantLock是允许重入的。对于线程来说,如果线程正在执行是不允许其它锁重入进来的。 + +线程只需要两个状态,一个是独占锁,表明正在执行任务;一个是不加锁,表明是空闲状态。 + +##### 在runWorker方法中,为什么要在执行任务的时候对每个工作线程都加锁呢? +shutdown方法与getTask方法存在竞态条件.(这里不做深入,建议自己深入研究,对它比较熟悉的面试官一般会问) + + +### 高频考点 +1. 创建线程池的五个方法。 +2. 线程池的五个状态 +3. execute执行过程。 +4. runWorker执行过程。(把两个流程图记下,理解后说个大该就行。) +5. 比较深入的问题就是我在文中插入的问题。 +6. ...期望大家能在评论区补充。 + + +声明:图片来源于网络,若有侵犯请联系博主。 \ No newline at end of file diff --git "a/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/Dubbo-RpcException\346\212\245\351\224\231\350\247\243\345\206\263.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/Dubbo-RpcException\346\212\245\351\224\231\350\247\243\345\206\263.md" deleted file mode 100644 index 0550688..0000000 --- "a/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/Dubbo-RpcException\346\212\245\351\224\231\350\247\243\345\206\263.md" +++ /dev/null @@ -1,4 +0,0 @@ - -这个报错是因为用到的一些类没有序列化造成的,这个错误是由于我service用到了一些工具类,工具类没有实例化造成的。 - -只要是需要注册到dbbo上的每一个对象都需要做序列化。 diff --git "a/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis#\345\222\214$\345\214\272\345\210\253.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis#\345\222\214$\345\214\272\345\210\253.md" new file mode 100644 index 0000000..d588235 --- /dev/null +++ "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis#\345\222\214$\345\214\272\345\210\253.md" @@ -0,0 +1,4 @@ +* `#{}`: 根据参数的类型进行处理,比如传入String类型,则会为参数加上双引号。#{} 传参在进行SQL预编译时,会把参数部分用一个占位符 ? 代替,这样可以防止 SQL注入。 +* `${}` : 将参数取出不做任何处理,直接放入语句中,就是简单的字符串替换,并且该参数会参加SQL的预编译,需要手动过滤参数防止 SQL注入。 +- 因此 mybatis 中优先使用 #{};当需要动态传入 表名或列名时,再考虑使用 ${} 。 +- ``其中 ${} 比较特殊, 他的应用场景是 需要动态传入 表名或列名时使用。 \ No newline at end of file diff --git "a/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis\347\274\223\345\255\230.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis\347\274\223\345\255\230.md" new file mode 100644 index 0000000..f340acc --- /dev/null +++ "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis\347\274\223\345\255\230.md" @@ -0,0 +1,25 @@ +### Mybatis缓存 + +> mybatis支持一级和二级缓存 + +##### 一级缓存 + +一级缓存是基于SqlSession建立,当用户发起查询时,MyBatis根据当前执行的语句生成MappedStatement,在Local Cache进行查询(SqlSession接口默认实现类中有Executor接口实现类对象,该对象中存放了缓存,当有UDI操作时会清空缓存)。配置:`` + +1. MyBatis一级缓存的生命周期和SqlSession一致。 +2. MyBatis一级缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺。 +3. MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement。 + +##### 二级缓存 + +如果多个SqlSession之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询。 + +二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个SqlSession共享,是一个全局的变量。 + +当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。 + +1. MyBatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。 +2. MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。 +3. 在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis,Memcached等分布式缓存可能成本更低,安全性也更高。 + +个人建议MyBatis缓存特性在生产环境中进行关闭,单纯作为一个ORM框架使用可能更为合适。 \ No newline at end of file diff --git "a/4. \345\210\206\345\270\203\345\274\217\344\270\223\351\242\230/4.1 \345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\216\237\347\220\206\347\256\200\344\273\213" "b/4. \345\210\206\345\270\203\345\274\217\344\270\223\351\242\230/4.1 \345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\216\237\347\220\206\347\256\200\344\273\213" new file mode 100644 index 0000000..44d2e34 --- /dev/null +++ "b/4. \345\210\206\345\270\203\345\274\217\344\270\223\351\242\230/4.1 \345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\216\237\347\220\206\347\256\200\344\273\213" @@ -0,0 +1,293 @@ +# 1. 单数据源事务实现 + +工作上,对于只有一个数据源的操作中,我们仅仅用@Transaction 注解即可实现多个操作的事务控制。这个注解是spring提供的,与此同时,spring提供了多种事务管理器,有相应的依赖就会自动完成配置,我们使用的时候,只需要将我们想要的事务实现对象注入到 PlatformTransactionManager 里,就可以实现多种事务控制,也可以自己遵循spring的事务管理接口规范,自己写一个事务实现并注入: + +![image.png](https://upload-images.jianshu.io/upload_images/5786888-d4c40b7d02d3670e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +spring是如何自动识别并装配事务呢。以jdbc事务为例: +#### 扩展:jdbc事务自动配置 +``` +@Configuration +@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class }) +@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE) +@EnableConfigurationProperties(DataSourceProperties.class) +public class DataSourceTransactionManagerAutoConfiguration { + + @Configuration + @ConditionalOnSingleCandidate(DataSource.class) + static class DataSourceTransactionManagerConfiguration { + + private final DataSource dataSource; + + private final TransactionManagerCustomizers transactionManagerCustomizers; + + DataSourceTransactionManagerConfiguration(DataSource dataSource, + ObjectProvider transactionManagerCustomizers) { + this.dataSource = dataSource; + this.transactionManagerCustomizers = transactionManagerCustomizers + .getIfAvailable(); + } + + @Bean + @ConditionalOnMissingBean(PlatformTransactionManager.class) + public DataSourceTransactionManager transactionManager( + DataSourceProperties properties) { + DataSourceTransactionManager transactionManager = new DataSourceTransactionManager( + this.dataSource); + if (this.transactionManagerCustomizers != null) { + this.transactionManagerCustomizers.customize(transactionManager); + } + return transactionManager; + } + + } + +} +``` +@AutoConfigureOrder这个注解是springboot的新特性, 它修饰的类可以在容器启动的时候被spring扫描并配置注入。再加上@Conditional 家族的组合拳,使得jdbc的DataSourceTransactionManager 事务管理可以注入到IOC容器。其他的数据源也类似。 + + +#### 1.1 spring单机事务实战 + +声明式事务:@Transactional 一个注解就好了, spring通过切面来实现事务控制。 + +编程式事务: + +``` +public void transferAccount() { + Connection conn = null; + Statement stmt = null; + try{ + conn = getDataSource().getConnection(); + // 将自动提交设置为 false, + //若设置为 true 则数据库将会把每一次数据更新认定为一个事务并自动提交 + conn.setAutoCommit(false); + + stmt = conn.createStatement(); + // 将 A 账户中的金额减少 500 + stmt.execute("\ + update t_account set amount = amount - 500 where account_id = 'A'"); + // 将 B 账户中的金额增加 500 + stmt.execute("\ + update t_account set amount = amount + 500 where account_id = 'B'"); + + // 提交事务 + conn.commit(); + // 事务提交:转账的两步操作同时成功 + } catch(SQLException sqle){ + try{ + // 发生异常,回滚在本事务中的操作 + conn.rollback(); + // 事务回滚:转账的两步操作完全撤销 + stmt.close(); + conn.close(); + }catch(Exception ignore){ + + } + sqle.printStackTrace(); + } + } +``` +上面的两种方式,大家应该是再熟悉不过了,编程式事务就是声明式事务的源码。在编程式事务中可以看到,`conn = getDataSource().getConnection(); `会在执行sql前调用,这个就是用来获取jdbc链接的,思考一下如果我开多个线程去调用它内存地址会是一样的吗,大家可以在代码里面试试。那为什么会不一样呢,结合mysql事务机制仔细想一下,相信你一定题录灌顶。 + + +#### 思考:单数据源事务的关键是同一连接,spring是如何保证的 + +> 我们知道,mysql不能用两个黑框连接来实现同一个事务,假如有多个sql操作,分别操作同库中不同的表,要完成这个事务,必须要保证这些操作在同一个sql连接中,spring是如何做的呢。 + +以JDBC驱动为例,JDBC的dataSource连接是通过org.springframework.jdbc.datasource.DataSourceUtils来获取,在DataSourceUtils获取连接的时候,会将Connection和当前线程做绑定(调用TransactionSynchronizationManager的bindResource(dataSource, holderToUse);方法),在获取连接时,从ThreadLocal中拿到对应的Connection(TransactionSynchronizationManager中提供 getResource方法),然后在同一连接下继续执行事务。 + + +#### 哪些场景不应该用普通事务 + +> 当容器内存在多个数据源时,如果一个应用容器中存在多个不同的数据源,通常我们会创建多个数据源的事务管理器. + +比如一个Java应用要连数据库,同时也要连接MQ,这种情况下进行事务管理,我们就会创建一个DataSourceTransactionManager和一个JMSTransactionManager分别来对数据库事务和MQ事务进行管理. + +在一次请求服务的过程中使用到了2个数据源,那么事务的处理可能是这样的流程: + +``` +// 进行如下操作 +1. start message transaction +2. receive message +3. start database transaction +4. update database +5. commit database transaction +// 我出错了 +6. commit messaging transaction +``` + +在第5步和第6步之间如果出现了异常,那么由于第5步已经提交了,不能进行回滚操作,数据库已经插入了,但是消息还没有被消费掉,这样就无法满足事务的一致性了.所以就需要用分布式事务来保证其一致性。 + + +# 2. 分布式事务 + +> 跨数据源的事务。 + +### 2.1 XA协议简介 + +XA是由X/Open组织提出的分布式事务的架构(或者叫协议),是一种通用的规范。XA规范的基础是两阶段提交协议(2PC)。Oracle, Sybase, DB2, SQL Server等大型数据库支持XA。 + +XA协议内容是对底层事务资源的抽象,定义了分布式事务处理过程中事务管理器和资源管理器之间的协议,各事务资源提供商(如 JDBC 驱动,JMS)将提供此接口的实现。使用此接口,开发人员可以通过自己的编程实现分布式事务处理,但这些通常都是由应用服务器实现的(服务器自带实现更加高效,稳定) + +Mysql也有XA的实现,获取了一个普通的链接Connection之后,封装成了MysqlXAConnection + +#### XA常见问题 +##### XA二阶段提交 + +1. 性能问题 +XA协议遵循强一致性。在事务执行过程中,各个节点占用着数据库资源,只有当所有节点准备完毕,事务协调者才会通知提交,参与者提交后释放资源。这样的过程有着非常明显的性能问题。 + +2. 协调者单点故障问题 +事务协调者是整个XA模型的核心,一旦事务协调者节点挂掉,参与者收不到提交或是回滚通知,参与者会一直处于中间状态无法完成事务。 + +3. 丢失消息导致的不一致问题。 +在XA协议的第二个阶段,如果发生局部网络问题,一部分事务参与者收到了提交消息,另一部分事务参与者没收到提交消息,那么就导致了节点之间数据的不一致。 + +##### XA三阶段提交 +XA三阶段提交在两阶段提交的基础上增加了CanCommit阶段,并且引入了超时机制。一旦事物参与者迟迟没有接到协调者的commit请求,会自动进行本地commit。这样有效解决了协调者单点故障的问题。但是性能问题和不一致的问题仍然没有根本解决。 + +### 2.2 JTA事务介绍 + +Java 事务编程接口(JTA:Java Transaction API),是J2EE的编程接口规范,它是XA协议的JAVA实现。 + +``` +public void transferAccount() { + UserTransaction userTx = null; + try{ + // 获得 Transaction 管理对象 + userTx = (UserTransaction)getContext().lookup("\ + java:comp/UserTransaction"); + // 启动事务 + userTx.begin(); + // 将 A 账户中的金额减少 500 + stmtA.execute(sql1); + // 将 B 账户中的金额增加 500 + stmtB.execute(sql2); + // 提交事务 + userTx.commit(); + // 事务提交:转账的两步操作同时成功(数据库 A 和数据库 B 中的数据被同时更新) + } catch(SQLException sqle){ + // 发生异常,回滚在本事务中的操纵 + userTx.rollback(); + // 事务回滚:转账的两步操作完全撤销 + //( 数据库 A 和数据库 B 中的数据更新被同时撤销) + } catch(Exception ne){ + e.printStackTrace(); + } + } +``` + +面向开发人员的接口为 UserTransaction: + + +* begin() – 开始一个分布式事务,(在后台 TransactionManager 会创建一个 Transaction 事务对象并把此对象通过 ThreadLocale 关联到当前线程上 ) +* commit() – 提交事务(在后台 TransactionManager 会从当前线程下取出事务对象并把此对象所代表的事务提交) +* rollback() – 回滚事务(在后台 TransactionManager 会从当前线程下取出事务对象并把此对象所代表的事务回滚) +* getStatus() – 返回关联到当前线程的分布式事务的状态 (Status 对象里边定义了所有的事务状态,感兴趣的读者可以参考 API 文档 ) +* setRollbackOnly() – 标识关联到当前线程的分布式事务将被回滚 + + +UserTransaction是对Transaction接口的扩展,上面的这些操作最终都会落到真正的Transaction上来实现commit和rollback,这里的Transaction 操作的就是实现了XA协议的数据源。 + +不同的是,JTA的连接管理是通过TransactionManager 来实现,调用 UserTransaction.begin() 方法时 TransactionManager 会创建一个 Transaction 事务对象并把此对象通过 ThreadLocale 关联到当前线程上 + +#### 思考:如何记录同一个切面下 不同事务 属于同一个分布式事务呢? + +支持事务的数据源与普通的数据源是不同的,它实现了额外的 XADataSource 接口。 + +![image.png](https://upload-images.jianshu.io/upload_images/5786888-d2bb5fa2960a8404.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +Xid 类用来标识事务,可以把 Xid 想象成事务的一个标志符,每次在新事务创建是都会为事务分配一个 Xid,Xid 包含三个元素:formatID、gtrid(全局事务标识符)和 bqual(分支修饰词标识符)。 formatID 通常是零,这意味着你将使用 OSI CCR(Open Systems Interconnection Commitment, Concurrency 和 Recovery 标准)来命名;如果你要使用另外一种格式,那么 formatID 应该大于零,-1 值意味着 Xid 为无效。gtrid 和 bqual 分别包含 64 个字节二进制码来分别标识全局事务和分支事务, gtrid 和 bqual 必须是全局唯一的。 + +在事务被提交时,Transaction 对象会收集所有被当前事务包含的 XAResource 资源,然后调用资源的提交方法,如果有失败就全部rollback。 + +JTA一般和Atomikos框架配合使用 + +### 2.3 TCC事务介绍 + +> 是基于补偿型事务的AP系统的一种实现, 具有最终一致性。原理是2PC两阶段提交。 + +TCC(Try/Confirm/Cancel)事务机制相对于传统事务机制(X/Open XA),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。 + +国内开源的TCC框架:ByteTCC、Himly、TCC-transaction + +##### TCC原理图: + +![image.png](https://upload-images.jianshu.io/upload_images/5786888-7ef17ab1538782a0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +TCC是2PC的思想在应用层面的落地实现,使用TCC实现事务,需要每个接口中都有try、Confirm、Cancel接口,并保证这三个接口的幂等性。需要开发很大量的代码,花费很高的成本,一般不会使用它。 + +##### TCC优缺点 +优点:可以让应用自己定义数据操作的粒度,使得降低锁冲突、提高吞吐量成为可能。 +缺点:对应用的侵入性非常强,业务逻辑的每个分支都需要实现try、confirm、cancel三个操作。此外,其实现难度也比较大,需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。Confirm/Cancel业务可能会被多次调用,就需要保障其幂等性。 + +TCC故障处理:[https://blog.csdn.net/dm_vincent/article/details/92432059](https://blog.csdn.net/dm_vincent/article/details/92432059) + +### 2.4 LCN事务框架 + +> LCN分布式事务框架其本身并不创建事务,而是基于对本地事务的协调从而达到事务一致性的效果。 + +LCN有三种模式:LCN模式,TCC模式,TXC模式。TCC模式上面有说,这里介绍LCN模式。 + +##### 使用方式: +多数据源配置 + 多数据源实例注入到LCN事务管理器中,后续即可通过@LcnTransaction 注解来实现。 + +事务流程图: + +![image.png](https://upload-images.jianshu.io/upload_images/5786888-a16cb14618965f66.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +TxManager本身是一个Socket服务,来处理每个事务传递过来的指令。同一个事物组中都成功后会返回成功,有一个事务失败后会返回给所有的事务rollback。 + +整个事务在一个切面里面,切面里面会生成一个事务组id,将各个事务绑定,并上传各个事务的结果:commit 还是rollback。 + +##### 事务管理如何做到等待全部事务的结果呢? + +每个事务提交会附带一个锁对象,事务管理新开辟线程来锁住该对象,等本地事务状态达到终态后将锁进行释放,事务管理就会收到事务的结果。 + +LCN事务本地事务是一定要保持在同一个线程内完成,如何保证的呢?ThreadLocal,在获取dataSource连接&创建事务的时候,在threadLocal中存放一份事务对象 + +##### 优缺点 + +优点:性能优秀;对代码的嵌入性低;该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。 + +缺点:需额外部署 tx-manager 服务节点;代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间; 服务超时时,会造成其他服务的资源被锁住,比如支付服务超时过程中,相关商品库存会一直无法操作; + +### seata框架(GTS开源版本) + +官网:http://seata.io/zh-cn/ +文档:http://seata.io/zh-cn/docs/overview/what-is-seata.html + +GTS文档:https://www.jianshu.com/p/65b7fd061a33 + +### 2.5 消息驱动事务 + +> 基于消息中间件的最终一致性事务方案是互联网公司在高并发场景中探索出的一种创新型应用模式。 + +消息一致性方案是通过消息中间件保证上、下游应用数据操作的一致性。基本思路是将本地操作和发送消息放在一个事务中,保证本地操作和消息发送要么两者都成功或者都失败。下游应用向消息系统订阅该消息,收到消息后执行相应操作。 + +![image.png](https://upload-images.jianshu.io/upload_images/5786888-4f13ec2482fa7ae1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +消息方案从本质上讲是将分布式事务转换为两个本地事务,然后依靠下游业务的重试机制达到最终一致性。基于消息的最终一致性方案对应用侵入性也很高,应用需要进行大量业务改造,成本较高。由于会出现网络延迟的问题,消息重复、消息顺序无法保证的情况也会出现。需要业务机制进行补偿。 + +#### 扩展:dataSource连接 Connection 接口会有commit和rollback方法,它如何实现提交和回滚操作的? + +通过sockcet和mysql进行的指令交互,真正实现靠mysql的事务管理。undo、redo日志实现。[https://www.cnblogs.com/wyc1994666/p/11367051.html](https://www.cnblogs.com/wyc1994666/p/11367051.html) + + +### 事务的适用场景 +>分布式事务,按照控制力度可以分为:不控制、部分控制和完全控制。 + +* 不控制就是不引入分布式事务。 +* 部分控制就是引入了分布式事务,但数据不是强一致性,比如TCC和消息驱动事务,它的优点是并发量和性能很好。适用于一些不要求时效性和一致性的场景。比如:交易成功发短息、推送消息等 +* 完全控制保证了数据的强一致性,比如LCN、GTS,这种事务牺牲了性能,保障了数据的一致性。适用于要求时效性和一致性的场景,比如金融支付,共享出行等。 + + +### 工作上能到用哪些 +目前我们大多数场景使用的是消息驱动的事务模型,确保数据最终一致性即可,金融、库存服务对数据强一致性要求较高,所以对事务的强一致性要求也较高,不适合用消息驱动的事务模型。这些不同的思想的学习,可以为我们后续的工作添加一份思路。 + +1. 学习spring事务管理接口定义的设计模式,在需求开发中提高功能的可扩展性。 +2. 对需要同一线程处理的资源,可以模拟TransactionSynchronizationManager 的思路,用ThreadLocal实现。使用时注意ThreadLocal的内存泄漏问题。 diff --git "a/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-ZK\345\215\225\346\234\272\346\220\255\345\273\272.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-ZK\345\215\225\346\234\272\346\220\255\345\273\272.md" new file mode 100644 index 0000000..6f551b2 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-ZK\345\215\225\346\234\272\346\220\255\345\273\272.md" @@ -0,0 +1,30 @@ +### 单机安装 + +#### 目录结构 +![](https://upload-images.jianshu.io/upload_images/5786888-30439c11210644fe.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +contrib:附加功能 + +dist-maven:maven编译后产生的目录 + +recipes:demo案例 + +lib:依赖的jar包 + + +#### 配置zoo.cfg +``` +# 用于计算的时间单元。 +tickTime=2000 +# The number of ticks that the initial +# 用于集群,允许从节点连接并同步到master节点的初始化连接时间,以tickTime的倍数来表示 +initLimit=10 +# 用于集群,主从节点通讯请求和应答时间(心跳机制) +syncLimit=5 +# 数据目录 +dataDir=/tmp/zookeeper +# 客户端端口(用户客户端连接) +clientPort=2181 + +``` +#### 启动 +`./zkServer.sh start` \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/\347\224\250mwget\345\267\245\345\205\267\346\235\245\346\217\220\345\215\207wget\344\270\213\350\275\275\351\200\237\345\272\246.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/\347\224\250mwget\345\267\245\345\205\267\346\235\245\346\217\220\345\215\207wget\344\270\213\350\275\275\351\200\237\345\272\246.md" new file mode 100644 index 0000000..1de4c32 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/\347\224\250mwget\345\267\245\345\205\267\346\235\245\346\217\220\345\215\207wget\344\270\213\350\275\275\351\200\237\345\272\246.md" @@ -0,0 +1,31 @@ +>mwget是一个多线程实现wget的一个工具。 + +### 1. 安装 +``` +wget http://jaist.dl.sourceforge.net/project/kmphpfm/mwget/0.1/mwget_0.1.0.orig.tar.bz2 +# 安装bzip2压缩工具 +yum install bzip2 +# 安装c++ +yum install gcc-c++ + +# 解压 +tar -jxvf mwget_0.1.0.orig.tar.bz2 ./ + +cd mwget_0.1.0.orig + +./configure + +make&make install +``` +### 2. 使用 + +``` +将wget命令改成mwget +``` + +### 3. 效果 +![image.png](https://upload-images.jianshu.io/upload_images/5786888-8647b6cce0a1b190.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +![欢迎关注公众号](https://user-gold-cdn.xitu.io/2020/2/22/1706c6ee573189ff?w=258&h=258&f=jpeg&s=15621) + +GitHub地址: https://github.com/fantj2016/java-reader \ No newline at end of file diff --git "a/9. \346\236\266\346\236\204\350\256\276\350\256\241/\347\224\261\346\265\205\345\205\245\346\267\261\347\220\206\350\247\243Raft\345\215\217\350\256\256.md" "b/9. \346\236\266\346\236\204\350\256\276\350\256\241/\347\224\261\346\265\205\345\205\245\346\267\261\347\220\206\350\247\243Raft\345\215\217\350\256\256.md" new file mode 100644 index 0000000..7d3d846 --- /dev/null +++ "b/9. \346\236\266\346\236\204\350\256\276\350\256\241/\347\224\261\346\265\205\345\205\245\346\267\261\347\220\206\350\247\243Raft\345\215\217\350\256\256.md" @@ -0,0 +1,2 @@ +分享一个同事的公众号写的非常详细: +https://mp.weixin.qq.com/s/20Rno9Er_x4gg6kERSF1bA \ No newline at end of file diff --git "a/HR\351\235\242\350\257\225\346\212\200\345\267\247/HR\351\200\232\345\270\270\346\217\220\347\232\204\344\270\200\344\272\233\351\235\242\350\257\225\351\227\256\351\242\230.md" "b/HR\351\235\242\350\257\225\346\212\200\345\267\247/HR\351\200\232\345\270\270\346\217\220\347\232\204\344\270\200\344\272\233\351\235\242\350\257\225\351\227\256\351\242\230.md" deleted file mode 100644 index faad802..0000000 --- "a/HR\351\235\242\350\257\225\346\212\200\345\267\247/HR\351\200\232\345\270\270\346\217\220\347\232\204\344\270\200\344\272\233\351\235\242\350\257\225\351\227\256\351\242\230.md" +++ /dev/null @@ -1,161 +0,0 @@ - ->如果你是应届毕业生,请一定看完!如果你正在找工作,请一定看完!看完这篇文章都会对你有帮助的,最刁钻的10大面试问题一次性帮你解决(均附参考建议及回答)全程干货无废话。文末链接50个面试必答问题技巧! -### 1.你最大的缺点问题分析: ->考察候选人的自我认知能力,个人优点是否符合岗位要求。 - -参考建议: - -第一,不宜说自己没有缺点,只要是人就有缺点,这么说一定会令人反感。 - -第二,年轻经验不足、缺乏磨炼、有些着急、对待效率低下的人缺乏耐心等根据以上的关键点,缺点参考回复:首先,我刚毕业,经验方面不足,我会在工作中积极完成工作,积累各方面经验其次,性子急,对待效率低下的人缺乏耐心,但是我平时和别人聊天的时候会控制自己语速和讲话,慢慢培养自己耐心,避免浮躁。(遵循一个原则避重就轻) - -Tips:利用你的优点改正你的缺点,比如,工作追求细节极致,导致项目无法按时完成,通过时间管理,得以解决。一定不能说对应聘岗位的硬伤的缺点,以及无法弥补的缺点。 - - -### 2.面试最难:自我介绍问题分析: ->这道题主要考察应聘者的逻辑思维、语言表达、自我认知等能力。 - -参考建议: - -第一,条理清晰,层次分明,突出与岗位要求相吻合的技能、个人所长、行为风格、实际经验等。 - -第二,现场表达必须与个人简历所写保持一致。 - -第三,控制时间,一般不超过 3 分钟。 - -第四,尽量口语化,语言平实可信。 - -▲根据以上的关键点,参考回复:首先,我叫xxx,xxx大学xxx专业毕业,我在学校获得xxx荣誉(或者证书)……这样的信息(基本信息介绍)其次,在工作方面,我在xxx公司实习(或者学校活动),我负责xx工作,为了完成这个工作,我做了xxx努力,最后取得xxx成果,结尾,还可以总结一下通过这次活动或者项目有什么收获。关键点,在做了什么努力这部分要体现做的深度。(利用STAR法则)第三,在大型比赛中取得xxx的成绩,如果没有,可以讲自己参加过的公益类活动,例如支教,敬老院看望爷爷奶奶。(特殊经历亮点加分项)(ps:最好准备一个1分钟自我介绍,一个3-5 分钟自我介绍,多手准备!!) - -Tips:在面试前一天,一定一定要熟记自我介绍,自己也可以对着镜子模拟面试情景反复刻意练习自我介绍。这样才能保证面试从容,不紧张。 - - -### 3.面试必答题:谈谈薪酬待遇? ->问题分析:判断候选人对薪资待遇的要求是否与单位能够提供的标准相匹配。写到这里我就想起自己第一次面试这道问题,我是这样回答的: - -Q:你期望薪资多少? - -A:都可以,够在这个城市生活就好…… - - -参考建议: - -第一,每家单位都有自己的薪酬标准。 - -第二,可以先提交一个薪酬区间,一旦被录用,人力资源部一定会有专人与您进行薪酬沟通,到时再友好协商也不晚。 - -Tips:每个单位都有薪资宽带就最低最高界限,评估自己能力及自己生活所需,可以先提交一个薪酬区间,如果你能力强可以往上限靠,如果一般取中间值。 - -▲提示:关于正确提问薪资待遇 - -方法一:不能谈薪资,为什么说不能谈薪资呢?作为一个毕业生,一个初入职场的人的时候,我们不能跟谈薪资,我们只能听薪资,适合我就做,不适合我就走。 - -方法二:谈薪资,首先,你要证明自己的价值。第二,你要让HR认可你的价值。这两点做到了,就可以谈薪资了。 - -### 4.你的兴趣爱好? ->问题分析:了解候选人的心态、性格、价值观、责任感等当HR问到兴趣爱好,这时大脑一片空白,一时想不到,拍脑袋回答没什么爱好,或者随便回答,那你就会掉进坑里了。 - -参考建议: - -第一,常见的爱好无外乎运动、旅游、听音乐、读书等,比如篮球:团队精神,古典音乐、阅读、书法:细心耐心,旅游:适应能力学习能力,演讲:沟通能力,唱歌、舞蹈:性格外向,沟通能力 - -第二,如果有表现突出的文体爱好,例如书法、羽毛球、小提琴、写文章等获得过有关奖项,可以适当加分 - -第三,如果热衷社会公益,参与过某些公益组织,为困难人群提供过无私、积极的帮助的,可以突出介绍,可以给面试官留下更好地印象。 - -▲根据以上的关键点,参考回复:eg1:写与岗位匹配的爱好,假如是应聘文案类(编辑)岗位,你说你喜欢写文章,发表过文章,还获得奖,加分!加分!(没得奖也不怕给HR看写过的文章,体现你是有潜力的)eg2:我喜欢读书,一年读了xx本,收获xxx。 - -Tips:说岗位需求匹配的爱好,有助于工作的爱好;回答要真实,否则HR接着深入一问,容易露馅。 - -### 5.介绍未来5年职业规划(必考题!!!) ->问题分析:考察候选人对自己未来发展的设想、职业生涯的规划能力。除非是目标非常明确的人,或者有多年工作经验的职场人,不然很难回答清楚,那么怎么说才能回答好这个问题呢? - -不要说“几年当主管”,"几年当经理"毫无意义。 - -参考建议: - -第一,介绍自己认真思考过这个问题,自己的规划是基于目前的实际情况来设计的。 - -第二,在工作方面,突出自己打算通过积极完成工作任务,积累各方面的经验,让自己成为这个领域的专业人士,也希望有机会能够带领团队,成为优秀的管理者,为单位做出更大贡献,获得双赢。 - -第三,在学习方面,打算在专业领域做进一步学习和研究,将实践经验与专业知识相结合,为自己的职业成长做好铺垫,打好基础。 - - - -Tips:回答这个问题强调你稳定性,踏实工作的态度,重点在工作技能方面的提升与内在积累,不要描述外在的东西,比如职位,薪资。 - - -### 6.面试入坑题:怎样看待加班? ->问题分析:考察候选人的责任心和职业道德。五花八门的回答:“我不愿意接受无意义加班”“没问题,随时都可以加班” - -参考建议: - -第一,任何一家单位都有可能要加班。 - -第二,自身的工作任务没有完成,加班是理所当然的,当然,自己会不断提高专业技能,以尽量减少不必要的加班,之前也是这么做的。 - -第三,如果遇到紧急任务或突发情况时,需要加班,自己会尽己所能,希望能够尽快顺利地完成团队面临的任务。 - - -Tips:表现出自己愿意牺牲自己的一部分个人时间,提升个人能力,为公司创造更多利益;明确岗位是否需要经常加班,表明自己态度。 - -### 7.面试陷阱题:希望与怎样的领导共事?一类题:希望与怎样的领导合作?怎样处理与领导的关系? ->问题分析:考察候选人的人际交往能力、主动适应能力。如果你回答:我希望我的上级比较有经验,能够给饿哦一些帮助,陷阱!这样会暴露自己短处。 - -参考建议: - -第一,尽量不要提及对领导的具体要求,而应该突出自己会认真向领导学习,尽快熟悉和适应工作环境,主动向领导请教,保质保量完成本职工作。 - -第二,如果有做得不到的地方,会诚恳地向领导请教,可以在哪些地方多多改善。这才是一位职业人作为下属应该秉持的工作态度。Tips:切忌一切围绕工作进行,着重谈论对自己有要求,自身努力的方向,千万不要提及前任领导的缺点。 - -### 8.若领导布置了大量的工作,而完成时间又十分有限,为了完成任务,您怎么办? ->问题分析:考察候选人的时间管理能力。 - -参考建议:第 - -一,分清任务的轻重缓急,紧急又重要的任务先完成。 - -第二,发动团队其他成员,借力完成。 - -第三,鼓励老人带新人,提高工作效率。Tips:实在是过重,以上方法全部用上了都不行,可以与领导协商,先完成几成,其他不重要的任务可以缓办。 - -### 9.为什么应聘这个岗位? ->问题分析:考察候选人的求职动机、求职意向及对岗位的认知能力。 - -参考建议: - -第一 ,是要突出个人经验和技能与该职位的匹配度相对比较高。 - -第二,提前做功课,仔细查阅用人单位的网站和视频资料,最好是要在应答中提到招聘单位的规模、品牌、知名度、规范性、愿景等等。 - -第三,强调用人单位是适合个人职业发展的平台。 - -Tips:重点突出个人经验和技能与该职位的匹配度。如果之前有与这个单位有直接交往的正面案例,也可以顺便提出来,这是个加分项,说明对方是自己心仪的单位,希望能够加盟这个优秀的团队。 - - - -### 10.面试终极必杀问题:还有什么要问的吗? ->问题分析:考察候选人的情商,是否对这个公司或者行业很了解,是否用心准备。 - -参考建议: - -第一,可以问本职岗位工作要求、职责。例如,这个部门人员设置是怎么样的。 - -第二,可以问公司、公司的业务、体系、行业、客户。eg:为了胜任该职位,需要我提前学习哪些技术知识?eg:贵公司业务及战略的未来发展?eg:团队、公司现在面临的最大挑战是什么? - - -Tips:切忌纠缠薪资,如果回答没问题,HR会误会,你对岗位没有太大兴趣。在每道题回答之后,加两字,谢谢!最后推荐职场相关电影当幸福来敲门肖申克救赎终极面试阿甘正传书籍《不要等到毕业以后》(ps:如果有启发,请点个小赞鼓励一下哈,听说点赞的童鞋面试必过哦~皮一下感谢感恩!) - - - -### 声明 -文章转载自知乎:https://www.zhihu.com/question/24192778/answer/631081857 - -作者:职研社De圆圆 - -著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 - - -还有,不要以为这些就够用了,还需要去再多了解了解。知乎就是个不错的选择:https://www.zhihu.com/question/24192778 - -https://www.zhihu.com/search?type=content&q=hr%E9%9D%A2%E8%AF%95%E4%B8%80%E8%88%AC%E4%BC%9A%E9%97%AE%E5%95%A5 \ No newline at end of file diff --git a/README.md b/README.md index 3cccb9e..1993530 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,21 @@ [![微信公众号](https://img.shields.io/badge/%E5%85%AC%E4%BC%97%E5%8F%B7-PlayInJava-red.svg)](https://upload-images.jianshu.io/upload_images/5786888-74bca7fff151cfb8.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/500) ->【Java入门 -> 架构笔记】未来的你一定会感谢今天学习的自己! 本项目主打Java基础、算法、框架实战、源码、中间件、大数据、面试等知识积累和经验总结。文章共300多篇, 大部分为原创, 部分翻译和转载已标明出处。 谢谢大家的支持, 转载请标明出处, 谢谢。 +>【Java入门 -> 进大厂笔记📖📒】未来的你一定会感谢今天学习的自己! 本项目主打Java基础、算法、框架实战、源码、中间件、大数据、面试等知识积累和经验总结。文章共300多篇, 大部分为原创, 部分翻译和转载已标明出处。 谢谢大家的支持, 转载请标明出处, 谢谢。 +[![Star History Chart](https://api.star-history.com/svg?repos=fantj2016/java-reader&type=Date)](https://star-history.com/#fantj2016/java-reader) + 感兴趣的朋友可以加入我们, 一起完善, 投稿请加微信。 -参考书籍: 《深入理解Java虚拟机》、《并发编程的艺术》、《Java多线程核心编程艺术》、《Java8函数式编程》、《Redis设计与实现》、《RocketMQ技术内幕》、《Spring技术内幕》、《Spring源码深度解析》、《剑指Offer》、《大话设计模式》... +推荐书籍: 《深入理解Java虚拟机》、《并发编程的艺术》、《Java多线程核心编程艺术》、《Java8函数式编程》、《Redis设计与实现》、《RocketMQ技术内幕》、《Spring技术内幕》、《Spring源码深度解析》、《剑指Offer》、《大话设计模式》... 详细书单请在公众号获取。 + +我的简书:https://www.jianshu.com/u/f223a6ff7f2a + +我的掘金:https://juejin.im/user/5a6adbdb6fb9a01ca6030469 + +我的开源项目: [https://github.com/fantj2016/java-reader](https://github.com/fantj2016/java-reader/blob/master/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE.md) + ### 1. 算法 @@ -20,7 +29,7 @@ ### 2. Java基础 -#### 2.1 基础篇 +#### 2.1 基础 * [JVM](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.1%20%E5%9F%BA%E7%A1%80%E7%AF%87/1.%20JVM%E7%AF%87) * [Java并发、多线程](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.1%20%E5%9F%BA%E7%A1%80%E7%AF%87/2.%20%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E7%AF%87) * [反射详解](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.1%20%E5%9F%BA%E7%A1%80%E7%AF%87/3.%20%E5%8F%8D%E5%B0%84%E7%AF%87) @@ -32,17 +41,14 @@ * [设计模式文章](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.1%20%E5%9F%BA%E7%A1%80%E7%AF%87/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F) * [Tomcat类加载器(待完成)]() -#### 2.2 源码篇 +#### 2.2 源码 * [Java源码、集合源码](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.2%20%E6%BA%90%E7%A0%81%E7%AF%87) -#### 2.3 面试篇 -* [Java基础面试题总结](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.3%20%E9%9D%A2%E8%AF%95%E7%AF%87) - --- ### 3. 框架专题 -#### 3.1 实战篇 +#### 3.1 实战 * [SpringBoot整合&实战](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.1%20%E5%AE%9E%E6%88%98%E7%AF%87/SpringBoot) * [Mybatis使用&常见问题解决](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.1%20%E5%AE%9E%E6%88%98%E7%AF%87/Mybatis) * [SpringSecurity安全框架实战](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.1%20%E5%AE%9E%E6%88%98%E7%AF%87/SpringSecurity) @@ -54,7 +60,7 @@ * [Netty](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.1%20%E5%AE%9E%E6%88%98%E7%AF%87/Netty) * [Spring](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.1%20%E5%AE%9E%E6%88%98%E7%AF%87/Spring) -#### 3.2 源码篇 +#### 3.2 源码解析 * [Spring源码解析](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.2%20%E6%BA%90%E7%A0%81%E7%AF%87/Spring%E6%BA%90%E7%A0%81) * [SpringBoot源码](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.2%20%E6%BA%90%E7%A0%81%E7%AF%87/SpringBoot%E6%BA%90%E7%A0%81) * [SpringMvc源码解析](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.2%20%E6%BA%90%E7%A0%81%E7%AF%87/SpringMVC%E6%BA%90%E7%A0%81) @@ -63,14 +69,8 @@ * [Dubbo源码](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.2%20%E6%BA%90%E7%A0%81%E7%AF%87/Dubbo%E6%BA%90%E7%A0%81) -#### 3.3 面试题 -* [Spring面试题(待完成)]() -* [SpringMvc面试题(待完成)]() -* [Mybatis面试题(待完成)]() -* [Redis面试](https://github.com/fantj2016/java-reader/blob/master/Redis%E9%9D%A2%E8%AF%95/redis.md) - -#### 3.4 框架实现篇 +#### 3.3 框架实现 * [Tomcat框架简单实现](https://github.com/fantj2016/MyTomcat) * [Jedis框架简单实现](https://github.com/fantj2016/easy-jedis) * [MVC框架简单实现](https://github.com/fantj2016/easy-springmvc) @@ -80,18 +80,16 @@ * [分布式事务](https://github.com/fantj2016/java-reader/tree/master/4.%20%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%93%E9%A2%98/4.1%20%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1) * 分布式锁..待更新 -##### 4.1 分布式面试题 - --- ### 5. 中间件专题 -#### 5.1 实战篇 +#### 5.1 实战 * [Nginx入门](https://github.com/fantj2016/java-reader/tree/master/5.%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B8%93%E9%A2%98/5.1%20%E5%AE%9E%E6%88%98%E7%AF%87/1.%20Nginx) * [Redis入门](https://github.com/fantj2016/java-reader/tree/master/5.%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B8%93%E9%A2%98/5.1%20%E5%AE%9E%E6%88%98%E7%AF%87/2.%20Redis) * [Zookeeper部分](https://github.com/fantj2016/java-reader/tree/master/5.%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B8%93%E9%A2%98/5.1%20%E5%AE%9E%E6%88%98%E7%AF%87/3.%20Zookeeper) * [MQ从入门到实战](https://github.com/fantj2016/java-reader/tree/master/5.%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B8%93%E9%A2%98/5.1%20%E5%AE%9E%E6%88%98%E7%AF%87/4.%20MQ) -#### 5.2 源码篇 +#### 5.2 源码解析 * [Redis源码分析](https://github.com/fantj2016/java-reader/tree/master/5.%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B8%93%E9%A2%98/5.2%20%E6%BA%90%E7%A0%81%E7%AF%87/Redis%E6%BA%90%E7%A0%81) ### 6. 高效研发 @@ -115,27 +113,27 @@ --- ### 8. 大数据 -* [服务搭建篇](https://github.com/fantj2016/java-reader/tree/master/8.%20%E5%A4%A7%E6%95%B0%E6%8D%AE/8.1%20%E6%9C%8D%E5%8A%A1%E6%90%AD%E5%BB%BA%E7%AF%87) -* [实战篇](https://github.com/fantj2016/java-reader/tree/master/8.%20%E5%A4%A7%E6%95%B0%E6%8D%AE/8.2%20%E5%AE%9E%E6%88%98%E7%AF%87) +* [服务搭建](https://github.com/fantj2016/java-reader/tree/master/8.%20%E5%A4%A7%E6%95%B0%E6%8D%AE/8.1%20%E6%9C%8D%E5%8A%A1%E6%90%AD%E5%BB%BA%E7%AF%87) +* [实战](https://github.com/fantj2016/java-reader/tree/master/8.%20%E5%A4%A7%E6%95%B0%E6%8D%AE/8.2%20%E5%AE%9E%E6%88%98%E7%AF%87) ### 9. 架构设计 -* [架构设计篇](https://github.com/fantj2016/java-reader/tree/master/9.%20%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1) - -### HR面经验 -* [HR面技巧](https://github.com/fantj2016/java-reader/tree/master/HR%E9%9D%A2%E8%AF%95%E6%8A%80%E5%B7%A7) +* [架构设计](https://github.com/fantj2016/java-reader/tree/master/9.%20%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1) ### 公众号 +>github阅读不方便?添加公众号,随时随地当reader,不定时发放福利 + ![我的公众号](https://upload-images.jianshu.io/upload_images/5786888-74bca7fff151cfb8.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/500) #### 公众号留言 +刚入门的小伙伴、或者是学习方向上遇到障碍的小伙伴可以在公众号留言,把你的问题描述清楚,我将在24H内认真回复。 -帮大伙找个互联网男女朋友, 生活从此无鸭梨~~ +#### 公众号福利 +添加本人微信留言:领取福利。 -1. 回复"找对象", 拉你进IT单身集中营。 -2. 回复"学习", 可分享给你学习资料, 若资料失效可联系vx更新。 -3. 有什么技术方向、情感等问题, 都可以在公众号给我留言, 24H内认真回复。 ### 交流群 ->不定时会分享一些学习方法, 书籍, 企业技术, 算法等知识, 一起成长、一起进步。 +>不定时会分享一些学习方法, 书籍, 企业技术, 算法等知识, 一起成长、一起进步。 有需要阿里社招内推的同学也可以联系哈,会帮你一直跟进流程。 + +![微信群加我备注进群](https://upload-images.jianshu.io/upload_images/5786888-9d87c2d1812f322e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/200) ![QQ交流群](https://upload-images.jianshu.io/upload_images/5786888-bc946ca74be7d601.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/200) diff --git a/a-code/src/Entry.class b/a-code/src/Entry.class new file mode 100644 index 0000000..9b7a328 Binary files /dev/null and b/a-code/src/Entry.class differ diff --git a/a-code/src/Entry.java b/a-code/src/Entry.java new file mode 100644 index 0000000..95f9f98 --- /dev/null +++ b/a-code/src/Entry.java @@ -0,0 +1,12 @@ +public class Entry { + private int total = 10; + + public int getTotal() { + return total; + } + + public Entry setTotal(int total) { + this.total = total; + return this; + } +} diff --git a/a-code/src/Main.class b/a-code/src/Main.class new file mode 100644 index 0000000..7bb683c Binary files /dev/null and b/a-code/src/Main.class differ diff --git a/a-code/src/Main.java b/a-code/src/Main.java new file mode 100644 index 0000000..ac07454 --- /dev/null +++ b/a-code/src/Main.java @@ -0,0 +1,5 @@ +public class Main { + public static void main(String[] args) { + Entry entry = new Entry(); + } +} \ No newline at end of file diff --git "a/\346\210\221\347\232\204\345\274\200\346\272\220\351\241\271\347\233\256/\345\274\200\346\272\220\351\241\271\347\233\256.md" "b/\346\210\221\347\232\204\345\274\200\346\272\220\351\241\271\347\233\256/\345\274\200\346\272\220\351\241\271\347\233\256.md" new file mode 100644 index 0000000..7c13fb6 --- /dev/null +++ "b/\346\210\221\347\232\204\345\274\200\346\272\220\351\241\271\347\233\256/\345\274\200\346\272\220\351\241\271\347\233\256.md" @@ -0,0 +1,45 @@ +# 竞赛网 + +#### 概述 +1. 使用Redis实现数据库减压,Mysql实现数据持久化。 +2. 使用SpringBoot做基础框架,JPA/Mybatis作ORM框架,Spring Security 作权限鉴定框架。 +3. 使用Dubbo做微服务框架,Zookeeper做服务注册中心。 +4. 使用Nginx做反向代理,Nginx+ FTP实现文件服务器。 + +git地址:https://github.com/fantj2016/internet-plus + + +# 博客论坛 +正在开源的路上... + +# Tomcat框架简单实现 +#### 概述 +1. 完成对请求头中url的解析,并返回静态资源。 +2. 完成对配置类`conf.properties`的加载,通过反射完成动态请求的实现。 + +git地址:https://github.com/fantj2016/MyTomcat + +# Jedis框架简单实现 +#### 概述 +redis运行流程:发送命令-命令排队-命令执行-返回结果 + +根据RESP协议, 模拟一个redis的客户端。 + +git地址:https://github.com/fantj2016/easy-jedis + +# MVC框架简单实现 +#### 概述 +1. IOC容器实现 +2. MVC视图解析实现。 + +git地址:https://github.com/fantj2016/easy-springmvc + +# rpc框架简单实现 +#### 概述 +1. 服务提供者向zk注册服务 +2. 消费者去注册中心拿到服务信息 +3. 消费者根据接口信息进行动态代理(代理内容:拿到接口名、方法名、参数类型、参数,然后根据接口名再获取到服务端url信息, 对对应的ip发送Invocation信息),服务端监听到请求,根据Invocation反射调用方法,将结果返回给消费者 + + + +git地址:https://github.com/fantj2016/easy-dubbo \ No newline at end of file