diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/21. \345\214\205\345\220\253min\345\207\275\346\225\260\347\232\204\346\240\210.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/21. \345\214\205\345\220\253min\345\207\275\346\225\260\347\232\204\346\240\210.md" new file mode 100644 index 0000000..0aa1953 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/21. \345\214\205\345\220\253min\345\207\275\346\225\260\347\232\204\346\240\210.md" @@ -0,0 +1,62 @@ +### Title +设计一个支持push,pop,top等操作并且可以在O(1)时间内检索出最小元素的堆栈。 + +- push(x)–将元素x插入栈中 +- pop()–移除栈顶元素 +- top()–得到栈顶元素 +- getMin()–得到栈中最小元素 +### Demo +``` +MinStack minStack = new MinStack(); +minStack.push(-1); +minStack.push(3); +minStack.push(-4); +minStack.getMin(); --> Returns -4. +minStack.pop(); +minStack.top(); --> Returns 3. +minStack.getMin(); --> Returns -1. +``` +### Analysis +准备两个栈, 一个用来放当前入栈的最小值, 一个用来入栈全量数据。 + +当当前的值比min的栈顶还小, 则将该值放到min栈中去。 + +### Code + +```java +class MinStack { + + Stack stk = new Stack<>(); + Stack min = new Stack<>(); + /** initialize your data structure here. */ + public MinStack() { + + } + + public void push(int x) { + if(min.empty() || min.peek() >= x){ + min.push(x); + } + stk.push(x); + } + + public void pop() { + if(stk.peek() == min.peek()){ + min.pop(); + } + stk.pop(); + } + + public int top() { + return stk.peek(); + } + + public int getMin() { + if(!min.empty()){ + return min.peek(); + } + return stk.peek(); + } +} + +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/22. \351\241\272\346\227\266\351\222\210\346\211\223\345\215\260\347\237\251\351\230\265.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/22. \351\241\272\346\227\266\351\222\210\346\211\223\345\215\260\347\237\251\351\230\265.md" new file mode 100644 index 0000000..7e599b1 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/22. \351\241\272\346\227\266\351\222\210\346\211\223\345\215\260\347\237\251\351\230\265.md" @@ -0,0 +1,80 @@ +### Title +输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。 +### Demo +``` +输入: +[ + [1, 2, 3, 4], + [5, 6, 7, 8], + [9,10,11,12] +] + +输出:[1,2,3,4,8,12,11,10,9,5,6,7] +``` +### Analysis + +1. 获取到二维数组的列数和行数 +2. 往右方向遍历 == x不变, y++ +3. 同理每个方向都遍历一遍 +4. 以一圈为单位遍历 + +### Code + +```java +class Solution { + public int[] printMatrix(int[][] matrix) { + if(matrix == null|| matrix.length == 0){return new int[0];} + int startX = 0; + int endX = matrix.length; + int startY = 0; + int endY = matrix[0].length; + //System.out.println(startX + " " + endX + " " + startY + " " + endY); + int res[] = new int[endX * endY ]; + while((startX < endX) && (startY < endY)){ + printMatrix(res, matrix, startX, endX-1, startY, endY-1); + startX++; + startY++; + endX--; + endY--; + } + return res; + } + public int index = 0; + public void printMatrix(int res[], int[][] matrix, int startX, int endX, int startY, int endY){ + // 向右 ➡ + if(startX == endX){ + // 说明只能竖着来了, 为什么要等于, 因为怕拉下最后一个元素了 + for(int i = startY; i <= endY; i++){ + res[index++] = matrix[startX][i]; + } + return; + } + + // 向下 ⬇ + if(startY == endY){ + // 说明只能竖着来了 + for(int i = startX; i <= endX; i++){ + res[index++] = matrix[i][startY]; + } + return; + } + // 向右 ➡ + for(int i = startY; i < endY; i++){ + res[index++] = matrix[startX][i]; + } + // 向下 ⬇ + for(int i = startX; i < endX; i++){ + res[index++] = matrix[i][endY]; + } + // 向左 ⬅ + for(int i = endY; i > startY; i--){ + res[index++] = matrix[endX][i]; + } + // 向上 ⬆ y不变x变 + for(int i = endX; i > startX; i--){ + res[index++] = matrix[i][startY]; + } + + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/23. \346\225\260\347\273\204\344\270\255\345\207\272\347\216\260\346\254\241\346\225\260\350\266\205\350\277\207\344\270\200\345\215\212\347\232\204\346\225\260\345\255\227.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/23. \346\225\260\347\273\204\344\270\255\345\207\272\347\216\260\346\254\241\346\225\260\350\266\205\350\277\207\344\270\200\345\215\212\347\232\204\346\225\260\345\255\227.md" new file mode 100644 index 0000000..c00ea51 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/23. \346\225\260\347\273\204\344\270\255\345\207\272\347\216\260\346\254\241\346\225\260\350\266\205\350\277\207\344\270\200\345\215\212\347\232\204\346\225\260\345\255\227.md" @@ -0,0 +1,43 @@ +### Title +数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 + +假设数组非空,并且一定存在满足条件的数字。 + +假设要求只能使用 O(n) 的时间和额外 O(1) 的空间,该怎么做呢? +### Demo +``` +输入:[1,2,1,1,3] + +输出:1 +``` +### Analysis +如果数组中有一个数字出现的次数超过数组长度的一半, 那这个数的数量一定大于别的数的个数总和。 + + +### Code + +```java +class Solution { + public int moreThanHalfNum_Solution(int[] nums) { + if(nums.length == 0){ + return -1; + } + int count = 1; + int val = nums[0]; + for(int i = 0; i< nums.length; i++){ + if(val == nums[i]){ + count ++; + }else{ + count --; + } + + if(count == 0){ + // 如果count为0 , 则val设置为当前值 + val = nums[i]; + count = 1; + } + } + return val; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/24. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/24. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.md" new file mode 100644 index 0000000..944a033 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/24. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.md" @@ -0,0 +1,41 @@ +### Title +输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。 + +如果是则返回true,否则返回false。 + +假设输入的数组的任意两个数字都互不相同。 +### Demo +``` +输入:[4, 8, 6, 12, 16, 14, 10] + +输出:true +``` +### Analysis + +后序遍历的特点是根在最后, 拿这个为突破点, 因为是搜索树, 所以自然有序, 拿到不符合有序的交界节点, 则其前应为小数升序, 其后为大数升序, 不满足则返回false +### Code + +```java +class Solution { + public boolean verifySequenceOfBST(int [] sequence) { + // 迭代, 拿到最后一个元素 即根节点 + return dfs(sequence, 0 , sequence.length-1); + } + public boolean dfs(int [] sequence, int l, int r){ + if(l >= r){return true;} + // 拿到最后一个元素 + int root = sequence[r]; + // 遍历前面比root小的数, 拿到一个k + int k = l; + while( k < r && sequence[k] getLeastNumbers_Solution(int [] input, int k) { + PriorityQueue q = new PriorityQueue<>((o1,o2)->o2-o1); + for(int i = 0; i< input.length; i++){ + q.offer(input[i]); + if(q.size() > k){ + q.poll(); + } + } + var res = new ArrayList<>(q); + Collections.sort(res); + return res; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/26. \350\277\236\347\273\255\345\255\220\346\225\260\347\273\204\347\232\204\346\234\200\345\244\247\345\222\214.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/26. \350\277\236\347\273\255\345\255\220\346\225\260\347\273\204\347\232\204\346\234\200\345\244\247\345\222\214.md" new file mode 100644 index 0000000..ab3e1ba --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/26. \350\277\236\347\273\255\345\255\220\346\225\260\347\273\204\347\232\204\346\234\200\345\244\247\345\222\214.md" @@ -0,0 +1,40 @@ +### Title +输入一个 非空 整型数组,数组里的数可能为正,也可能为负。 + +数组中一个或连续的多个整数组成一个子数组。 + +求所有子数组的和的最大值。 + +要求时间复杂度为O(n)。 +### Demo +``` +输入:[1, -2, 3, 10, -4, 7, 2, -5] + +输出:18 +``` +### Analysis +如果sum和都比当前数字小, 则sum改为当前值 + +如果sum比res大, 则res为sum值 +### Code + +```java +class Solution { + public int maxSubArray(int[] nums) { + int sum = nums[0]; + int res = nums[0]; + for(int i = 1; i< nums.length; i++){ + sum += nums[i]; + // 如果sum和都比当前数字小, 则sum为当前值 + if(sum < nums[i]){ + sum = nums[i]; + } + // 如果sum比res大, 则res为sum值 + if(sum > res){ + res = sum; + } + } + return res; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/27. \344\272\214\345\217\211\346\240\221\344\270\255\345\222\214\344\270\272\346\237\220\344\270\200\345\200\274\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/27. \344\272\214\345\217\211\346\240\221\344\270\255\345\222\214\344\270\272\346\237\220\344\270\200\345\200\274\347\232\204\350\267\257\345\276\204.md" new file mode 100644 index 0000000..3de0d22 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/27. \344\272\214\345\217\211\346\240\221\344\270\255\345\222\214\344\270\272\346\237\220\344\270\200\345\200\274\347\232\204\350\267\257\345\276\204.md" @@ -0,0 +1,45 @@ +### Title +输入一棵二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。 + +从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。 +### Demo +``` +给出二叉树如下所示,并给出num=22。 + 5 + / \ + 4 6 + / / \ + 12 13 6 + / \ / \ +9 1 5 1 + +输出:[[5,4,12,1],[5,6,6,5]] +``` +### Analysis + +### Code + +```java +class Solution { + private List> res = new ArrayList<>(); + public List> findPath(TreeNode root, int sum) { + dfs(root, sum, new ArrayList<>()); + return res; + } + + private void dfs(TreeNode root, int sum, List path){ + if(root == null){ + return; + } + path.add(root.val); + sum -= root.val; + if(sum == 0 && root.left == null && root.right == null){ + res.add(new ArrayList<>(path)); + }else{ + dfs(root.left, sum, path); + dfs(root.right, sum, path); + } + path.remove( path.size() -1); + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/28. \344\272\214\345\217\211\346\240\221\347\232\204\346\267\261\345\272\246.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/28. \344\272\214\345\217\211\346\240\221\347\232\204\346\267\261\345\272\246.md" new file mode 100644 index 0000000..3fa7955 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/28. \344\272\214\345\217\211\346\240\221\347\232\204\346\267\261\345\272\246.md" @@ -0,0 +1,34 @@ +### Title + +输入一棵二叉树的根结点,求该树的深度。 + +从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 + +### Demo +``` +输入:二叉树[8, 12, 2, null, null, 6, 4, null, null, null, null]如下图所示: + 8 + / \ + 12 2 + / \ + 6 4 + +输出:3 +``` +### Analysis +递归基础题 + +### Code + +```java +class Solution { + public int treeDepth(TreeNode root) { + if(root == null){ + return 0; + } + int left = treeDepth(root.left); + int right = treeDepth(root.right); + return left>right?left+1:right+1; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/29. \345\255\227\347\254\246\344\270\262\344\270\255\347\254\254\344\270\200\344\270\252\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\345\255\227\347\254\246.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/29. \345\255\227\347\254\246\344\270\262\344\270\255\347\254\254\344\270\200\344\270\252\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\345\255\227\347\254\246.md" new file mode 100644 index 0000000..3c4ea55 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/29. \345\255\227\347\254\246\344\270\262\344\270\255\347\254\254\344\270\200\344\270\252\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\345\255\227\347\254\246.md" @@ -0,0 +1,43 @@ +### Title + +在字符串中找出第一个只出现一次的字符。 + +如输入"abaccdeff",则输出b。 + +如果字符串中不存在只出现一次的字符,返回#字符。 + +### Demo +``` +输入:"abaccdeff" + +输出:'b' +``` +### Analysis +利用map, 将字符作为key, 出现次数作为value, 返回第一个value为1的key。 + +### Code + +```java +class Solution { + public char firstNotRepeatingChar(String s) { + char chars [] = s.toCharArray(); + Map map = new LinkedHashMap<>(); + for(int i = 0; i< s.length(); i++){ + int count = 0; + if(map.containsKey(chars[i])){ + count = map.get(chars[i]); + } + // System.out.println(chars[i] + " " + (count+1) ); + map.put(chars[i], ++count); + } + + for(Map.Entry entry:map.entrySet()){ + // System.out.println(entry.getKey() + " " + entry.getValue() ); + if(entry.getValue() == 1){ + return entry.getKey(); + } + } + return '#'; + } +} +``` 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/1. JVM/JVM-ZGC.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-ZGC.md" new file mode 100644 index 0000000..1e45276 --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-ZGC.md" @@ -0,0 +1,113 @@ +### 低延迟垃圾收集器 + +> Shenandoah和ZGC为什么被称为低延迟GC,因为它几乎整个工作过程全部都是并发的,只有初始标记、最终标记这些阶段有短暂的停顿,这部分停顿的时间基本上是固定的,与堆的容量、堆中对象的数量没有正比例关系。实际上,它们都可以在任意可管理的(譬如现在ZGC只能管理4TB以内的堆)堆容量下,实现垃圾收集的停顿都不超过十毫秒这种以前听起来是天方夜谭、匪夷所思的目标。这两款目前仍处于实验状态的收集器,被官方命名为“低延迟垃圾收集器”。 + +衡量垃圾收集器的三项最重要的指标是:内存占用(Footprint)、吞吐量(Throughput)和延迟(Latency),三者共同构成了一个“不可能三角”。 + +### 1\. Shenandoah垃圾回收器 + +> 比起稍后要介绍的有着Oracle正朔血统的ZGC,Shenandoah反而更像是G1的下一代继承者。使用转发指针(Forwarding Pointer,也常被称为Indirection Pointer)来实现对象移动与用户程序并发的一种解决方案。 + +##### 那Shenandoah相比起G1又有什么改进呢? + +虽然Shenandoah也是使用基于Region的堆内存布局,同样有着用于存放大对象的HumongousRegion,默认的回收策略也同样是优先处理回收价值最大的Region……但在管理堆内存方面,它与G1至少有三个明显的不同之处,最重要的当然是支持并发的整理算法,G1的回收阶段是可以多线程并行的,但却不能与用户线程并发,这点作为Shenandoah最核心的功能稍后笔者会着重讲解。其次,Shenandoah(目前)是默认不使用分代收集的,换言之,不会有专门的新生代Region或者老年代Region的存在,没有实现分代,并不是说分代对Shenandoah没有价值,这更多是出于性价比的权衡,基于工作量上的考虑而将其放到优先级较低的位置上。最后,Shenandoah摒弃了在G1中耗费大量内存和计算资源去维护的记忆集,改用名为“连接矩阵”(Connection Matrix)的全局数据结构来记录跨Region的引用关系,降低了处理跨代指针时的记忆集维护消耗,也降低了伪共享问题。 + +##### Shenandoah收集器的工作过程大致可以划分为以下九个阶段 + +1. 初始标记 这个阶段仍是“Stop The World”的,但停顿时间与堆大小无关,只与GC Roots的数量相关 + +2. 并发标记 与G1一样,遍历对象图,标记出全部可达的对象,这个阶段是与用户线程一起并发的,时间长短取决于堆中存活对象的数量以及对象图的结构复杂程度。 + +3. 最终标记 与G1一样,处理剩余的SATB扫描,并在这个阶段统计出回收价值最高的Region,将这些Region构成一组回收集(Collection Set)。最终标记阶段也会有一小段短暂的停顿。 + +4. 并发清理 这个阶段用于清理那些整个区域内连一个存活对象都没有找到的Region + +5. 并发回收 在这个阶段,Shenandoah要把回收集里面的存活对象先复制一份到其他未被使用的Region之中。复制对象这件事情如果将用户线程冻结起来再做那是相当简单的,但如果两者必须要同时并发进行的话,就变得复杂起来了。其困难点是在移动对象的同时,用户线程仍然可能不停对被移动的对象进行读写访问,移动对象是一次性的行为,但移动之后整个内存中所有指向该对象的引用都还是旧对象的地址,这是很难一瞬间全部改变过来的。对于并发回收阶段遇到的这些困难,Shenandoah将会通过读屏障和被称为“Brooks Pointers”的转发指针来解决(讲解完Shenandoah整个工作过程之后笔者还要再回头介绍它)。并发回收阶段运行的时间长短取决于回收集的大小 + +6. 初始引用更新 并发回收阶段复制对象结束后,还需要把堆中所有指向旧对象的引用修正到复制后的新地址,这个操作称为引用更新。 + +7. 并发引用更新 真正开始进行引用更新操作,这个阶段是与用户线程一起并发的,时间长短取决于内存中涉及的引用数量的多少。并发引用更新与并发标记不同,它不再需要沿着对象图来搜索,只需要按照内存物理地址的顺序,线性地搜索出引用类型,把旧值改为新值即可。 + +8. 最终引用更新 解决了堆中的引用更新后,还要修正存在于GC Roots中的引用。这个阶段是Shenandoah的最后一次停顿,停顿时间只与GC Roots的数量相关。 + +9. 并发清理 经过并发回收和引用更新之后,整个回收集中所有的Region已再无存活对象,最后再调用一次并发清理过程来回收这些Region的内存空间,供以后新对象分配使用。 + +##### Shenandoah收集器性能 + +2016年做该测试时的Shenandoah并没有完全达成预定目标,停顿时间比其他几款收集器确实有了质的飞跃,但也并未实现最大停顿时间控制在十毫秒以内的目标,而吞吐量方面则出现了很明显的下降。 + +![](https://user-gold-cdn.xitu.io/2020/1/11/16f931defeabda94?w=1240&h=262&f=png&s=110126) + +Shenandoah的性能在日益改善,逐步接近“Low-Pause”的目标。此外,RedHat也积极拓展Shenandoah的使用范围,将其Backport到JDK 11甚至是JDK 8之上,让更多不方便升级JDK版本的应用也能够享受到垃圾收集器技术发展的最前沿成果。 + +### 2. ZGC收集器 + +> ZGC是一款在JDK 11中新加入的具有实验性质[插图]的低延迟垃圾收集器,是由Oracle公司研发的。2018年Oracle创建了JEP 333将ZGC提交给OpenJDK,推动其进入OpenJDK 11的发布清单之中。 + +ZGC和Shenandoah的目标是高度相似的,都希望在尽可能对吞吐量影响不太大的前提下,实现在任意堆内存大小下都可以把垃圾收集的停顿时间限制在十毫秒以内的低延迟。但是ZGC和Shenandoah的实现思路又是差异显著的。 + +ZGC收集器是一款基于Region内存布局的,(暂时)不设分代的,使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-整理算法的,以低延迟为首要目标的一款垃圾收集器。 + +##### 并发整理算法的实现 + +> Shenandoah使用转发指针和读屏障来实现并发整理,ZGC虽然同样用到了读屏障,但用的却是一条与Shenandoah完全不同,更加复杂精巧的解题思路。 + +ZGC收集器有一个标志性的设计是它采用的染色指针技术(Colored Pointer),直接把标记信息记在引用对象的指针上。指针对于计算机来讲,它也是一个信息的载体,但是目前而言,内存中的理论可访问信息是远大于实际需求的,尽管Linux高18位不能用来寻址,但剩余的46位也足以满足需求,所以ZGC团队就将指针信息载体进行染色,将其高4位用来存储四个记号信息,通过这些标志位,虚拟机可以直接从指针中看到其引用对象的三色标记状态、是否进入了重分配集(即被移动过)、是否只能通过finalize()方法才能被访问到。 + +由于这些标志位进一步压缩了原本就只有46位的地址空间,也直接导致ZGC能够管理的内存不可以超过4TB(2的42次幂)。 + +![](https://user-gold-cdn.xitu.io/2020/1/11/16f931df02d6c256?w=1073&h=289&f=png&s=115259) + +##### 染色指针的三大优势: + +1. 染色指针可以使得一旦某个Region的存活对象被移走之后,这个Region立即就能够被释放和重用掉,而不必等待整个堆中所有指向该Region的引用都被修正后才能清理。 + +2. 染色指针可以大幅减少在垃圾收集过程中内存屏障的使用数量,设置内存屏障,尤其是写屏障的目的通常是为了记录对象引用的变动情况,如果将这些信息直接维护在指针中,显然就可以省去一些专门的记录操作。 + +3. 染色指针可以作为一种可扩展的存储结构用来记录更多与对象标记、重定位过程相关的数据,以便日后进一步提高性能。 + +##### Java虚拟机作为一个普普通通的进程,这样随意重新定义内存中某些指针的其中几位,操作系统是否支持?处理器是否支持? + +这里面的解决方案要涉及虚拟内存映射技术。把染色指针中的标志位看作是地址的分段符,那只要将这些不同的地址段都映射到同一个物理内存空间,经过多重映射转换后,就可以使用染色指针正常进行寻址了。 + +![image](https://user-gold-cdn.xitu.io/2020/1/11/16f931df01f9c7ed?w=1074&h=446&f=png&s=283314) + +##### ZGC的运作过程大致可划分为以下四个大的阶段 + +1. 并发标记(Concurrent Mark):并发标记是遍历对象图做可达性分析的阶段,与G1、Shenandoah不同的是,ZGC的标记是在指针上而不是在对象上进行的,标记阶段会更新染色指针中的Marked 0、Marked 1标志位。 + +2. 并发预备重分配(Concurrent Prepare for Relocate):这个阶段需要根据特定的查询条件统计得出本次收集过程要清理哪些Region,ZGC划分Region的目的并非为了像G1那样做收益优先的增量回收,而实用范围更大的扫描成本换取省去G1中记忆集的维护成本。此外,在JDK12的ZGC中开始支持的类卸载以及弱引用的处理,也是在这个阶段中完成的。 + +3. 并发重分配(Concurrent Relocate):重分配是ZGC执行过程中的核心阶段,这个过程要把重分配集中的存活对象复制到新的Region上,并为重分配集中的每个Region维护一个转发表(Forward Table),记录从旧对象到新对象的转向关系。得益于染色指针的支持,ZGC收集器能仅从引用上就明确得知一个对象是否处于重分配集之中,如果用户线程此时并发访问了位于重分配集中的对象,这次访问将会被预置的内存屏障所截获,然后立即根据Region上的转发表记录将访问转发到新复制的对象上,并同时修正更新该引用的值,使其直接指向新对象,ZGC将这种行为称为指针的“自愈”(Self-Healing)能力。 + +4. 并发重映射(Concurrent Remap):重映射所做的就是修正整个堆中指向重分配集中旧对象的所有引用。ZGC的并发重映射并不是一个必须要“迫切”去完成的任务,因为前面提到ZGC有"自愈"能力,最坏也就多跳转一层,这时候,一旦所有指针都被修正之后,原来记录新旧对象关系的转发表就可以释放掉了。 + +##### ZGC收集器性能 + +1. ZGC的设计理念是迄今垃圾收集器研究的最前沿成果,可是,必定要有优有劣才会称作权衡,ZGC的这种选择[插图]也限制了它能承受的对象分配速率不会太高,目前唯一的办法就是尽可能地增加堆容量大小,获得更多喘息的时间。 + +2. ZGC还有一个常在技术资料上被提及的优点是支持“NUMA-Aware”非统一内存访问架构的内存分配。由于摩尔定律逐渐失效,现代处理器因频率发展受限转而向多核方向发展,这就造成了如果要访问被其他处理器核心管理的内存,就必须通过Inter-Connect通道来完成,这要比访问处理器的本地内存慢得多,ZGC收集器会优先尝试在请求线程当前所处的处理器的本地内存上分配对象,以保证高效内存访问。 + +3. 在ZGC的“弱项”吞吐量方面,以低延迟为首要目标的ZGC已经达到了以高吞吐量为目标Parallel Scavenge的99%,直接超越了G1。 + +4. ZGC均能毫不费劲地控制在十毫秒之内 + +![image](https://user-gold-cdn.xitu.io/2020/1/11/16f931df3d56bb0a?w=1063&h=708&f=png&s=195145) + +![image](https://user-gold-cdn.xitu.io/2020/1/11/16f931dfb1f140d1?w=1068&h=555&f=png&s=163271) + + +声明:本问参考书籍《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版) 》,这个版本刚上市两个月,新增了一些新的GC和内存分配策略的知识,大伙有兴趣可以看看。若有侵权,请联系删除,谢谢! + + +--- + +--- +如果你喜欢我的文章,那麻烦请关注我的公众号,该公众号还处于初始阶段,谢谢大家的支持。 +![](https://user-gold-cdn.xitu.io/2019/12/3/16ecadc23e4f9bc3?w=258&h=258&f=jpeg&s=26754) +关注公众号,回复`java架构`获取架构视频资源(后期还会分享不同的优质资源噢)。回复`找对象`可以拉你进IT单身交友群噢。 + +想看往期文章, 请点击我的GitHub地址: https://github.com/fantj2016/java-reader + +--- +--- \ 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-\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/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" "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" new file mode 100644 index 0000000..544e93e --- /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/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" @@ -0,0 +1,244 @@ +>带你快速了解这几个同步信号工具。 + +### 1. CountDownLatch +>用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。 + +案例:项目经理改个需求,当小王、小李、小赵都相继改完代码后,项目经理才进行审查。 + +##### 1.1 Boss.java +``` +public class Boss implements Runnable{ + private CountDownLatch countDownLatch; + + public Boss(CountDownLatch countDownLatch) { + this.countDownLatch = countDownLatch; + } + + @Override + public void run() { + System.out.println("Boss: 需求改动!速度更新!"); + try { + countDownLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("Boss: 都改完了是吗,我看看阿!"); + } +} +``` +##### 1.2 Worker.java +``` +public class Worker implements Runnable{ + private CountDownLatch countDownLatch; + private String name; + + public Worker(CountDownLatch countDownLatch, String name) { + this.countDownLatch = countDownLatch; + this.name = name; + } + + @Override + public void run() { + System.out.println(name + ": 开始改代码"); + try { + Thread.sleep((long) (Math.random()*10000)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println(name + ": 代码改完了!"); + countDownLatch.countDown(); + } +} +``` + +##### 1.3 Main +``` +public class Main { + public static void main(String[] args) { + // 当调用三次countDown时释放所有等待线程 + CountDownLatch countDownLatch = new CountDownLatch(3); + ExecutorService executorService = Executors.newCachedThreadPool(); + Boss boss = new Boss(countDownLatch); + Worker worker1 = new Worker(countDownLatch, "小王"); + Worker worker2 = new Worker(countDownLatch, "小李"); + Worker worker3 = new Worker(countDownLatch, "小赵"); + executorService.execute(boss); + executorService.execute(worker1); + executorService.execute(worker2); + executorService.execute(worker3); + executorService.shutdown(); + } +} +``` + + +结果: +``` +Boss: 需求改动!速度更新! +小王: 开始改代码 +小李: 开始改代码 +小赵: 开始改代码 +小王: 代码改完了! +小李: 代码改完了! +小赵: 代码改完了! +Boss: 都改完了是吗,我看看阿! +``` + +### 2. CyclicBarrier +>一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。 + +案例:老板叫开会,等所有人都来了后就开始开会,先来的人先wait。 + +#### 2.1 Boss +``` +public class Boss implements Runnable{ + private CyclicBarrier cyclicBarrier; + + Boss(CyclicBarrier cyclicBarrier) { + this.cyclicBarrier = cyclicBarrier; + } + + @Override + public void run() { + System.out.println("Boss: 开会!"); + } +} +``` + +#### 2.2 Worker +``` +public class Worker implements Runnable{ + private CyclicBarrier cyclicBarrier; + private String name; + + public Worker(CyclicBarrier cyclicBarrier, String name) { + this.cyclicBarrier = cyclicBarrier; + this.name = name; + } + + @Override + public void run() { + try { + System.out.println(name + ": 我正在路上"); + Thread.sleep((long) (Math.random()*10000)); + System.out.println(name + ": 我到了!"); + cyclicBarrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + e.printStackTrace(); + } + } +} +``` + +#### 2.3 Main +``` +public class Main { + public static void main(String[] args) { + // 当三个线程处于barrier(wait)状态的时候,同时开始执行后续任务。 + CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () ->System.out.println("Boss: 都到了吗?那我来说两句...balabala...")); + ExecutorService executorService = Executors.newFixedThreadPool(4); + Boss boss = new Boss(cyclicBarrier); + Worker worker1 = new Worker(cyclicBarrier, "小王"); + Worker worker2 = new Worker(cyclicBarrier, "小李"); + Worker worker3 = new Worker(cyclicBarrier, "小赵"); + + executorService.execute(boss); + executorService.execute(worker1); + executorService.execute(worker2); + executorService.execute(worker3); + executorService.shutdown(); + } +} +``` +我设置当三个线程处于barrier(wait)状态的时候,同时开始执行后续任务。 +``` +Boss: 开会! +小王: 我正在路上 +小李: 我正在路上 +小赵: 我正在路上 +小王: 我到了! +小李: 我到了! +小赵: 我到了! +Boss: 都到了吗?那我来说两句...balabala... +``` + + +### 3. Semaphore +>一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。 + +通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。(类似网络带宽) + +案例:老板发红包,办公室只能站下三个人,员工排队去领。 + + +#### 3.1 Boss +``` +public class Boss implements Runnable{ + @Override + public void run() { + System.out.println("排队领红包!我身边最多围三个人!"); + } +} +``` +#### 3.2 Worker +``` +public class Worker implements Runnable{ + private Semaphore semaphore; + private String name; + + Worker(Semaphore semaphore, String name) { + this.semaphore = semaphore; + this.name = name; + } + + @Override + public void run() { + try { + semaphore.acquire(); + System.out.println(name + ": 我正在老板面前等红包"); + Thread.sleep((long) (Math.random()*10000)); + System.out.println(name +": 我拿到红包了"); + semaphore.release(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} +``` +#### 3.3 Main +``` +public class Main { + public static void main(String[] args) { + // 设置三个信号量,只有拿到信号才能执行后续任务 + Semaphore semaphore = new Semaphore(3); + ExecutorService executorService = Executors.newCachedThreadPool(); + Worker worker1 = new Worker(semaphore, "小王"); + Worker worker2 = new Worker(semaphore, "小Li"); + Worker worker3 = new Worker(semaphore, "小赵"); + Worker worker4 = new Worker(semaphore, "小孙"); + Worker worker5 = new Worker(semaphore, "小周"); + Boss boss = new Boss(); + + executorService.execute(boss); + executorService.execute(worker1); + executorService.execute(worker2); + executorService.execute(worker3); + executorService.execute(worker4); + executorService.execute(worker5); + executorService.shutdown(); + } +} +``` +``` +排队领红包!我身边最多围三个人! +小王: 我正在老板面前等红包 +小Li: 我正在老板面前等红包 +小孙: 我正在老板面前等红包 +小Li: 我拿到红包了 +小赵: 我正在老板面前等红包 +小孙: 我拿到红包了 +小周: 我正在老板面前等红包 +小周: 我拿到红包了 +小王: 我拿到红包了 +小赵: 我拿到红包了 +``` \ No newline at end of file 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.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/ACL.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/ACL.md" new file mode 100644 index 0000000..3f4f44b --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/ACL.md" @@ -0,0 +1,88 @@ +>acl全称是访问控制列表,如果涉及到对某个用户的权限设置,则用ACL更方便快捷。 + +注意:针对某个用户设置的权限是覆盖other用户权限的。 + +比如你给jiao用户设置了---权限,系统默认other权限是rw-,但是该文件对jiao用户所执行的权限是---。 + +#### 查看文件权限 +`getfacl xxx` + +``` + +``` + + +#### 设置文件/文件夹权限 + +对用户设置: + +`setfacl -m u:jiao:rw- fantj.file ` +``` +-m modify修改文件用户权限 +-x 取消用户权限 +``` +``` +[root@localhost fantj]# getfacl fantj.file +# file: fantj.file +# owner: root +# group: root +user::rw- +user:jiao:rw- +group::r-- +mask::rw- +other::r-- +``` + + + +对组设置: + +与对用户设置一样`setfacl -m g:jiao:rw- fantj.file` +就是把u改成g + +#### 取消文件权限 +`setfacl -x u:jiao fantj.file ` + +``` +[root@localhost fantj]# setfacl -x u:jiao fantj.file +[root@localhost fantj]# getfacl fantj.file +# file: fantj.file +# owner: root +# group: root +user::rw- +group::r-- +mask::r-- +other::r-- +``` + +对组设置: +与对用户设置一样,将u变成g即可。 + + +#### 设置文件/目录对用户的默认权限 +>有的时候,我们需要对某个目录下的所有新建东西给某个用户权限,那我们就需要用这个命令。 +`setfacl -m d:u:jiao:rwx /fantj` + +d的意思是default,不管哪个用户在fantj里新创建的文件或者目录,对jiao都是rwx权限 + +``` +[root@localhost fantj]# setfacl -m d:u:jiao:rwx /home/fantj/ + +[root@localhost fantj]# touch newfile.txt +[root@localhost fantj]# getfacl newfile.txt +# file: newfile.txt +# owner: root +# group: root +user::rw- +user:jiao:rwx #effective:rw- +group::r-x #effective:r-- +mask::rw- +other::r-- +``` +可以看到,user里jiao有rwx权限。 + +#### 取消文件/目录对用户的默认权限 + +`setfacl -x d:u:jiao /home/fantj` + +注意:该操作不会对已经设置权限的文件有影响,只会对新的文件有影响。 \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/DNS.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/DNS.md" new file mode 100644 index 0000000..948b2f7 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/DNS.md" @@ -0,0 +1,187 @@ +>在以前的版本,我们都用bind来设置DNS,在Rhel7中,我们用unbound来配置。 + +### DHCP&MAC地址绑定 +>为客户端分配必要的网络信息。 + + +###### 工作流程 +>客户端新生成后会在局域网发广播,DHCP收到后从地址池里拿出ip作回应。然后客户端再发广播拿到的ip地址,然后DHCP回应放出ip。 + + +##### 安装DHCP服务器 + +1. `yum install dhcp -y` +2. 配置文件`/etc/dhcp/dhcpd.conf`,发现里面是空的,所以找个模版`rpm -ql dhcp |grep example` +``` +[root@fantj dhcp]# rpm -ql dhcp |grep example +/usr/share/doc/dhcp-4.2.5/dhcpd.conf.example +/usr/share/doc/dhcp-4.2.5/dhcpd6.conf.example +``` + +复制内容:`cat /usr/share/doc/dhcp-4.2.5/dhcpd.conf.example > /etc/dhcp/dhcpd.conf` + +dhcpd.conf文件配置解析 + +``` +# dhcpd.conf +option domain-name "fantj.cc"; 域名 +option domain-name-servers 192.168.27.3; 编址服务器 + +default-lease-time 600; 租约:租期 +max-lease-time 7200; 租约:最大续期 + +#ddns-update-style none; 根据客户端ip地址的改变来动态dns +log-facility local7; +subnet 192.168.27.0(作用域) netmask 255.255.255.0 { + range 192.168.27.169 192.168.27.250;定义地址池 + option domain-name "internal.example.org"; + option routers 192.168.27.2;网关地址 + option broadcast-address 192.168.27.255;广播地址 +} subnet配置网络信息,一个网段一个作用域, + + +绑定mac地址 + +:服务器在运行时,如果没有给服务器配静态地址, +但是我们希望每次获取的地址是一样的, +就需要我们对mac地址做绑定。 +host fantasia { + hardware ethernet 08:00:07:26:c0:a5; mac地址(可通过ip a 查看) + fixed-address 192.168.27.88;分配的地址不一定要在上面的地址池里。 +} + +``` + +3. 开启服务:`systemctl start dhcpd` +4. 开启防火墙:`firewall-cmd --add-service=dhcp`允许dhcp服务。 + + +### DNS +> + + +`www.baidu.com `是主机名 + +`baidu.com ` 是域名 + +`mail.baidu.com` 是`baidu.com`域中的主机 + + +###### DNS解析路径追踪` + +`dig +trace www.baidu.com` + +``` +[root@fantj ~]# dig +trace www.baidu.com + +; <<>> DiG 9.9.4-RedHat-9.9.4-14.el7 <<>> +trace www.baidu.com +;; global options: +cmd +. 221744 IN NS m.root-servers.net. +. 221744 IN NS b.root-servers.net. +. 221744 IN NS c.root-servers.net. +. 221744 IN NS d.root-servers.net. +. 221744 IN NS e.root-servers.net. +. 221744 IN NS f.root-servers.net. +. 221744 IN NS g.root-servers.net. +. 221744 IN NS h.root-servers.net. +. 221744 IN NS i.root-servers.net. +. 221744 IN NS j.root-servers.net. +. 221744 IN NS a.root-servers.net. +. 221744 IN NS k.root-servers.net. +. 221744 IN NS l.root-servers.net. + +上面是13个.域服务器 + + +com. 172800 IN NS a.gtld-servers.net. +com. 172800 IN NS b.gtld-servers.net. +com. 172800 IN NS c.gtld-servers.net. +com. 172800 IN NS d.gtld-servers.net. +com. 172800 IN NS e.gtld-servers.net. +com. 172800 IN NS f.gtld-servers.net. +com. 172800 IN NS g.gtld-servers.net. +com. 172800 IN NS h.gtld-servers.net. +com. 172800 IN NS i.gtld-servers.net. +com. 172800 IN NS j.gtld-servers.net. +com. 172800 IN NS k.gtld-servers.net. +com. 172800 IN NS l.gtld-servers.net. +com. 172800 IN NS m.gtld-servers.net. + + +com域服务器 + + +baidu.com. 172800 IN NS dns.baidu.com. +baidu.com. 172800 IN NS ns2.baidu.com. +baidu.com. 172800 IN NS ns3.baidu.com. +baidu.com. 172800 IN NS ns4.baidu.com. +baidu.com. 172800 IN NS ns7.baidu.com. + + +上面的是百度域服务器 + + +www.baidu.com. 1200 IN CNAME www.a.shifen.com. +a.shifen.com. 1200 IN NS ns2.a.shifen.com. +a.shifen.com. 1200 IN NS ns3.a.shifen.com. +a.shifen.com. 1200 IN NS ns1.a.shifen.com. +a.shifen.com. 1200 IN NS ns4.a.shifen.com. +a.shifen.com. 1200 IN NS ns5.a.shifen.com. + + +上面是baidu.com别名以后a.shifen.com的dns服务器 +``` + + +###### 1. 安装unbound + +`yum install unbound -y` + + +###### 2. 开启dns服务 +>默认53端口,支持tcp协议和udp协议。 + +`firewall-cmd --add-service=dns` + +``` +[root@fantj ~]# firewall-cmd --add-service=dns +success +``` + + +###### 3. 配置DNS + +主配置文件: +`/etc/unbound/unbound.conf` + +``` +interface 0.0.0.0 监听接口 +# 访问控制 +access-control: 127.0.0.0/8 allow +access-control: 192.168.0.0/24 allow +# 区域配置,本地区域配置主要分为local-zone、local-data 和 local-data-ptr 。这里假设配置一个域名为rhce.com 的域名,其A记录、MX记录配置结果如下 + +local-zone: "rhce.com" static +local-data: "www.rhce.com. IN A 192.168.0.103" +local-data-ptr: "192.168.0.103 www.rhce.com" +local-data: "rhce.com. IN MX 5 mail.rhce.com" +local-data: "mail.rhce.com. IN A 192.168.0.103" + +#转发配置,指定上一级DNS转发 +forward-zone: + name: . + forward-addr: 114.114.114.114 + forward-addr: 8.8.8.8 + +``` +###### 4. 重启服务 +`systemctl restart unbound` + +`systemctl enable unbound` +###### 5. 防火墙配置 + +`firewall-cmd --permanent --add-service=dns` + + + + diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/LDAP.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/LDAP.md" new file mode 100644 index 0000000..e69de29 diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/LVM.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/LVM.md" new file mode 100644 index 0000000..14d2fdd --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/LVM.md" @@ -0,0 +1,309 @@ +>逻辑卷优点?可以动态扩展内存空间。 + +传统分区缺点:如果需要更改分区大小需要格式化分区然后重新分区。 + +VG:卷组 + +LV:逻辑卷 + +PV:物理卷 + +关系:物理卷组成卷组,然后在卷组上划分逻辑卷(PV->VG->LV) + +#### 1. 将物理分区初始化为物理卷 + +1. `pvscan或者pvs` 查看物理卷 + +2. 开始转换分区类型 + +注意:不能直接更改扩展分区类型为8e(Lvm分区id) +``` +You cannot change a partition into an extended one or vice versa. +Delete it first. +``` +所以我们如果想将扩展分区改为LVM分区则先要删除扩展分区。 + +``` +命令(输入 m 获取帮助):d +分区号 (1-6,默认 6):4 +分区 4 已删除 + +命令(输入 m 获取帮助):p + +磁盘 /dev/sda:21.5 GB, 21474836480 字节,41943040 个扇区 +Units = 扇区 of 1 * 512 = 512 bytes +扇区大小(逻辑/物理):512 字节 / 512 字节 +I/O 大小(最小/最佳):512 字节 / 512 字节 +磁盘标签类型:dos +磁盘标识符:0x00092946 + + 设备 Boot Start End Blocks Id System +/dev/sda1 * 2048 1026047 512000 83 Linux +/dev/sda2 1026048 2050047 512000 83 Linux +/dev/sda3 2050048 3074047 512000 83 Linux + +命令(输入 m 获取帮助):n +Partition type: + p primary (3 primary, 0 extended, 1 free) + e extended +Select (default e): p +已选择分区 4 +起始 扇区 (3074048-41943039,默认为 3074048): +将使用默认值 3074048 +Last 扇区, +扇区 or +size{K,M,G} (3074048-41943039,默认为 41943039): +将使用默认值 41943039 +分区 4 已设置为 Linux 类型,大小设为 18.5 GiB + +命令(输入 m 获取帮助):t +分区号 (1-4,默认 4):3 +Hex 代码(输入 L 列出所有代码):8e +已将分区“Linux”的类型更改为“Linux LVM” +``` +同理,将sda4也做类型转换 +``` +命令(输入 m 获取帮助):p + +磁盘 /dev/sda:21.5 GB, 21474836480 字节,41943040 个扇区 +Units = 扇区 of 1 * 512 = 512 bytes +扇区大小(逻辑/物理):512 字节 / 512 字节 +I/O 大小(最小/最佳):512 字节 / 512 字节 +磁盘标签类型:dos +磁盘标识符:0x00092946 + + 设备 Boot Start End Blocks Id System +/dev/sda1 * 2048 1026047 512000 83 Linux +/dev/sda2 1026048 2050047 512000 83 Linux +/dev/sda3 2050048 3074047 512000 8e Linux LVM +/dev/sda4 3074048 41943039 19434496 8e Linux LVM +``` + +然后执行`pvcreate /dev/sda{3,4}`即可创建物理卷,然后`pvs`查看。 + +``` +[root@localhost home]# pvcreate /dev/sda{5,6} + Physical volume "/dev/sda5" successfully created + Physical volume "/dev/sda6" successfully created +[root@localhost home]# pvs + PV VG Fmt Attr PSize PFree + /dev/sda5 lvm2 a-- 1.00g 1.00g + /dev/sda6 lvm2 a-- 2.00g 2.00g +``` + + +#### 2. 将物理卷创建为卷组 +`vgcreate vg0 /dev/sda{3,4}`然后`vgscan`查看卷组,然后再执行`pvs`可以看到VG列有参数了,那就是卷组名vg0. + +``` +[root@localhost home]# vgcreate vg0 /dev/sda{5,6} + Volume group "vg0" successfully created +``` +``` +[root@localhost home]# vgs + VG #PV #LV #SN Attr VSize VFree + vg0 2 0 0 wz--n- 2.99g 2.99g +``` +``` +[root@localhost home]# pvs + PV VG Fmt Attr PSize PFree + /dev/sda5 vg0 lvm2 a-- 1020.00m 1020.00m + /dev/sda6 vg0 lvm2 a-- 2.00g 2.00g +``` +###### 查看所有卷组信息 +`vgdisplay` +``` +[root@localhost home]# vgdisplay + --- Volume group --- + VG Name vg0 + System ID + Format lvm2 + Metadata Areas 2 + Metadata Sequence No 1 + VG Access read/write + VG Status resizable + MAX LV 0 + Cur LV 0 + Open LV 0 + Max PV 0 + Cur PV 2 + Act PV 2 + VG Size 2.99 GiB + PE Size 4.00 MiB + Total PE 766 + Alloc PE / Size 0 / 0 + Free PE / Size 766 / 2.99 GiB + VG UUID FGEHCq-RI5V-DXOX-t9Io-b0v5-gEmx-1r08c3 + +``` +其中,PE(物理扩展):组成卷组的最小单位。默认是4M +###### 创建PE大小为8的卷组 +>什么是PE,PE是卷组的最小单位,可以理解成组成卷组的元素大小。默认是4M.可以通过`vgdisplay`命令查看 + +修改PE大小需要使用命令 +`vgcreate -s 8 vg0 /dev/sda{3,4}` + +其中,-s 8 的意思指定是PE为8MB + +###### 查看某个卷组信息 +`vgdisplay vgname` + +###### 扩展卷组 +`vgextend vg0 /dev/sda5`将sda5扩展到vg0卷组,然后在`vgdisplay vg0`查看vg size,会发现,变大了。同理,`pvs`命令结果也会同步的加上sda5的vg所属组。 + +###### 减小卷组 +`vgreduce vg0 /dev/sda5`从vg0卷组中将sda5移除 + + +###### vg重命名 +`vgrename old new` + +###### 删除卷组 +`vgremove vgname` + +###### 导入/导出卷组 +>当我们需要将硬盘组成的LVM转移到另一台机器,并且希望这个LVM还照旧的话,就用此命令。 + +`vgexport`和`vgimport`分别为导出和导入命令 + + + +### 3. 将卷组创建为逻辑卷 +>创建逻辑卷必须要在卷组上面创建。 + +###### 创建逻辑卷 +1. 创建方式一: + +`lvcreate -n lv0 -L 99M vg0` +``` +[root@localhost home]# lvcreate -n lv0 -L 99M vg0 + Rounding up size to full physical extent 100.00 MiB + Logical volume "lv0" created +``` + +-n name名字 + +-L length大小 + +执行该命令后提示创建了一个100M的lv0.为什么呢?因为PE默认为4M,100是99最接近的4的整数倍。也可以用`lvscan`来确认lv0的大小是100. + +2. 创建方式二: + +`lvcreate -n lv1 -l 25 vg0` + +-l 是指多少个PE,25个PE就是100M + +然后`lvscan`查看结果。 +``` +[root@localhost home]# lvcreate -n lv1 -l 25 vg0 + Logical volume "lv1" created +[root@localhost home]# lvs + LV VG Attr LSize Pool Origin Data% Move Log Cpy%Sync Convert + lv0 vg0 -wi-a----- 100.00m + lv1 vg0 -wi-a----- 100.00m +``` +3. 创建方式三: + +`lvcreate -n lv2 -l 100%free vg0` + +将vg0卷组中剩余的空间容量都分给lv2。当然,百分比可以是其他数值。 +###### 扩展逻辑卷 +注意三点: +1. 扩展lv时不需要卸载,即在线扩展 +2. lv可以减小,但是变成xfs(文件系统)不能减小。 +3. ext4是可以减小的。(ext4操作在后面展示) + +如果一开始用的xfs文件系统的话,一开始就需要规划好。 + +操作步骤: + +1. lv0扩展200M + +`lvextend -L +200M /dev/vg0/lv0` + +此时这200M只是空间,没有文件系统。所以在`df -hT`查看lv0大小时,还是显示100M,因为这200M还不是文件系统。 +``` +[root@localhost home]# lvextend -L +200M /dev/vg0/lv0 + Extending logical volume lv0 to 500.00 MiB + Logical volume lv0 successfully resized +[root@localhost home]# df -hT +文件系统 类型 容量 已用 可用 已用% 挂载点 +/dev/sda2 xfs 15G 3.4G 12G 23% / +devtmpfs devtmpfs 906M 0 906M 0% /dev +tmpfs tmpfs 914M 136K 914M 1% /dev/shm +tmpfs tmpfs 914M 8.9M 905M 1% /run +tmpfs tmpfs 914M 0 914M 0% /sys/fs/cgroup +/dev/sda1 xfs 197M 101M 97M 52% /boot +/dev/mapper/vg0-lv0 xfs 297M 16M 282M 6% /home/html +``` +2. 扩展文件系统:执行`xfs_growfs /dev/vg0/lv0`,然后在`df -hT`查看lv0大小,此时就变成了300M + +``` +[root@localhost home]# xfs_growfs /dev/vg0/lv0 +meta-data=/dev/mapper/vg0-lv0 isize=256 agcount=4, agsize=19200 blks + = sectsz=512 attr=2, projid32bit=1 + = crc=0 +data = bsize=4096 blocks=76800, imaxpct=25 + = sunit=0 swidth=0 blks +naming =version 2 bsize=4096 ascii-ci=0 ftype=0 +log =internal bsize=4096 blocks=853, version=2 + = sectsz=512 sunit=0 blks, lazy-count=1 +realtime =none extsz=4096 blocks=0, rtextents=0 +data blocks changed from 76800 to 128000 +[root@localhost home]# df -hT +文件系统 类型 容量 已用 可用 已用% 挂载点 +/dev/sda2 xfs 15G 3.4G 12G 23% / +devtmpfs devtmpfs 906M 0 906M 0% /dev +tmpfs tmpfs 914M 136K 914M 1% /dev/shm +tmpfs tmpfs 914M 8.9M 905M 1% /run +tmpfs tmpfs 914M 0 914M 0% /sys/fs/cgroup +/dev/sda1 xfs 197M 101M 97M 52% /boot +/dev/mapper/vg0-lv0 xfs 497M 16M 482M 4% /home/html +``` + + +###### 删除逻辑卷 +`lvremoce -f /dev/vg0/lv2` + +-f 强制删除 + + +### 4. 挂载逻辑卷 + +`mount /dev/vg0/lv0 /home/fantj` + +在挂载的时候可能会报错: +``` +[root@localhost home]# mount /dev/vg0/lv0 /home/html/ +mount: /dev/mapper/vg0-lv0 写保护,将以只读方式挂载 +mount: 未知的文件系统类型“(null)” +``` +此时需要对lv进行文件系统类型格式化`mkfs.xfs /dev/vg0/lv0`: +``` +[root@localhost home]# mkfs.xfs /dev/vg0/lv0 +meta-data=/dev/vg0/lv0 isize=256 agcount=4, agsize=19200 blks + = sectsz=512 attr=2, projid32bit=1 + = crc=0 +data = bsize=4096 blocks=76800, imaxpct=25 + = sunit=0 swidth=0 blks +naming =version 2 bsize=4096 ascii-ci=0 ftype=0 +log =internal log bsize=4096 blocks=853, version=2 + = sectsz=512 sunit=0 blks, lazy-count=1 +realtime =none extsz=4096 blocks=0, rtextents=0 +``` +然后执行挂载: +``` +[root@localhost home]# mount /dev/vg0/lv0 /home/html/ +[root@localhost home]# df -hT +文件系统 类型 容量 已用 可用 已用% 挂载点 +/dev/sda2 xfs 15G 3.4G 12G 23% / +devtmpfs devtmpfs 906M 0 906M 0% /dev +tmpfs tmpfs 914M 136K 914M 1% /dev/shm +tmpfs tmpfs 914M 8.9M 905M 1% /run +tmpfs tmpfs 914M 0 914M 0% /sys/fs/cgroup +/dev/sda1 xfs 197M 101M 97M 52% /boot +/dev/mapper/vg0-lv0 xfs 297M 16M 282M 6% /home/html +``` + + + +### ext4文件系统的lvm设置 + diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/MariaDB.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/MariaDB.md" new file mode 100644 index 0000000..6382490 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/MariaDB.md" @@ -0,0 +1,159 @@ + + +### 1. 安装 +`yum install mariadb\* -y` + +##### 1.1 启动服务 +>默认3306端口 + +`systemctl restart mariadb` + +`systemctl enable mariadb` + +##### 1.2 登录 + +`mysql` + +查看mysql用户列表: +``` +MariaDB [mysql]> select host,user,password from user; ++-----------+------+----------+ +| host | user | password | ++-----------+------+----------+ +| localhost | root | | +| s168 | root | | +| 127.0.0.1 | root | | +| ::1 | root | | +| localhost | | | +| s168 | | | ++-----------+------+----------+ +6 rows in set (0.00 sec) +``` + +##### 1.3 设置密码 + +###### 方法一: +`mysqladmin -u root -p password 'redhat'` + +没有原密码的话直接回车。此时redhat就是新密码。 + +###### 方法二: + +`mysql_secure_installation` +``` +Set root password? [Y/n] 这里直接回车 +New password: 填写密码 +Re-enter new password: 重复密码 +Password updated successfully! +然后一路回车,一路默认就行 +``` +###### 方法三: +1. `mysql `无登录用户进入cli + +2. 然后`set password=password('redhat');` + +3. 最后`flush privileges;`刷新配置。 + + +### 2. 配置 + + +##### 2.1 主配置文件 +`/etc/my.cnf` + +###### 拒绝远程访问配置 +>对[mysqld]配置块做修改。 +``` +单机运行MySQL使用skip-networking关闭MySQL的TCP/IP连接方式 + +skip-networking + +开启该选项后就不能远程访问MySQL +``` +###### 优化连接速度 +>对[mysqld]配置块做修改。 +``` +使用skip-name-resolve增加远程连接速度 + +skip-name-resolve + +该选项表示禁用DNS解析,属于官方一个系统上的特殊设定不管,链接的的方式是经过hosts或是IP的模式,他都会对DNS做反查,由于反查解析过慢,就会无法应付过量的查询。 +``` + +###### 指定ip访问 +>对[mysqld]配置块做修改。 + +``` +为安全考虑希望指定的IP访问MySQL,可以在配置文件中增加bind-address=IP,前提是关闭skip-networking + +bind-address=192.168.1.100 + +MySQL优化应该按实际情况配置。 +``` + + + +### 强行修改密码 + +##### 修改配置文件 + +1. `/etc/my.cnf`在mysqld下面加`skip-gran-tables`(或者执行命令`mysqld_safe --skip-grant-tables` +两者道理一样。) +2. 重启服务 +3. `mysql`直接回车遍可以进入安全模式。 +4. `update mysql.user set password=password('redhat') where user='root' and host='localhost'` +5. `flush privileges;` +6. 去掉1中添加的配置。 +7. + +# Mysqldump +> + + +### 1. 做备份 + + +语法: +`mysqldump -u root-p 库 表 > /path/file` + +``` +[root@s168 ~]# mysqldump -uroot -p mysql user > /home/fantj/sql.bak; +Enter password: +[root@s168 ~]# cd /home/fantj/ +[root@s168 fantj]# ls +download hadoop jdk keepalived scala spark sql.bak + +[root@s168 fantj]# cat sql.bak +-- MySQL dump 10.14 Distrib 5.5.60-MariaDB, for Linux (x86_64) +-- +-- Host: localhost Database: mysql +-- ------------------------------------------------------ +-- Server version 5.5.60-MariaDB +``` + +###### 备份库中所有表 + +省略表参数就行:`mysqldump -uroot -p 库 > /path/file` + +如果希望sql中附带创建数据库,则需要在库名前外加`-B `属性。 + +###### 备份表数据(不带数据库语句) +>在mysql cli中完成。 + +`select * from user into outfile '/home/fantj/sql.data'`也可以给他加分隔符,在该命令后追加`fields terminated by ','`则会以逗号分隔保存。 + +当前,前提是mysql用户对该目录有写权限:`setfacl -m u:mysql:rwx /home/fantj` + + +相反,将数据文件导入user表则是:`load data infile '/hoem/fantj/sql.data into table user'`,如果有分隔符,则需要在命令后追加`fields terminated by ','` +### 2. 做还原 +>需要进入mysql cli中进行操作。 + +语法:`source /home/fantj/sql.bak` + +``` +MariaDB [(none)]> source /home/fantj/sql.bak +Query OK, 0 rows affected (0.00 sec) + +Query OK, 0 rows affected (0.00 sec) +``` \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/NFS.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/NFS.md" new file mode 100644 index 0000000..dc41761 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/NFS.md" @@ -0,0 +1,147 @@ +>两个linux系统可以通过NFS系统实现文件共享。 + +其中,`showmount`是连接linux系统中的nfs。如果想连接windows上的共享文件,需要用`smbclient`工具来实现。 + + +### 1. 搭建nfs服务 +>我在ip地址为192.168.27.100上部署NFS服务。 + +默认端口2049 + +##### 1.1 修改配置文件 +>该文件是共享目录设置,格式为 共享目录+参数(在尾巴详解) + + +`vim /etc/exports` +``` +添加一行 + +home/fantj *(rw,sync,no_root_squash) +``` +###### 刷新文件 +`exports -r` +###### 全部参数介绍: +``` +rw 可读写的权限  +ro 只读的权限  +no_root_squash 登入NFS主机,使用该共享目录时相当于该目录的拥有者,如果是root的话,那么对于这个共享的目录来说,他就具有root的权  +               限,这个参数『极不安全』,不建议使用 + +root_squash 登入NFS主机,使用该共享目录时相当于该目录的拥有者。但是如果是以root身份使用这个共享目录的时候,那么这个使用者(root) +             的权限将被压缩成为匿名使用者,即通常他的UID与GID都会变成nobody那个身份 + +all_squash 不论登入NFS的使用者身份为何,他的身份都会被压缩成为匿名使用者,通常也就是nobody +anonuid 可以自行设定这个UID的值,这个UID必需要存在于你的/etc/passwd当中 +anongid 同anonuid,但是变成groupID就是了  +sync 资料同步写入到内存与硬盘当中  +async 资料会先暂存于内存当中,而非直接写入硬盘  +insecure 允许从这台机器过来的非授权访问 +sec 加密 如:sec=krb5p +``` + +##### 1.2 启动nfs +`systemctl start nfs` + +`systmctl status nfs` + +``` +[root@localhost home]# systemctl start nfs +[root@localhost home]# systemctl status nfs +nfs-server.service - NFS Server + Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; disabled) + Active: active (exited) since 二 2018-09-18 13:54:01 CST; 5s ago + Process: 46909 ExecStart=/usr/sbin/rpc.nfsd $RPCNFSDARGS $RPCNFSDCOUNT (code=exited, status=0/SUCCESS) + Process: 46905 ExecStartPre=/usr/sbin/exportfs -r (code=exited, status=0/SUCCESS) + Process: 46903 ExecStartPre=/usr/libexec/nfs-utils/scripts/nfs-server.preconfig (code=exited, status=0/SUCCESS) + Main PID: 46909 (code=exited, status=0/SUCCESS) + CGroup: /system.slice/nfs-server.service + +9月 18 13:54:01 localhost.localdomain systemd[1]: Starting NFS Server... +9月 18 13:54:01 localhost.localdomain systemd[1]: Started NFS Server. +``` + + + +### 2. 客户端连接nfs +>我在ip地址为192.168.27.169上部署客户端。 + +###### showmount命令 +``` +语法:showmount [-aed] [hostname] +-a:显示目前以及连上主机的client机器的使用目录的状态 +-e:显示hostname的/etc/exports里面共享的目录 +-d:只显示被client机器挂载的目录 +``` +--------------------- + + +###### 2.1 测试连通性 +`showmount -e xx.xx.xx.xx` +如果没有showmount命令的话 先yum安装。 +``` +[root@s169 ~]# showmount -e 192.168.27.100 +Export list for 192.168.27.100: +/home/fantj * +``` +如果这一步出现报错,则需要设置服务端的防火墙规则,不正规的做法: `iptables -F`,清除所有防火墙规则。 + +##### 2.2 挂载 +>使用网络设备的时候,也需要挂载才可以使用。 + + +挂载命令:`mount 192.168.27.100:/home/fantj /home/nfs` + +``` +[root@s169 home]# mount 192.168.27.100:/home/fantj /home/nfs +[root@s169 home]# df -h +Filesystem Size Used Avail Use% Mounted on +/dev/mapper/centos-root 13G 2.4G 11G 19% / +devtmpfs 476M 0 476M 0% /dev +tmpfs 488M 0 488M 0% /dev/shm +tmpfs 488M 7.7M 480M 2% /run +tmpfs 488M 0 488M 0% /sys/fs/cgroup +/dev/sda1 1014M 159M 856M 16% /boot +tmpfs 98M 0 98M 0% /run/user/0 +192.168.27.100:/home/fantj 15G 3.4G 12G 23% /home/nfs +``` +可以看到,已经成功挂载(自动识别文件系统)。 + + +然后写入`/etc/fstab`文件中。切记文件系统的格式是nfs。 + +那如果nfs文件是加密的呢?👇 +###### 带加密挂载到fstab +>如果nfs经过krb5加密,则需要在挂载的时候声明它的参数。 + +`192.168.27.100:home/fantj /home/nfs defaults,v4.2,sec=krb5p 0 0` + +##### 2.3 测试写入读取 + +``` +[root@s169 home]# cd /home/nfs/ +[root@s169 nfs]# touch test +[root@s169 nfs]# ls +test +``` + + +### 3. linux连接windows共享文件 + +##### 3.1 建立连接 +`smbclient -L //192.168.0 +1 -U username%passwd` + +username是账号,passwd是密码。 + +-L 是登录 + +##### 3.2 挂载 + +直接使用mount挂载的话会报错显示文件系统类型错误。 + +此时我们需要安装`samba、*和cifs\*`(使其支持cifs文件格式) + +然后再挂载`mount -o username=xxx,password=xxx //192.168.0.1/D /windisk`。文件类型是cifs。 + + +然后 diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/Network-Management.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/Network-Management.md" new file mode 100644 index 0000000..f9a5f65 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/Network-Management.md" @@ -0,0 +1,163 @@ +LDAP:轻量型目录访问协议 + +RHDS:红帽目录服务,客户端工具 +IPA: + +>使用nmcli命令会同步写到配置文件中。 +##### 查看链接信息 + +``` +[root@localhost ~]# nmcli connection +名称 UUID 类型 设备 +ens33 13756690-ac77-b776-4fc1-f5535cee6f16 802-3-ethernet eno16777736 +``` +##### 查看活跃网络信息 +``` +[root@localhost ~]# nmcli connection show --active +名称 UUID 类型 设备 +ens33 13756690-ac77-b776-4fc1-f5535cee6f16 802-3-ethernet eno16777736 +``` + +### 添加新网卡 + +在vm中设置>网络适配器>添加>网络适配器>NET>确定 + +##### 方法一:复制配置文件 + +复制修改`/etc/sysconfig/network-scripts/ifcfg-enxxxx` +##### 方法二:命令行添加 + + +显示网络信息: +`nmcli connection show` + +添加网络信息: + +`nmcli connection add con-name eth1 type ethernet ifname eth1` + +con-name 是指定名字 + +ifname 是接口名 + +tpye 因特网 + +``` +[root@localhost ~]# nmcli connection add con-name eth1 type ethernet ifname eth1 +Connection 'eth1' (bd5b981e-3518-4dee-8aad-0b756622ab55) successfully added. +``` + +检查结果: +``` +[root@localhost ~]# ip a +1: lo: mtu 65536 qdisc noqueue state UNKNOWN + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + valid_lft forever preferred_lft forever + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever +2: eno16777736: mtu 1500 qdisc pfifo_fast state UP qlen 1000 + link/ether 00:0c:29:f3:47:3c brd ff:ff:ff:ff:ff:ff + inet 192.168.27.100/24 brd 192.168.27.255 scope global eno16777736 + valid_lft forever preferred_lft forever + inet6 fe80::20c:29ff:fef3:473c/64 scope link + valid_lft forever preferred_lft forever +3: eno33554992: mtu 1500 qdisc pfifo_fast state UP qlen 1000 + link/ether 00:0c:29:f3:47:46 brd ff:ff:ff:ff:ff:ff +``` +``` +[root@localhost ~]# nmcli connection show +名称 UUID 类型 设备 +eth1 bd5b981e-3518-4dee-8aad-0b756622ab55 802-3-ethernet -- +ens33 13756690-ac77-b776-4fc1-f5535cee6f16 802-3-ethernet eno16777736 +``` + +### 配置网络 + +##### 1. 先查看是否活跃 +``` +[root@localhost ~]# nmcli connection show --active +名称 UUID 类型 设备 +ens33 13756690-ac77-b776-4fc1-f5535cee6f16 802-3-ethernet eno16777736 +``` +可以看到eth1并没有激活。 +所以我们需要先激活。 + +激活网络连接:`nmcli connection up eth1` + +关闭网络连接:`nmcli connection down eth1` + +去掉网络连接(拔掉网线):`nmcli device disconnect eth1` + +接上网络连接(插入网线):`nmcli device connect eth1` + +查看eth1所有参数:`nmcli connection show eth1` + +修改ip地址:`nmcli connection modify eth1 ipv4.addresses 192.168.27.110`,改完重启NetworkManager服务,不然不能及时生效。 + +修改ip获取方式:`nmcli connection eth1 ipv4.method manua` 修改为手动获取。 + + + + + + + + + +### 设置主机名 + +##### 传统方式: + +修改/etc/hostname + +或者临时修改`hostname xxx` + +##### rhel7方式: + +查看hostname信息:`hostnamectl status` +``` +[root@localhost ~]# hostnamectl status + Static hostname: localhost.localdomain +Transient hostname: xxx + Icon name: computer + Chassis: n/a + Machine ID: dace283296e141d6b6eaa41d03184eeb + Boot ID: 0c39622521a64fcf86c5d9297f1e8a17 + Virtualization: vmware + Operating System: Red Hat Enterprise Linux Server 7.0 (Maipo) + CPE OS Name: cpe:/o:redhat:enterprise_linux:7.0:GA:server + Kernel: Linux 3.10.0-123.el7.x86_64 + Architecture: x86_64 +``` +命令永久修改hostname:`hostnamectl set-hostname fantj` + +``` +[root@localhost ~]# hostnamectl set-hostname fantj +[root@localhost ~]# cat /etc/hostname +fantj +``` + + +### ipv6设置 +>ipv6地址举例:(128比特,16进制) 2001:250:c01:6188:655b:1bd0:b59a:9ef + +ipv6的公网地址:2000::1/64 + +ipv6的私有地址:fec0::1/64 + +回还地址: ::1 + +fe80:: 一般表示没有获取ipv6地址。 + +查看ipv6地址信息: +`nmcli connection show eth1 |grep ipv6` + + +设置ipv6地址:`nmcli connection modify eth1 ipv6.addresses "fec0::2/64 fec0::1"`前者是地址,后者是网关。注意修改获取方式为手工manual.和网段/64保持一致(掩码保持一致)。 + +ping ipv6的方法: +ping6 200exxx + + + + diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/SMB.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/SMB.md" new file mode 100644 index 0000000..898aaf7 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/SMB.md" @@ -0,0 +1,100 @@ +>Samba是一种在局域网上共享文件和打印机的一种通信协议,它为局域网内的不同计算机之间提供文件及打印机等资源的共享服务。 + +如果是windows的文件共享,需要安装cifs包来支持此文件系统。 + + + +### 1. 安装 +`yum install samba -y` + +### 2. 配置 + +###### 配置文件目录 +`/etc/samba/` + + +###### 修改文件安全上下文 +>chcon命令是修改对象(文件)的安全上下文,比如:用户、角色、类型、安全级别。也就是将每个文件的安全环境变更至指定环境。 + + +``` +chcon [选项]... 环境 文件... +chcon [选项]... [-u 用户] [-r 角色] [-l 范围] [-t 类型] 文件... +chcon [选项]... --reference=参考文件 文件... + + +-h, --no-dereference:影响符号连接而非引用的文件。 + --reference=参考文件:使用指定参考文件的安全环境,而非指定值。 +-R, --recursive:递归处理所有的文件及子目录。 +-v, --verbose:为处理的所有文件显示诊断信息。 +-u, --user=用户:设置指定用户的目标安全环境。 +-r, --role=角色:设置指定角色的目标安全环境。 +-t, --type=类型:设置指定类型的目标安全环境。 +-l, --range=范围:设置指定范围的目标安全环境。 + + +以下选项是在指定了-R选项时被用于设置如何穿越目录结构体系。如果您指定了多于一个选项,那么只有最后一个会生效。 + +-H:如果命令行参数是一个通到目录的符号链接,则遍历符号链接。 +-L:遍历每一个遇到的通到目录的符号链接。 +-P:不遍历任何符号链接(默认)。 +--help:显示此帮助信息并退出。 +--version:显示版本信息并退出。 +``` + +如果你希望将samba目录共享给其他用户,你需要设置:`chcon -R -t samba_sharee_t /common` + +如果你想把这个ftp共享给匿名用户的话,需要开启以下:`chcon -R -t public_content_t /var/ftp` + +如果你想让你设置的FTP目录可以上传文件的话,SELINUX需要设置:`chcon -t public_content_rw_t /var/ftp/incoming` + +###### 主配置文件`smb.conf` +``` +workgroup = SMBGROUP #设置工作组 +[common] #与文件路径同名 + comment = linhut #描述 + path = /common #共享资源名 + #read list= natasha #读权限用户,添加这个名单就只有里面的可以读 + browseable = yes #共享资源是否允许用户浏览 + hosts allow = 172.25.12. #允许访问的(这里题目DNS域解析为这个段) +[miscellaneous] #与文件路径同名 + comment = linhut #描述 + path = /miscellaneous #共享资源名 + #read list= silene #有读权限用户 + write list = akira #读写权限用户 + browseable = yes #共享资源是否允许用户浏览 + hosts allow = 172.25.12. #允许访问的(这里题目DNS域解析为这个段) + +``` + +###### 查看SMB分享 + +`smbclietn -L //192.168.0.1` + +###### 设置smb密码 + +`smbpasswd -a fantj` + +###### 服务重启 +`systemctl restart smb;systemctl restart nmb` +#### 在防火墙中开启服务支持 + +`firewall-cmd --permanent --add-rich-rule 'rule family=ipv4 source address=172.25.xx.xx/24 service name="samba" reject` + +### 3. 挂载 + +#### 3.1 安装cifs支持 + +`yum install cifs-utils -y` + +#### 3.2 设置密码文件 + +`echo "username"=xxx >> /root/smb-multiuser.txt` +`echo "password"=xxx >> /root/smb-multiuser.txt` + +#### 3.3 挂载 +`vim /etc/fstab` + +``` +//serverX/miscellaneous /mnt/multi cifs credentials=/root/smb-multiuser.txt,multiuser,sec=ntlmssp 0 0 +``` \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/httpd.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/httpd.md" new file mode 100644 index 0000000..9bf857c --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/httpd.md" @@ -0,0 +1,172 @@ + + +### 1. 安装 +`yum install httpd -y` + +#### 开启端口 + +`firewall-cmd --permanent --add-service=http` + +### 2. 配置 +>配置文件都在`/etc/httpd/conf`下。 + + +##### 查看已经加载的模块 + +`httpd -M` +##### 主配置文件 + +`/etc/httpd/conf/httpd.conf` + +``` +ServerRoot "/etc/httpd" 根目录 +Listen 80 监听80端口 +Include conf.modules.d/*.conf 扫描配置文件目录 +User apache 所有者 +Group apache 所属组 +ServerAdmin root@localhost 设置管理员邮箱 +ServerName www.example.com:80 域名 + 访问目录 + AllowOverride none 不允许覆盖 + Require all denied 所有都拒绝访问 + + +DocumentRoot "/var/www/html" html目录 + + + AllowOverride None + # Allow open access: + Require all granted 所有请求都授权 + + + + Options Indexes FollowSymLinks Indexes:类似ftp功能,FollowSymLinks:支持软连接 + AllowOverride None 配置不被覆盖 + Require all granted + + +Alias /path /realpath 访问/path的时候实则访问/realpath +当然,完成上面的alias功能还需要添加访问权限,如下: + + Options Indexes FollowSymLinks Indexes:类似ftp功能,FollowSymLinks:支持软连接 + AllowOverride None 配置不被覆盖 + Require all granted + + + + ScriptAlias /cgi-bin/ "/var/www/cgi-bin/" 动态脚本支持,访问/cgi-bi时,实际访问本地/var/www/cgi-bin/ + + +ErrorLog "logs/error_log" 错误日志 + +EnableSendfile on +``` + +##### 简单配置虚拟主机 + +从`/usr/share/doc/httpd-2.4.6/httpd-vhosts.conf`中拷贝一份模版到`/etc/httpd/conf.d/`中 + +``` + + DocumentRoot "/var/www/html" + ServerName fantj1.com + +``` +然后在`/var/www/html/`下添加index.html文件。 + +重启服务请求`fantj1.com`即可访问到index.html。(前提是hosts认识fantj1.com这个域名) + +### 3. 配置CA + + +##### 3.1 生成自签名CA证书 +`cd /etc/pki/tls/certs/`然后执行`make caname.crt` + +``` +[root@s168 certs]# make fantj.crt +umask 77 ; \ +/usr/bin/openssl genrsa -aes128 2048 > fantj.key +Generating RSA private key, 2048 bit long modulus +...............+++ +....................+++ +e is 65537 (0x10001) +Enter pass phrase: +Verifying - Enter pass phrase: +``` +然后目录里多了两个文件:`fantj.crt`和`fantj.key` +##### 3.2 将私钥公钥拷贝 +`cp fantj.* /etc/httpd/conf/` + +##### 3.3 安装https模块 +`yum install mod_ssl -y` + +编辑`/etc/httpd/conf.d/ssl.conf`文件。 +``` +Listen 443 https 监听端口 + + +SSLPassPhraseDialog exec:/usr/libexec/httpd-ssl-pass-dialog + +SSLSessionCache shmcb:/run/httpd/sslcache(512000) +SSLSessionCacheTimeout 300 + +SSLRandomSeed startup file:/dev/urandom 256 +SSLRandomSeed connect builtin + +SSLCryptoDevice builtin + + + 虚拟主机配置 + + +ErrorLog logs/ssl_error_log +TransferLog logs/ssl_access_log +LogLevel warn 日志信息 + +SSLEngine on ssl开关 + +SSLProtocol all -SSLv2 -SSLv3 支持ssl协议 + +SSLCipherSuite HIGH:3DES:!aNULL:!MD5:!SEED:!IDEA + + +SSLCertificateFile 证书 /etc/pki/tls/certs/localhost.crt + +SSLCertificateKeyFile /etc/pki/tls/private/localhost.key 私钥 + + + + + + + SSLOptions +StdEnvVars + + + SSLOptions +StdEnvVars + + +BrowserMatch "MSIE [2-5]" \ + nokeepalive ssl-unclean-shutdown \ + downgrade-1.0 force-response-1.0 + +CustomLog logs/ssl_request_log \ + "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" + + +``` + +其中我们需要修改`SSLCertificateFile`和`SSLCertificateKeyFile`这两个配置,它们分别用来设置crt文件目录和key文件目录。其他保持默认配置即可。 + +``` +SSLCertificateFile /etc/httpd/conf/fantj.crt +SSLCertificateKeyFile /etc/httpd/conf/fantj.key +``` +###### 重启httpd服务 +``` +[root@s168 conf.d]# systemctl restart httpd +Enter SSL pass phrase for www.example.com:443 (RSA) : ****** +``` +好了,设置成功. + + + diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/iscsi.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/iscsi.md" new file mode 100644 index 0000000..3bdfc2c --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/iscsi.md" @@ -0,0 +1,145 @@ +>iSCSI技术是一种由IBM公司研究开发的,是一个供硬件设备使用的可以在IP协议的上层运行的SCSI指令集,这种指令集合可以实现在IP网络上运行SCSI协议,使其能够在诸如高速千兆以太网上进行路由选择。iSCSI技术是一种新储存技术,该技术是将现有SCSI接口与以太网络(Ethernet)技术结合,使服务器可与使用IP网络的储存装置互相交换资料。 + +###### iSCSI的工作过程: +1. 当iSCSI主机应用程序发出数据读写请求后,操作系统会生成一个相应的SCSI命令, +2. 该SCSI命令在iSCSI initiator层被封装成ISCSI消息包并通过TCP/IP传送到设备侧, +3. 设备侧的iSCSI target层会解开iSCSI消息包,得到SCSI命令的内容,然后传送给SCSI设备执行; +4. 设备执行SCSI命令后的响应,在经过设备侧iSCSI target层时被封装成ISCSI响应PDU,通过TCP/IP网络传送给主机的ISCSI initiator层, +5. iSCSI initiator会从ISCSI响应PDU里解析出SCSI响应并传送给操作系统,操作系统再响应给应用程序。 + + +共享设备:可以是磁盘,也可以是本地设备。 + +# 服务端 +### 1. 安装 + +###### 1.1 查找targetcli依赖 +`yum list targetcli\*` + +###### 1.2 安装 +`yum install targetcli\* -y` + +###### 1.3 开放端口 +`firewall-cmd --permanent --add-port=3260/tcp` + +### 2. 使用 + +#### 2.1 使用targetcli +进入cli:`targetcli` + +如果是共享磁盘,则放到block下 + +如果是共享文件,则放到fileio下 + + +``` +cd backstores/ + +创建磁盘:block/ create block1 /dev/sdb1 + +创建fileio:fileio/ create file1 /xxx/file + +查看创建项目:ls + +删除项目:fileio/ delete file1 + + +``` + +#### 2.2 创建target +>需要进入到iscsi目录下操作。 + +``` +cd / + +cd iscsi + +ls + +创建target:create ign.2018-09.com.fantj:disk + +删除target:delete ign.2018-09.com.fantj:disk +``` + +#### 2.3 建立设备和target的联系 + +``` +cd ign.2018-09.com.fantj:disk/ + +cd tpg1/ + +ls + +创建ACL(相当于密码): acls/ create ign.2018-09.com.fantj:xx + +删除ACL:/acl delete ign.2018-09.com.fantj:xx + +创建LUN(创建联系):luns/ create /backstores/fileio/file1 + +ls + +设置portals(访问地址):portals/ create 192.168.27.100 + +ls 查看自动分配的端口 + +退出 :exit +``` +默认配置文件保存在`/etc/target/saveconfig.json`文件中。 + +###### 重启服务/开机启动 +`systemctl restart target` + +`systemctl enable target` + +--- +# 客户端 + +### 1. 安装 +`yum install iscsi\*` + +##### 启动服务 +`systemctl start iscsi` + +### 2. 使用 + +###### 2.1 寻找/扫描服务 +>扫描某个ip下的共享设备列表。 + +`iscsiadm -m discovery -t st -p 192.168.27.100` + +``` +[root@fantj]# iscsiadm -m discovery -t st -p 192.168.27.100 +192.168.27.100:3260 ign.2018-09.com.fantj:disk +``` +``` +iscsiadm参数: + +-m 模式model +-t 类型type +-T target名字 +-p 地址portal +``` + +###### 2.2 加载设备 +``` +[root@fantj]# iscsiadm -m node -T ign.2018-09.com.fantj:disk -p 192.168.27.100 -l +此时是加载不成功的,因为在服务端有配置ACL加密信息。 + +解决ACL加密: +1. 编辑/etc/iscsi/initiatorname.iscsi文件, +2. 将值改为我们服务端设置的ACL:ign.2018-09.com.fantj:xx + +重启服务: systemctl restart iscsi + +重复上面步骤:扫描->加载 +``` + +###### 2.3 查看加载结果 + +`cat /etc/proc/partitions` +或者 +`iscsiadm -m session` + +开机自动连接,只需要做好挂载就行。 + +如果不想自动连接:修改配置`/etc/iscsi/iscsi.conf`将`node.startup=automatic 改成 manual` \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/vim.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/vim.md" new file mode 100644 index 0000000..d8940af --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/vim.md" @@ -0,0 +1,89 @@ +### 插入命令 + +a 在光标所在字符后插入 +A 在光标所在行尾插入 + +i 在光标所在字符前插入 +I 在光标所在行行首插入 + +o 在光标下插入新行 +O 在光标上插入新行 + + +### 定位命令 + +:set nu 设置行号 +:set nonu 取消行号 + +gg 到第一行 +GG 到最后一行 + +nG 到第n行 +:n 到第n行 + +$ 移至行尾 +0 移至行首 + + +### 删除命令 + +x 删除光标所在字符 +nx 删除后n个字符 +dd 删除所在行 + +dG 删除到文件末尾 +D 删除到行尾 + +:n1,n2d 删除指定范围的行 + + +### 复制和剪切命令 + +yy 复制当前行 +nyy 复制当前以下n行 +dd 剪切当前行 +ndd 。。。 +p 粘贴在行下 +P 粘贴在行上 + +### 替换和取消 + +r 取代光标所在字符 +R 从当前开始替换字符,esc结束 +u 取消上一步操作 + + +### 搜索和搜索替换命令 + +/string 搜索字符串string + +n 查看下一个string + +:%s/old/new/g 全文替换 + +:n1,n2s/old/new/g 替换范围行 + +### 保存和退出命令 + +:w +:wq +:wq! +:q! + +ZZ :wq的捷键 + +:w new_filename 另存为指定文件 + + +### 连续行注释 + +:n1,n2s/^/#/g ^表示行首 + + + + + + + + + diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\346\226\207\344\273\266\347\263\273\347\273\237\347\256\241\347\220\206\357\274\232\345\210\206\345\214\272\343\200\201\346\214\202\350\275\275....md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\346\226\207\344\273\266\347\263\273\347\273\237\347\256\241\347\220\206\357\274\232\345\210\206\345\214\272\343\200\201\346\214\202\350\275\275....md" new file mode 100644 index 0000000..61bb286 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\346\226\207\344\273\266\347\263\273\347\273\237\347\256\241\347\220\206\357\274\232\345\210\206\345\214\272\343\200\201\346\214\202\350\275\275....md" @@ -0,0 +1,411 @@ +### 1. 文件系统常用命令 + +#### 当前分区信息查看 +`cat /proc/partitions ` + +``` +[roo@localhost ~]$ cat /proc/partitions +major minor #blocks name + + 8 0 20971520 sda + 8 1 512000 sda1 + 8 2 20458496 sda2 + 11 0 3655680 sr0 + 253 0 18358272 dm-0 + 253 1 2097152 dm-1 +``` + +#### 1.1 df、du、fsck、dump2fs + +##### `df`统计文件系统占用情况 +`df [选项] [挂载点]` +``` +-a 显示所有文件系统信息 +-h 人性化容量显示 +-T 文件系统类型 +-m 以MB为单位显示容量 +-k 以KB为单位显示容量 +``` + +##### `du`统计目录或文件大小 +`du [optionals] [dir or filename]` +``` +-a 显示每个子文件的占用量 +-h 人性化显示占用量 +-s 统计总占用量 +``` +用ls也可以查看文件大小,但是ls的查看的文件夹大小是不包含文件夹内文件大小。 + +可以用`du -sh /xxx`来查看文件占用量,但是du看到的只是文件大小。df查到的是所有空间内的大小。 + +##### `fsck`文件系统修复命令 +`fsck [选项] 分区设备文件名` +``` +-a 不显示用户提示,自动修复文件系统 +-y 自动修复 +``` +系统开机自动检测,不需要手工用。 + +##### `dump2fs`显示磁盘状态命令 +`dump2fs 分区设备文件名` +所有磁盘的相关信息都会显示:挂载、格式等。 + +#### 1.2 挂载命令 + +##### 查询挂载 +查询系统已挂载的设备,-l显示卷标名称:`mount [-l]` + +##### 自动挂载 +依据配置文件/etc/fstab的内容,自动挂载:`mount -a` + +##### 挂载命令 +`mount [-t文件系统] [-L卷标名] [-o特殊选项] 设备文件名 挂载点` +``` +选项: + -t 文件系统,ext3/ext4/iso9660等 + -L 卷标名,指定卷标分区, + -o 特殊选项:可以指定挂载的额外选项 + 比如 remount 重新挂载、 + exec/noexec 是否允许文件系统高中可执行文件的执行 + mount -o remount,noexec /home :将home重新挂载,并禁止执行文件执行 +``` +#### 1.3 挂载光盘与U盘 +>如果需要不断的切换光盘来读取数据,那么我们就需要设置一个目录和该设备绑定,只要进入这个目录,系统就自动挂载光盘。这个会在1.3最后面讲到。 +##### 1.3.1 挂载光盘 +>RHEL7中,默认光盘的设备文件名是:/dev/sr0,系统默认挂载点是 /run/media/roo/ + +``` +mkdir /mnt/cdrom/ 建立挂载点 + +mount -t iso9660 /dev/sr0 /mnt/cdrom/ 挂载光盘 + +/dev/sr0 on /run/media/roo/RHEL-7.0 Server.x86_64 type iso9660 挂载信息 + +mount /dev/sr0 /mnt/cdrom/ +``` + +##### 1.3.2 卸载光盘 + +`umount 设备文件名或挂载点` +``` +umount /dev/sr0 卸载光盘 +``` + +##### 挂载U盘 +>注意默认不支持NTFS系统,详情见1.4 +``` +fdisk -l 查看U盘设备文件名是啥,假设是/dev/sdb1 + +mount -t vfat /dev/sdb1 /mnt/usb/ 挂载U盘 +``` +##### 卸载U盘 + + +`umount 设备文件名或挂载点` +``` +umount /dev/sdb1 卸载U盘 +``` + +##### 自动挂载光盘 +>如果需要不断的切换光盘来读取数据,那么我们就需要设置一个目录和该设备绑定,只要进入这个目录,系统就自动挂载光盘。 + +1. 编辑文件`/etc/auto.master` + +如果没有该文件,则先安装autofs: + +`yum install -y autofs +` +然后编辑`/etc/auto.master` + +在`/misc /etc/auto.misc`后面添加一行: +`/home/udisk /etc/auto.udisk` + +2. 创建并编辑文件auto.udisk + +` cp /etc/auto.misc /etc/auto.udisk +` + +编辑`/etc/auto.udisk` + +``` +cd -fstype=iso9660,ro,nosuid,nodev :/dev/cdrom +mycd -fstype=iso9660,ro,nosuid,nodev :/dev/cdrom +``` +其中,mycd是/home/udisk的子目录,然后设置文件类型,文件所在路径等。 + +然后重启`autofs`服务,即可实现自动检测挂载。 + +#### 单用户模式下mount -o + +我们的Linux系统在无法启动时候,通常需要进入单用户模式下进行修改一些配置文件,或调整一些参数方可。但是在进入单用户模式后,/文件系统是只读模式,任何用户都无法进行修改,那么这个时候我们就需要用到一条命令:mount -o remount,rw / 这个命令来让我们的/文件系统为可读可写模式,这样就可以实现自由修改了。 + + +#### 1.4 ntfs文件系统的支持 +>大概有两种方法: + +1. 内核编译(不常用) +2. 下载插件 NTFS-3G https://www.tuxera.com/community/open-source-ntfs-3g/ + + +##### 安装步骤 +``` +wget https://tuxera.com/opensource/ntfs-3g_ntfsprogs-2017.3.23.tgz + +tar ntfs-3g_ntfsprogs-2017.3.23.tgz + +cd ntfs-3g_ntfsprogs-2017.3.23 + +./configure 编译器准备 + +make 编译 + +make install 编译安装 + +mount -t ntfs-3g 分区设备文件名 挂载点 eg:mount -t ntfs-3g /dev/sda1 /mnt/windows + +``` + +### 2. fdisk分区 +>fdisk操作完成后,记着一定要w来保存更改,如果w报错,则需要启动内核对新分区表重新读取,命令是`partpr /dev/sda` + +##### fdisk命令分区过程 +`fdisk -l` 查看新硬盘 + +###### 使用fdisk命令分区 + +`fdisk /dev/sda` + +###### 分主分区: +``` +[root@localhost ~]# fdisk /dev/sda +欢迎使用 fdisk (util-linux 2.23.2)。 + +更改将停留在内存中,直到您决定将更改写入磁盘。 +使用写入命令前请三思。 + + +命令(输入 m 获取帮助):m +命令操作 + + d delete a partition 删除一个分区 + l list known partition types 显示文件系统类型,82为swap分区,83为分区 + m print this menu 显示帮助菜单 + n add a new partition 新建分区 + p print the partition table 显示分区列表 + q quit without saving changes 不保存退出 + w write table to disk and exit 保存退出 +命令(输入 m 获取帮助):n +Partition type: + p primary (2 primary, 0 extended, 2 free) + e extended +Select (default p): p +分区号 (3,4,默认 3):3 +First cylinder...: +Last cylinder ...: +500M +``` +###### 删除分区 +d + +``` +命令(输入 m 获取帮助):p + +磁盘 /dev/sda:21.5 GB, 21474836480 字节,41943040 个扇区 +Units = 扇区 of 1 * 512 = 512 bytes +扇区大小(逻辑/物理):512 字节 / 512 字节 +I/O 大小(最小/最佳):512 字节 / 512 字节 +磁盘标签类型:dos +磁盘标识符:0x00092946 + + 设备 Boot Start End Blocks Id System +/dev/sda1 * 2048 1026047 512000 83 Linux +/dev/sda2 1026048 41943039 20458496 8e Linux LVM + +命令(输入 m 获取帮助):d +分区号 (1,2,默认 2):2 +分区 2 已删除 +``` + +###### 分区类型转换 +t + +``` +命令(输入 m 获取帮助):t +分区号 (1,2,默认 2):2 +Hex 代码(输入 L 列出所有代码):L + + 0 空 24 NEC DOS 81 Minix / 旧 Linu bf Solaris + 1 FAT12 27 隐藏的 NTFS Win 82 Linux 交换 / So c1 DRDOS/sec (FAT- + 2 XENIX root 39 Plan 9 83 Linux c4 DRDOS/sec (FAT- + 3 XENIX usr 3c PartitionMagic 84 OS/2 隐藏的 C: c6 DRDOS/sec (FAT- + 4 FAT16 <32M 40 Venix 80286 85 Linux 扩展 c7 Syrinx + 5 扩展 41 PPC PReP Boot 86 NTFS 卷集 da 非文件系统数据 + 6 FAT16 42 SFS 87 NTFS 卷集 db CP/M / CTOS / . + 7 HPFS/NTFS/exFAT 4d QNX4.x 88 Linux 纯文本 de Dell 工具 + 8 AIX 4e QNX4.x 第2部分 8e Linux LVM df BootIt + 9 AIX 可启动 4f QNX4.x 第3部分 93 Amoeba e1 DOS 访问 + a OS/2 启动管理器 50 OnTrack DM 94 Amoeba BBT e3 DOS R/O + b W95 FAT32 51 OnTrack DM6 Aux 9f BSD/OS e4 SpeedStor + c W95 FAT32 (LBA) 52 CP/M a0 IBM Thinkpad 休 eb BeOS fs + e W95 FAT16 (LBA) 53 OnTrack DM6 Aux a5 FreeBSD ee GPT + f W95 扩展 (LBA) 54 OnTrackDM6 a6 OpenBSD ef EFI (FAT-12/16/ +10 OPUS 55 EZ-Drive a7 NeXTSTEP f0 Linux/PA-RISC +11 隐藏的 FAT12 56 Golden Bow a8 Darwin UFS f1 SpeedStor +12 Compaq 诊断 5c Priam Edisk a9 NetBSD f4 SpeedStor +14 隐藏的 FAT16 <3 61 SpeedStor ab Darwin 启动 f2 DOS 次要 +16 隐藏的 FAT16 63 GNU HURD or Sys af HFS / HFS+ fb VMware VMFS +17 隐藏的 HPFS/NTF 64 Novell Netware b7 BSDI fs fc VMware VMKCORE +18 AST 智能睡眠 65 Novell Netware b8 BSDI swap fd Linux raid 自动 +1b 隐藏的 W95 FAT3 70 DiskSecure 多启 bb Boot Wizard 隐 fe LANstep +1c 隐藏的 W95 FAT3 75 PC/IX be Solaris 启动 ff BBT +1e 隐藏的 W95 FAT1 80 旧 Minix +Hex 代码(输入 L 列出所有代码):fd +已将分区“Linux”的类型更改为“Linux raid autodetect” +``` + +###### 分扩展分区: +>扩展分区唯一的作用是包含逻辑分区,不能格式化,不能写入数据。 + +``` +首先先new一个扩展分区 +命令(输入 m 获取帮助):n +Partition type: + p primary (3 primary, 0 extended, 1 free) + e extended +Select (default e): e +已选择分区 4 +起始 扇区 (3074048-41943039,默认为 3074048): +将使用默认值 3074048 +Last 扇区, +扇区 or +size{K,M,G} (3074048-41943039,默认为 41943039): +将使用默认值 41943039 +分区 4 已设置为 Extended 类型,大小设为 18.5 GiB + + +扩展分区创建完毕 + +开始创建逻辑分区sda5 + +命令(输入 m 获取帮助):p + +磁盘 /dev/sda:21.5 GB, 21474836480 字节,41943040 个扇区 +Units = 扇区 of 1 * 512 = 512 bytes +扇区大小(逻辑/物理):512 字节 / 512 字节 +I/O 大小(最小/最佳):512 字节 / 512 字节 +磁盘标签类型:dos +磁盘标识符:0x00092946 + + 设备 Boot Start End Blocks Id System +/dev/sda1 * 2048 1026047 512000 83 Linux +/dev/sda2 1026048 2050047 512000 83 Linux +/dev/sda3 2050048 3074047 512000 83 Linux +/dev/sda4 3074048 41943039 19434496 5 Extended + +命令(输入 m 获取帮助):n +All primary partitions are in use +添加逻辑分区 5 +起始 扇区 (3076096-41943039,默认为 3076096): +将使用默认值 3076096 +Last 扇区, +扇区 or +size{K,M,G} (3076096-41943039,默认为 41943039):+500M +分区 5 已设置为 Linux 类型,大小设为 500 MiB + +命令(输入 m 获取帮助):p + +磁盘 /dev/sda:21.5 GB, 21474836480 字节,41943040 个扇区 +Units = 扇区 of 1 * 512 = 512 bytes +扇区大小(逻辑/物理):512 字节 / 512 字节 +I/O 大小(最小/最佳):512 字节 / 512 字节 +磁盘标签类型:dos +磁盘标识符:0x00092946 + + 设备 Boot Start End Blocks Id System +/dev/sda1 * 2048 1026047 512000 83 Linux +/dev/sda2 1026048 2050047 512000 83 Linux +/dev/sda3 2050048 3074047 512000 83 Linux +/dev/sda4 3074048 41943039 19434496 5 Extended +/dev/sda5 3076096 4100095 512000 83 Linux + +命令(输入 m 获取帮助):n +All primary partitions are in use +添加逻辑分区 6 +起始 扇区 (4102144-41943039,默认为 4102144): +将使用默认值 4102144 +Last 扇区, +扇区 or +size{K,M,G} (4102144-41943039,默认为 41943039): +将使用默认值 41943039 +分区 6 已设置为 Linux 类型,大小设为 18 GiB +``` +别忘了最后w保存修改。 +###### 格式化分区 +`mkfs -t ext4 /dev/sda2` + + +###### 建立挂载点并挂载 +``` +mkdir /disk1 + +mount /dev/sdb1 /disk1 +``` + +##### 分区自动挂载与fstab文件修复 +>命令挂载,reboot之后挂载会消失,所以我们需要开机自动挂载,写入/etc/fstab文件,写入文件要小心,否则系统崩溃。 + +`/etc/fstab` +``` +[root@localhost dev]# cat /etc/fstab + +# +# /etc/fstab +分区设备文件名或uuid:挂载点:文件系统名称:挂在参数:指定分区是否被dump备份(0不,1每天备份,2不定期备份):指定分区是否被fsck检测(0不,其他表示优先级1>2) +/dev/mapper/rhel-root / xfs defaults 1 1 +UUID=c2bb7ebb-1d45-4d22-81ee-11ece3d6e1b0 /boot xfs defaults 1 2 +/dev/mapper/rhel-swap swap swap defaults 0 0 + +``` +写完后 `mount -a `查看是否有报错。 +### 3. /etc/fstab 文件修复 +>一旦文件报错,则需要修复。 + +``` +1. 开机进入报错界面,最下面提示输入root密码 +2. 登录root后vim /etc/fstab。 +3. 如果有权限不够,则需要mount -o remount,rw / 将分区可读写 +4. 修改/etc/fstab + +``` +### 4. 分配swap分区 +>当我们内存不够用,就会使用swap分区暂时来与cpu做交互,性质与windows的虚拟内存类似(sys.page文件)。 + +##### 查看交换分区 + +`swapon -s` +``` +[roo@localhost ~]$ swapon -s +文件名 类型 大小 已用 权限 +/dev/dm-1 partition 2097148 0 -1 +``` +或者查看文件`/proc/swaps` +``` +[roo@localhost ~]$ cat /proc/swaps +Filename Type Size Used Priority +/dev/dm-1 partition 2097148 0 -1 +``` +其中,Priority表示swap分区的优先级,可以通过`swapon -p x /dev/sda3` +##### 设置交换分区 +``` +mkswap /dev/sda3 格式化分区,注意此时sda3并没有成为swap分区 + +swapon /dev/sda3 将sda3设置成swap分区 + +swapon 查看设置结果 +``` + +##### 关闭交换分区 +`swapoff /dev/sda3` + +##### 永久生效 + +将其写入`/etc/fstab`文件中 + +写入格式: +``` +/dev/sdba3 swap swap defaults 0 0 +``` +然后重新挂载`mount -a` diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\347\224\250\346\210\267\345\222\214\347\224\250\346\210\267\347\273\204.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\347\224\250\346\210\267\345\222\214\347\224\250\346\210\267\347\273\204.md" new file mode 100644 index 0000000..65a305e --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\347\224\250\346\210\267\345\222\214\347\224\250\346\210\267\347\273\204.md" @@ -0,0 +1,199 @@ +### 用户信息文件 +/etc/passwd + +``` +[root@FantJ ~]# cat /etc/passwd +用户名:密码标志:UID:GID:用户说明:家目录:登录后的shell +root:x:0:0:root:/root:/bin/bash +bin:x:1:1:bin:/bin:/sbin/nologin +daemon:x:2:2:daemon:/sbin:/sbin/nologin +adm:x:3:4:adm:/var/adm:/sbin/nologin +lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin +sync:x:5:0:sync:/sbin:/bin/sync +shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown + +``` +uid:0是超级用户;1-499是系统用户;500-65535普通用户 +### 影子文件 +/etc/shadow +``` +[root@FantJ ~]# cat /etc/shadow +用户名:加密密码(SHA512加密,!!和*代码无密,不能登录):密码最后一次修改日期(1970.1.1起):两次密码修改时间间隔:密码有效期:密码到期警告天数:密码过期后宽限天数(0代表立即失效,1代表永远不失效):账号失效时间:保留字段 +root:$6$kNnqtN1f$9mhJiIF50OVbQdCSxsbleqGdG8.9nyXDexn/yNkjsSczgNmWqKe6y3mgVyStOwfmaXulppdXCBLDBBaisHUr/.:17638:0:99999:7::: +bin:*:16659:0:99999:7::: +daemon:*:16659:0:99999:7::: +adm:*:16659:0:99999:7::: +lp:*:16659:0:99999:7::: +sync:*:16659:0:99999:7::: +shutdown:*:16659:0:99999:7::: +``` +### 组信息文件 +/etc/group + +``` +[root@FantJ ~]# cat /etc/group +组名:组密码标志:GID:组中附加用户 +root:x:0: +bin:x:1: +daemon:x:2: +sys:x:3: +adm:x:4: +tty:x:5: +disk:x:6: +lp:x:7: + +``` +### 组密码文件 +/etc/gshadow +和用户密码文件原理类似 + + +### 用户家目录 +普通用户:/home/用户名,权限默认700 + +超级用户:/root/,默认权限550 + +### 用户的邮箱 + +`/var/spool/mail/用户名/` + + +### 用户模版目录 +>用户创建时候需要从该处copy一份配置到家目录下。一般为隐藏目录 + +`/etc/skel` + +### 用户管理命令 + +##### `useradd` 用户添加命令 +`useradd [optional] 用户名` + +-u uid + +-d 家目录 + +-c 用户说明 + +-g 初始组 + +-G 附加组 + +-s 指定登录shell,默认/bin/bash + +###### useradd配置文件 +>有两个配置文件来管理useradd的配置,一个是`/etc/default/useradd`,一个是`/etc/login.defs` + +``` +[root@FantJ ~]# cat /etc/default/useradd +# useradd defaults file +GROUP=100 用户默认组 +HOME=/home 用户家目录 +INACTIVE=-1 密码过期宽限天数 +EXPIRE= 密码失效时间 +SHELL=/bin/bash 默认shell +SKEL=/etc/skel 模版目录 +CREATE_MAIL_SPOOL=yes 是否建立邮箱 + +``` +``` +[root@FantJ ~]# cat /etc/login.defs + +MAIL_DIR /var/spool/mail + +PASS_MAX_DAYS 99999 密码有效期 +PASS_MIN_DAYS 0 密码修改间隔 +PASS_MIN_LEN 5 密码最小5位 +PASS_WARN_AGE 7 密码到期警告 + +UID_MIN 1000 最小和最大uid +UID_MAX 60000 +# System accounts +SYS_UID_MIN 201 +SYS_UID_MAX 999 + +GID_MIN 1000 +GID_MAX 60000 +# System accounts +SYS_GID_MIN 201 +SYS_GID_MAX 999 + +CREATE_HOME yes + +# The permission mask is initialized to this value. If not specified, +# the permission mask will be initialized to 022. +UMASK 077 + +USERGROUPS_ENAB yes + +# Use SHA512 to encrypt password. +ENCRYPT_METHOD SHA512 加密模式 +``` + +##### `passwd` 修改用户密码 +`passwd [] 用户名` + +-S 查询用户密码状态,仅root可用 + +-l 暂时锁定用户,仅root可用 + +-u 解锁用户,仅root可用 + +--stdin 可通过grep输出数据作为用户密码 + +普通用户修改密码时直接`passwd`命令回车 + +###### 查看密码状态 +`passwd -S xxx` + +``` +[root@FantJ ~]# passwd -S ftpuser +用户名 :密码设定时间:密码修改时间间隔:密码有效期:警告时间:密码不失效 +ftpuser PS 2018-04-27 0 99999 7 -1 (Password set, SHA512 crypt.) +``` +###### 用户锁定 +`passwd -l xxx` + +相当于在shadow 文件中,用户密码前加了两个感叹号。 +##### `usermod` 修改用户信息 +`usermod [] 用户名` + +-u uid +-c 用户说明 +-G 组名 +-L 临时锁定用户 +-U 解锁用户 +##### `chage` 修改密码状态 +`chage [] 用户名` + +略,不常用 +##### `userdel` 删除用户 +`userdel [-r] 用户名` + +-r 删除用户的同时删除用户家目录 +### 添加用户组 +`groupadd [] 组名` + +-g gid + +##### 修改用户组名 +`groupmod -n name1 name2` + +-n 修改组名 + +##### 删除用户组 +`groupdel groupName` + +注意:删组要先删用户 + + +##### 修改用户所在组 +`gpasswd -a source target` +##### 从组中删除用户 +`gpasswd -d source target` + +##### 添加已有用户到已有组 + +`usermod -a -G groupName userName` + +##### 给文件设置用户组 +'chgrp groupname file' \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\347\263\273\347\273\237\345\220\257\345\212\250\350\277\207\347\250\213\345\222\214\346\225\205\351\232\234\344\277\256\345\244\215.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\347\263\273\347\273\237\345\220\257\345\212\250\350\277\207\347\250\213\345\222\214\346\225\205\351\232\234\344\277\256\345\244\215.md" new file mode 100644 index 0000000..f31aaad --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\347\263\273\347\273\237\345\220\257\345\212\250\350\277\207\347\250\213\345\222\214\346\225\205\351\232\234\344\277\256\345\244\215.md" @@ -0,0 +1,70 @@ +bios > MBR + + +bios:检测计算机硬件是否有问题。 + +MBR:硬盘开头特殊分区,主引导扇区。大小512B。分别是:446节(引导程序grub)+64B分区表(16*4)+2B结束符。 + +grub:有效的将内核加载到内存。grub会生成一个微系统。文件放在`/boot/grub2/grub.cfg`,该配置文件由`grub2-mkconfig`工具生成,该工具通过读取`/etc/grub.d/`目录下的脚本来生成配置,该目录下的`00_header`文件从`/etc/default/grup`中获取参数来加载配置,每个脚本文件的功能不同,所以,如果我们想改一些grub配置,只需要修改`/etc/default/grup`文件中的参数,然后执行`grub2-mkconfig -o /boot/grub2/grub.cfg`来重新生成grub.cfg文件即可。 + + + +#### 一级修复:进入单用户模式 + +开机按`e`进入编辑,然后在linux内核信息那行最后边加`s`,然后ctrl+x启动。 + +单用户使得执行线程数减少。 + +#### 二级修复:进入应急模式 + +开机按`e`进入编辑,然后在linux内核信息那行最后边加`emergency`,然后ctrl+x启动。 + +会以最小的要求来启动系统。 + +#### :修复root密码 + +开机按`e`进入编辑,然后在linux内核信息那行最后边先删除`rhgb quiet`然后再加`init=/bin/sh`,然后ctrl+x启动。 + +然后`mount -o remount,rw /`挂载为可读可写 + +然后执行`passwd`命令。 + + +#### grub加密 +>那如果都这样改密码,系统岂不是很不安全。所以我们需要一些手段:修改grub机制 + +``` +[root@s169 grub2]# cd /etc/grub.d/ +[root@s169 grub.d]# ls +00_header 01_users 20_linux_xen 30_os-prober 41_custom +00_tuned 10_linux 20_ppc_terminfo 40_custom README +``` +然后修改`00_header`文件,在最后面插入两行 +``` +cat <为了网络通讯的提高性能和容灾备份,同时将两个网卡与交换机来通讯,能增大通信速率,但是并不是简单的1+1,而是逻辑上将两个网卡合成一个网卡(称作term)然后与交换机通信,此时,term可以看做master节点,两个本机网卡可以看做是slave节点。 + +优点:双卡热备、负载均衡 + +### 配置 + +###### 添加team + +`nmcli connection add type team ifname team0 config {\"runner\":{\"name\":\"activebackup\"}}` + +注意要转义冒号。 +``` +[root@fantj network-scripts]# nmcli connection add type team ifname \ +> team0 config {"runner":{"name":"activebackup"}} +Connection 'team-team0' (86b19368-5b73-4ade-b5ff-f765c6e24856) successfully added. +[root@fantj network-scripts]# nmcli connection show +名称 UUID 类型 设备 +eth2 15ee8a85-8003-4ac2-8c5b-642efa0ca832 802-3-ethernet -- +ens33 13756690-ac77-b776-4fc1-f5535cee6f16 802-3-ethernet eno16777736 +eth1 bd5b981e-3518-4dee-8aad-0b756622ab55 802-3-ethernet -- +team-team0 86b19368-5b73-4ade-b5ff-f765c6e24856 team -- +``` + +##### 给team配置ip +赋予ip:`nmcli connection modify team-team0 ipv4.address "192.168.27.155/24"` + +改为手动: +`nmcli connection modify team-team0 ipv4.method manual` + +##### 给team配置slave +`nmcli connection add type team-slave con-name team0-port1 ifname eth1 master team-team0` + +同理,给网卡二也加入slave + + +##### 关闭原来网卡,开启slave节点网卡 +`nmcli connection up/down xxx ` +然后会发现,三个网卡的ip地址是一样的。 + + +##### team状态管理 + +`teamnl teamname state` + + +##### teamm设置属性值 +获取属性值: +`teamnl teamname getoption xxx` + + +设置属性值: +`teamnl teamname setoption xxx` + + + +### 桥接 + +>桥,与交换机的功能相似,当有数据到达时,会根据报文中的 MAC 信息进行广播、转发、丢弃处理。 + + +##### 创建交换机 +以前的方式: +`brctl addbr br0 +` + +rhel7方式:` nmcli connection add con-name br1 type bridge ifname br2 +` +##### 查看交换机 + +`nmcli connection show ` +``` +[root@fantj network-scripts]# nmcli connection show +名称 UUID 类型 设备 +team-team0 86b19368-5b73-4ade-b5ff-f765c6e24856 team -- +ens33 13756690-ac77-b776-4fc1-f5535cee6f16 802-3-ethernet eno33554992 +ethernet-eth2 74563dc7-d64c-4dde-b8a5-1f67a1c0465e 802-3-ethernet eno16777736 +br0 eafdc224-ff09-4fa6-bfd1-fd75d3a9e6f9 bridge br0 +``` + +##### 创建桥接 + +`nmcli connection add type bridge-slave con-name br0-port1 ifname eth1 master br0` \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\350\275\257\344\273\266\345\214\205\347\256\241\347\220\206rpm.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\350\275\257\344\273\266\345\214\205\347\256\241\347\220\206rpm.md" new file mode 100644 index 0000000..3aefe6f --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\350\275\257\344\273\266\345\214\205\347\256\241\347\220\206rpm.md" @@ -0,0 +1,74 @@ +### 软件包分类 +1. 源码包:脚本安装包:安装慢但能看到源码 +2. 二进制包(RPM、系统默认包):安装快但不知道源码 + +#### rpm包命名规则 +``` +httpd-2.215-15.e16.centos.1.i686,rpm + +httpd 软件包名 +2.2.15 软件版本 +15 软件发布次数 +e16.centos 适合的linux平台 +i686 适合的硬件平台 +rpm rpm包扩展名 + +``` + +#### rpm包依赖性 + +树形依赖:a>b>c +环形依赖:a>b>c>a 需要abc同时装 +模块依赖:查询网站 www.rpmfind.net + + +### RPM安装 +`rpm -ivh 包全名` + +-i install +-v 显示详细信息verbose +-h 显示进度hash +--nodeps 不检测依赖性 + +### RPM包查询 +`rpm -qa |grep xxx` + +### RPM包升级 +`rpm -Uvh 包全名` +-U upgrade升级 + +### RPM包卸载 +`rpm -e 包名` +-e erase卸载 +--nodeps 不检查依赖性 + + +### 查询RPM包是否安装 +`rpm -q 包名` +-q query查询 +-a all所有 + +### 查询RPM包详细信息 +`rpm -qi 包名` +-p package包信息 +-i information软件信息 + +### 查询RPM包中文件安装位置 +`rpm -ql 包名` + +-l list列表 + +-p 包信息package + +### 查询软件包的依赖性 +`rpm -qR 包名` + +-R 依赖性requires + +### RPM包校验 +>可以查看对原生包做的修改 + +`rpm -V 已安装包名` +-V verify校验 + + diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\351\202\256\344\273\266\346\234\215\345\212\241.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\351\202\256\344\273\266\346\234\215\345\212\241.md" new file mode 100644 index 0000000..e7ed929 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\351\202\256\344\273\266\346\234\215\345\212\241.md" @@ -0,0 +1,61 @@ +>rhel默认有postfix服务并且默认激活。默认端口25. + +``` +[root@fantj ~]# systemctl is-enabled postfix.service +enabled +[root@fantj ~]# systemctl is-active postfix.service +active +``` + +日志地址:`/var/log/maillog` + +### 1. 打开端口/服务 + +`firewall-cmd --permanent --add-port=25/tcp` +``` +[root@fantj ~]# firewall-cmd --permanent --add-port=25/tcp +success +``` +### 2. 配置文件 + +`/etc/postfix` + +``` +[root@fantj ~]# cd /etc/postfix/ +[root@fantj postfix]# ls +access canonical generic header_checks main.cf master.cf relocated transport virtual +``` +###### 查看所有默认参数 + +`postconf -d`统计行信息:`postconf -d | wc -l` + +###### 查看某个参数 +`postconf paramName` + +###### 命令行修改参数 + +`postconf -e paramName=xxxx` + +##### 主要配置文件1:`main.cf` +>主要控制组件和队列的信息,一般不需要修改,默认值就好。 + +1. `myhostname`和`mydomain`取值。`myhostname`默认取本机`hostname`,`mydomain`从m`yhostname`的第一个点后开始取值。。 +2. `myorigin`指的是@后面的值。 +3. `inet_interfaces = all`对所有ip开启端口。相当于0.0.0.0 +4. `inet_protocols=all`支持ipv4和ipv6 +5. `mydestination=` + +``` + +5. mydestination = $myhostname, localhost.$mydomain, localhost ,$mydomain + 设置目的地,若@后面的值(也就是myorigin值)在这里找不到,邮件将发送不出去 + +6. local_transport 收到邮件后如何进行投递和转发,默认值local:$myhostname + +7. relayhost =[ip.xx.xx.xx] 设置邮件网关,任何邮件都会被转发到此ip处理 + + +``` + +##### 主要配置文件2:`master.cf` +> \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\351\230\262\347\201\253\345\242\231.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\351\230\262\347\201\253\345\242\231.md" new file mode 100644 index 0000000..e1edd59 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\351\230\262\347\201\253\345\242\231.md" @@ -0,0 +1,215 @@ +### 1. 系统自带防火墙tcpwraps +>系统自带两个文件`/etc/hosts.allow`和`/etc/hosts.deny` + +###### 匹配规则: +1. 首先到/etc/hosts.allow里面去匹配,如果匹配成功,则直接通过 +2. 在allow里没有匹配成功的话,则到/etc/hosts.deny里去匹配,如果在deny匹配成功,则被拒绝,否则会通过。 + + +###### 测试: + +配置allow规则:`sshd : xxx.xxx.xxx.xx` +``` +[root@fantj etc]# cat hosts.allow +# +# hosts.allow This file contains access rules which are used to +sshd : 192.168.27.1 +``` +配置deny同理。 + +配置允许一个网段:`sshd : 192.168.27. EXCEPT 192.168.27.110`允许192.168.27这个网段中除了110ip以外的ip访问。 + +配置deny同理。 + +如果allow和deny同时都有一个相同的ip,则会允许,因为系统扫完allow确定了allow结果,不会再扫deny了。 + +写完立即生效,不需要重启服务。 + +tcpwraps所支持的服务是有限的。一般就是限制ssh。 + + +### 2. firewall +动态防火墙,所设置的配置立即生效。 +>在rhel7中,默认同时存在3中防火墙:firewalld iptables ebtables,后两者默认没有启用,firewall默认是启用的。因为三个daemon胡想冲突,建议mask其他两种daemon。 + +mask 是屏蔽(让它不能启动)的意思。 + +##### 两个工具 +firewall底层调用iptables命令,工具有两个:`firewall-cmd`和`firewall-config`图形化。 + + +##### 查看有多少个域 +>什么是域。系统中有默认的zone,不经过zone过滤不能进入内核,这些域过滤的强度不同,也就是不同的zone里对数据流量的信任度不同,信任排序:(默认域是public) + +注意:**一个网络接口只能连接一个域**。比如:eth1连接drop域,用来拒绝这个网络接口的所有请求。 + +1. drop:所有进来的流量直接丢弃 +2. block:所有进来的流量也被拒绝。允许通过开机时候dhcp获取ip。 +3. public:默认允许几个服务可以通过,可通过`firewall-cmd --list-services`查看。如果需要某个服务通过,将服务添加至public即可。 +4. trusted:所有进来的流量直接请求。(一般与内网接口相接) + +如果一个接口没有zone,则会使用默认规则,默认规则可通过`firewall-cmd --get-default-zone`查看。 + +###### 给接口添加zone +`firewall-cmd --zone=public --add-interface=eth0` + +###### 给接口修改zone +`firewall-cmd --zone=trusted --change-interface=eth0` + +###### 从域中移除接口 +`firewall-cmd --zone=trusted --remove-interface=eth0` + +###### 获取域信息 + +`firewall-cmd --list-all-zones`或者`firewall-cmd --get-zones` + +###### 获取默认域 + +`firewall-cmd --get-default-zone` + +``` +[root@fantj etc]# firewall-cmd --get-default-zone +public +``` + +###### 修改默认域 + +`firewall-cmd --set-default-zone=external` + +###### 查看所支持的服务 +`firewall-cmd --get-services` + + +###### 查看public域里开启的服务 + +`firewall-cmd --zone=public --list-services` + +也可以用`firewall-cmd --zone=public --list-all`来查看详情 + +###### 从域中取消服务 +`firewall-cmd --zone=public --remove-service=httpd` +同理,添加是`--add-service` + +###### 查看网卡接口所属域 +>上文讲过一个接口对应一个域。 + +`firewall-cmd --get-zone-of-interface=eth0` + +###### firewall服务重载 + +`firewall-cmd --reload` + +###### 查看域中开放端口 + +`firewall-cmd --zone=public query-port=80/tcp` + +###### 开启域中端口 +`firewall-cmd --zone=public --add-port=80/tcp` + +###### 关闭域中端口 +`firewall-cmd --zone=public --remove-port=80/tcp` + + +#### 2.2 设置ip源去哪个域 +>设置凡是来自192.168.27.0/24这个网段的数据包都走public这个域。 + + +###### 添加源 +>和一个接口对应一个zone一样,一个源也是对应一个zone。 + +添加源:`firewall-cmd --zone=public --add-source=192.168.27.0/24` + +查看源列表:`firewall-cmd --zone=public --list-sources` + +#### 2.3 特殊的 ping 规则 + +>ping是ICMP协议,默认不选是允许的,选中是拒绝的。ping有两个服务:`echo-request`和`echo-reply`. + +#### 2.4 启动应急模式 +>拒绝所有访问。 +`firewall-cmd --panic-on` + +同理,关闭为`firewall-cmd --panic-off` + +#### 2.5 永久修改 + +`firewall-cmd --permanent xxxx`及时成效。 + +#### 2.6 请求转发 + + +###### 本地端口转发至别的ip: +`firewall-cmd --add-forward-port=port=80:proto=tcp:toaddr=192.168.27.120` + +###### 本地端口转发至别的ip端口 + +将本地的80端口的请求转发到120的8080端口: +`firewall-cmd --add-forward-port=port=80:proto=tcp:toaddr=192.168.27.120:toport=8080` + +###### 删除请求转发 + +把`add` 改为`remove` +###### 查看请求转发列表 + +`firewall-cmd --list-forward-ports` + + +#### 2.7 富规则 + +格式:`firewall-cmd --add-rich-rule='rule xxx'` + +`firewall-cmd --add-rich-rule='rule family=ipv4 source address=192.168.111.0/24'` +默认会给源地址加伪装。 + + +###### 查看富规则列表 + +`firewall-cmd --list-rich-rules` + +###### 删除富规则 +`firewall-cmd --remove=rich-rule='xxxxx'` + +#### 2.8 伪装 +>给所有的子网做NAT转换。但是如果需要对子网网段的权限做划分(192.168.27可以访问,192.168.28不能访问)的话,就不适合使用了。可以用富规则去实现。 + +###### 设置伪装 +`firewall-cmd --add-masquerade` + + +注意:该设置对全局子网有效,如果子网中有不同权限的分段处理操作,请用富规则来实现。 + +#### 2.9 限流 + +`firewall-cmd --add-rich-rule='rule family=ipv4 service name=ftp limit value=2/m reject'` + +service name=ftp,表示ftp服务 + +2表示包个数,/m表示分钟。 + + +### SELinux(了解) + + +###### 查看所有端口上下文 + +`semanage port -l` + +``` +[root@fantj ~]# semanage port -l +SELinux 端口类型 协议 端口号 + +afs3_callback_port_t tcp 7001 +afs3_callback_port_t udp 7001 +``` + +###### 给端口添加上下文 + +`semanage port -a -t afs3_callback_port_t -p tcp 7002` 给`afs3_callback_port_t`添加70002端口上下文。 + +###### 删除端口上下文 + +-d + +###### 修改端口上下文 + +-m diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/\347\275\221\347\273\234\345\221\275\344\273\244.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/\347\275\221\347\273\234\345\221\275\344\273\244.md" new file mode 100644 index 0000000..922a6d3 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/\347\275\221\347\273\234\345\221\275\344\273\244.md" @@ -0,0 +1,110 @@ +### write +>给用户发信息,ctrl+d保存结束。 + +条件:用户必须在线 + +### wall +>给所有用户发信息(广播信息) + +`wall hello` + + +### ping +>给远程计算机发信息包 + +`ping www.baidu.com` + +-c 可以指定ping几次 + + +### ifconfig +>查看当前网络信息 + +eth 代表真实网卡 +lo 代表回还网卡(127.0.0.1) + + +### mail +>查看发送电子邮件 + +`mail root` +ctrl+d 发送 + + +### last +>查看目前和过去用户登录信息 + + +``` +[root@FantJ ~]# last +用户 终端 ip地址 日期 状态 +root pts/1 101.7.155.145 Sat Sep 15 18:19 still logged in(当前在线) +root pts/1 101.7.180.144 Thu Sep 13 19:41 - 22:42 (03:01) (持续三分钟) +root pts/1 1.68.71.222 Wed Sep 12 15:24 - 16:15 (00:51) +root pts/1 218.26.72.133 Tue Sep 11 21:30 - 22:08 (00:37) +root pts/2 101.7.148.254 Mon Sep 10 19:40 - 21:32 (01:51) +``` +### lastlog +>查看某特性用户上次登录时间 + +``` +[root@FantJ ~]# lastlog +Username Port From Latest +root pts/1 101.7.155.145 Sat Sep 15 18:19:37 +0800 2018 +bin **Never logged in** +daemon **Never logged in** +el pts/1 Wed Apr 25 20:30:47 +0800 2018 +es pts/1 Sat Apr 28 13:17:40 +0800 2018 +``` + +查看某用户登录记录:`lastlog -u uid` + +### traceroute +>显示数据包到主机间的路径 + + +``` +traceroute www.baidu.com +``` + +### netstat +>显示网络相关信息 + +-t tcp协议 +-u udp协议 +-l 监听 +-r 路由 +-n 显示ip地址和端口号 + +示例: +``` +netstat -tlun 查看本机监听端口 +netstat -an 查看本机所有网络连接 +netstat -rn 查看本机路由表 +``` +``` +[root@FantJ ~]# netstat -tlun +Active Internet connections (only servers) +Proto Recv-Q Send-Q Local Address Foreign Address State +tcp 0 0 0.0.0.0:9000 0.0.0.0:* LISTEN +tcp 0 0 0.0.0.0:9002 0.0.0.0:* LISTEN +tcp 0 0 0.0.0.0:9003 0.0.0.0:* LISTEN +tcp 0 0 0.0.0.0:26379 0.0.0.0:* LISTEN +tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN + +... +[root@FantJ ~]# netstat -rn +Kernel IP routing table +Destination Gateway Genmask Flags MSS Window irtt Iface +0.0.0.0 172.31.239.253 0.0.0.0 UG 0 0 0 eth0 +169.254.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0 +172.31.224.0 0.0.0.0 255.255.240.0 U 0 0 0 eth0 + +``` + +### setup +过时了 + + +### NetworkManager + 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