当前位置:   article > 正文

实战算法的基础入门

实战算法的基础入门

9532e7150dc15d6468cf9e5ec25c0e7d.gif

关于实战算法都需要了解哪些?一文带你详细了解,欢迎收藏!

  URL黑名单(布隆过滤器)

100亿黑名单URL,每个64B,问这个黑名单要怎么存?判断一个URL是否在黑名单中

散列表

如果把黑名单看成一个集合,将其存在hashmap中,貌似太大了,需要640G,明显不科学。

布隆过滤器

它实际上是一个很长的二进制矢量和一系列随机映射函数。

它可以用来判断一个元素是否在一个集合中。它的优势是只需要占用很小的内存空间以及有着高效的查询效率。对于布隆过滤器而言,它的本质是一个位数组:位数组就是数组的每个元素都只占用1 bit,并且每个元素只能是0或者1。

在数组中的每一位都是二进制位。布隆过滤器除了一个位数组,还有K个哈希函数。当一个元素加入布隆过滤器中的时候,会进行如下操作:

  • 使用K个哈希函数对元素值进行K次计算,得到K个哈希值。

  • 根据得到的哈希值,在位数组中把对应下标的值置为1。


  词频统计(分文件)

2GB内存在20亿整数中找到出现次数最多的数

通常做法是使用哈希表对出现的每一个数做词频统计,哈希表的key是某个整数,value记录整数出现的次数。本题的数据量是20亿,有可能一个数出现20亿次,则为了避免溢出,哈希表的key是32位(4B),value也是32位(4B),那么一条哈希表的记录就需要占用8B。

当哈希表记录数为2亿个时,需要16亿个字节数(8*2亿),需要至少1.6GB内存(16亿/2^30,1GB==2^30个字节==10亿)。则20亿个记录,至少需要16GB的内存,不符合题目要求。

解决办法是将20亿个数的大文件利用哈希函数分成16个小文件,根据哈希函数可以把20亿条数据均匀分布到16个文件上,同一种数不可能被哈希函数分到不同的小文件上,假设哈希函数够好。然后对每一个小文件用哈希函数来统计其中每种数出现的次数,这样我们就得到16个文件中出现次数最多的数,接着从16个数中选出次数最大的那个key即可。

  未出现的数(bit数组)
  • 40亿个非负整数中找到没有出现的数

对于原问题,如果使用哈希表来保存出现过的数,那么最坏情况下是40亿个数都不相同,那么哈希表则需要保存40亿条数据,一个32位整数需要4B,那么40亿*4B= 160亿个字节,一般大概10亿个字节的数据需要1G的空间,那么大概需要16G的空间,这不符合要求。

我们换一种方式,申请一个bit数组,数组大小为4294967295,大概为40亿bit,40亿/8=5亿字节,那么需要0.5G空间,bit数组的每个位置有两种状态0和1,那么怎么使用这个bit数组呢?呵呵,数组的长度刚好满足我们整数的个数范围,那么数组的每个下标值对应4294967295中的一个数,逐个遍历40亿个无符号数,例如,遇到20,则bitArray[20]=1;遇到666,则bitArray[666]=1,遍历完所有的数,将数组相应位置变为1。

  • 40亿个非负整数中找到一个没有出现的数,内存限制10MB

10亿个字节的数据大概需要1GB空间处理,那么10MB内存换算过来就是可以处理1千万字节的数据,也就是8千万bit,对于40亿非负整数如果申请bit数组的话,40亿bit /0.8亿bit=50,那么这样最少也得分50块来处理,下面就以64块来进行分析解答吧。

  • 总结一下进阶的解法

  1. 根据10MB的内存限制,确定统计区间的大小,就是第二次遍历时的bitArr大小。

  2. 利用区间计数的方式,找到那个计数不足的区间,这个区间上肯定有没出现的数。

  3. 对这个区间上的数做bit map映射,再遍历bit map,找到一个没出现的数即可。

  • 自己的想法

如果只是找一个数,可以高位模运算,写到64个不同的文件,然后在最小的文件中通过bitArray一次处理掉。

  • 40亿个无符号整数,1GB内存,找到所有出现两次的数

对于原问题,可以用bit map的方式来表示数出现的情况。具体地说,是申请一个长度为4294967295×2的bit类型的数组bitArr,用2个位置表示一个数出现的词频,1B占用8个bit,所以长度为4294967295×2的bit类型的数组占用1GB空间。怎么使用这个bitArr数组呢?遍历这40亿个无符号数,如果初次遇到num,就把bitArr[num2+1]和bitArr[num2]设置为01,如果第二次遇到num,就把bitArr[num2+1]和bitArr[num2]设置为10,如果第三次遇到num,就把bitArr[num2+1]和bitArr[num2]设置为11。以后再遇到num,发现此时bitArr[num2+1]和bitArr[num2]已经被设置为11,就不再做任何设置。遍历完成后,再依次遍历bitArr,如果发现bitArr[i2+1]和bitArr[i2]设置为10,那么i就是出现了两次的数。

  重复URL(分机器)
  • 找到100亿个URL中重复的URL

原问题的解法使用解决大数据问题的一种常规方法:把大文件通过哈希函数分配到机器,或者通过哈希函数把大文件拆成小文件。一直进行这种划分,直到划分的结果满足资源限制的要求。首先,你要向面试官询问在资源上的限制有哪些,包括内存、计算时间等要求。在明确了限制要求之后,可以将每条URL通过哈希函数分配到若干机器或者拆分成若干小文件,这里的“若干”由具体的资源限制来计算出精确的数量。

例如,将100亿字节的大文件通过哈希函数分配到100台机器上,然后每一台机器分别统计分给自己的URL中是否有重复的URL,同时哈希函数的性质决定了同一条URL不可能分给不同的机器;或者在单机上将大文件通过哈希函数拆成1000个小文件,对每一个小文件再利用哈希表遍历,找出重复的URL;或者在分给机器或拆完文件之后,进行排序,排序过后再看是否有重复的URL出现。总之,牢记一点,很多大数据问题都离不开分流,要么是哈希函数把大文件的内容分配给不同的机器,要么是哈希函数把大文件拆成小文件,然后处理每一个小数量的集合。

  TOPK搜索(小根堆)
  • 海量搜索词汇,找到最热TOP100词汇的方法

最开始还是用哈希分流的思路来处理,把包含百亿数据量的词汇文件分流到不同的机器上,具体多少台机器由面试官规定或者由更多的限制来决定。对每一台机器来说,如果分到的数据量依然很大,比如,内存不够或其他问题,可以再用哈希函数把每台机器的分流文件拆成更小的文件处理。

处理每一个小文件的时候,哈希表统计每种词及其词频,哈希表记录建立完成后,再遍历哈希表,遍历哈希表的过程中使用大小为100的小根堆来选出每一个小文件的top100(整体未排序的top100)。每一个小文件都有自己词频的小根堆(整体未排序的top100),将小根堆里的词按照词频排序,就得到了每个小文件的排序后top100。然后把各个小文件排序后的top100进行外排序或者继续利用小根堆,就可以选出每台机器上的top100。不同机器之间的top100再进行外排序或者继续利用小根堆,最终求出整个百亿数据量中的top100。对于top K的问题,除哈希函数分流和用哈希表做词频统计之外,还经常用堆结构和外排序的手段进行处理。

  中位数(单向二分查找)
  • 10MB内存,找到100亿整数的中位数

  1. 内存够:内存够还慌什么啊,直接把100亿个全部排序了,你用冒泡都可以...然后找到中间那个就可以了。但是你以为面试官会给你内存??

  2. 内存不够:题目说是整数,我们认为是带符号的int,所以4字节,占32位。

假设100亿个数字保存在一个大文件中,依次读一部分文件到内存(不超过内存的限制),将每个数字用二进制表示,比较二进制的最高位(第32位,符号位,0是正,1是负),如果数字的最高位为0,则将这个数字写入file_0文件中;如果最高位为1,则将该数字写入file_1文件中。

从而将100亿个数字分成了两个文件,假设file_0文件中有60亿个数字,file_1文件中有40亿个数字。那么中位数就在file_0文件中,并且是file_0文件中所有数字排序之后的第10亿个数字。(file_1中的数都是负数,file_0中的数都是正数,也即这里一共只有40亿个负数,那么排序之后的第50亿个数一定位于file_0中)

现在,我们只需要处理file_0文件了(不需要再考虑file_1文件)。对于file_0文件,同样采取上面的措施处理:将file_0文件依次读一部分到内存(不超内存限制),将每个数字用二进制表示,比较二进制的次高位(第31位),如果数字的次高位为0,写入file_0_0文件中;如果次高位为1,写入file_0_1文件中。

现假设file_0_0文件中有30亿个数字,file_0_1中也有30亿个数字,则中位数就是:file_0_0文件中的数字从小到大排序之后的第10亿个数字。

抛弃file_0_1文件,继续对file_0_0文件根据次次高位(第30位)划分,假设此次划分的两个文件为:file_0_0_0中有5亿个数字,file_0_0_1中有25亿个数字,那么中位数就是file_0_0_1文件中的所有数字排序之后的 第5亿个数。

按照上述思路,直到划分的文件可直接加载进内存时,就可以直接对数字进行快速排序,找出中位数了。

  短域名系统(缓存)
  • 设计短域名系统,将长URL转化成短的URL.

  1. 利用放号器,初始值为0,对于每一个短链接生成请求,都递增放号器的值,再将此值转换为62进制(a-zA-Z0-9),比如第一次请求时放号器的值为0,对应62进制为a,第二次请求时放号器的值为1,对应62进制为b,第10001次请求时放号器的值为10000,对应62进制为sBc。

  2. 将短链接服务器域名与放号器的62进制值进行字符串连接,即为短链接的URL,比如:t.cn/sBc。

  3. 重定向过程:生成短链接之后,需要存储短链接到长链接的映射关系,即sBc ->URL,浏览器访问短链接服务器时,根据URL Path取到原始的链接,然后进行302重定向。映射关系可使用K-V存储,比如Redis或Memcache。

  海量评论入库(消息队列)

假设有这么一个场景,有一条新闻,新闻的评论量可能很大,如何设计评论的读和写

前端页面直接给用户展示、通过消息队列异步方式入库

读可以进行读写分离、同时热点评论定时加载到缓存


  在线/并发用户数(Redis)
  • 显示网站的用户在线数的解决思路

  1. 维护在线用户表

  2. 使用Redis统计

  • 显示网站并发用户数

  1. 每当用户访问服务时,把该用户的ID写入ZSORT队列,权重为当前时间;

  2. 根据权重(即时间)计算一分钟内该机构的用户数Zrange;

  3. 删掉一分钟以上过期的用户Zrem;


  热门字符串(前缀树)

假设目前有1000w个记录(这些查询串的重复度比较高,虽然总数是1000w,但如果除去重复后,则不超过300w个)。请统计最热门的10个查询串,要求使用的内存不能超过1G。(一个查询串的重复度越高,说明查询它的用户越多,也就越热门。)

  • HashMap法

虽然字符串总数比较多,但去重后不超过300w,因此,可以考虑把所有字符串及出现次数保存在一个HashMap中,所占用的空间为300w*(255+4)≈777M(其中,4 表示整数占用的4个字节)。由此可见,1G的内存空间完全够用。

  • 思路如下

首先,遍历字符串,若不在map中,直接存入map,value记为1;若在map中,则把对应的value加1,这一步时间复杂度O(N)

接着遍历map,构建一个10个元素的小顶堆,若遍历到的字符串的出现次数大于堆顶字符串的出现次数,则进行替换,并将堆调整为小顶堆。

遍历结束后,堆中10个字符串就是出现次数最多的字符串。这一步时间复杂度O(Nlog10)

  • 前缀树法

当这些字符串有大量相同前缀时,可以考虑使用前缀树来统计字符串出现的次数,树的结点保存字符串出现次数,0表示没有出现。

  • 思路如下

在遍历字符串时,在前缀树中查找,如果找到,则把结点中保存的字符串次数加1,否则为这个字符串构建新结点,构建完成后把叶子结点中字符串的出现次数置为1。

最后依然使用小顶堆来对字符串的出现次数进行排序。

  红包算法

线性切割法,一个区间切N-1刀。越早越多

二倍均值法,【0~剩余金额 / 剩余人数*2】中随机,相对均匀

d5374329aef177a0c720208ef086b00e.png

eeca14c324b11bffb11a8f4a580b4bfc.png


  手写快排
  1. public class QuickSort {
  2. public static void swap(int[] arr, int i, int j) {
  3. int tmp = arr[i];
  4. arr[i] = arr[j];
  5. arr[j] = tmp;
  6. }
  7. /* 常规快排 */
  8. public static void quickSort1(int[] arr, int L , int R) {
  9. if (L > R) return;
  10. int M = partition(arr, L, R);
  11. quickSort1(arr, L, M - 1);
  12. quickSort1(arr, M + 1, R);
  13. }
  14. public static int partition(int[] arr, int L, int R) {
  15. if (L > R) return -1;
  16. if (L == R) return L;
  17. int lessEqual = L - 1;
  18. int index = L;
  19. while (index < R) {
  20. if (arr[index] <= arr[R])
  21. swap(arr, index, ++lessEqual);
  22. index++;
  23. }
  24. swap(arr, ++lessEqual, R);
  25. return lessEqual;
  26. }
  27. /* 荷兰国旗 */
  28. public static void quickSort2(int[] arr, int L, int R) {
  29. if (L > R) return;
  30. int[] equalArea = netherlandsFlag(arr, L, R);
  31. quickSort2(arr, L, equalArea[0] - 1);
  32. quickSort2(arr, equalArea[1] + 1, R);
  33. }
  34. public static int[] netherlandsFlag(int[] arr, int L, int R) {
  35. if (L > R) return new int[] { -1, -1 };
  36. if (L == R) return new int[] { L, R };
  37. int less = L - 1;
  38. int more = R;
  39. int index = L;
  40. while (index < more) {
  41. if (arr[index] == arr[R]) {
  42. index++;
  43. } else if (arr[index] < arr[R]) {
  44. swap(arr, index++, ++less);
  45. } else {
  46. swap(arr, index, --more);
  47. }
  48. }
  49. swap(arr, more, R);
  50. return new int[] { less + 1, more };
  51. }
  52. // for test
  53. public static void main(String[] args) {
  54. int testTime = 1;
  55. int maxSize = 10000000;
  56. int maxValue = 100000;
  57. boolean succeed = true;
  58. long T1=0,T2=0;
  59. for (int i = 0; i < testTime; i++) {
  60. int[] arr1 = generateRandomArray(maxSize, maxValue);
  61. int[] arr2 = copyArray(arr1);
  62. int[] arr3 = copyArray(arr1);
  63. // int[] arr1 = {9,8,7,6,5,4,3,2,1};
  64. long t1 = System.currentTimeMillis();
  65. quickSort1(arr1,0,arr1.length-1);
  66. long t2 = System.currentTimeMillis();
  67. quickSort2(arr2,0,arr2.length-1);
  68. long t3 = System.currentTimeMillis();
  69. T1 += (t2-t1);
  70. T2 += (t3-t2);
  71. if (!isEqual(arr1, arr2) || !isEqual(arr2, arr3)) {
  72. succeed = false;
  73. break;
  74. }
  75. }
  76. System.out.println(T1+" "+T2);
  77. // System.out.println(succeed ? "Nice!" : "Oops!");
  78. }
  79. private static int[] generateRandomArray(int maxSize, int maxValue) {
  80. int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
  81. for (int i = 0; i < arr.length; i++) {
  82. arr[i] = (int) ((maxValue + 1) * Math.random())
  83. - (int) (maxValue * Math.random());
  84. }
  85. return arr;
  86. }
  87. private static int[] copyArray(int[] arr) {
  88. if (arr == null) return null;
  89. int[] res = new int[arr.length];
  90. for (int i = 0; i < arr.length; i++) {
  91. res[i] = arr[i];
  92. }
  93. return res;
  94. }
  95. private static boolean isEqual(int[] arr1, int[] arr2) {
  96. if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null))
  97. return false;
  98. if (arr1 == null && arr2 == null)
  99. return true;
  100. if (arr1.length != arr2.length)
  101. return false;
  102. for (int i = 0; i < arr1.length; i++)
  103. if (arr1[i] != arr2[i])
  104. return false;
  105. return true;
  106. }
  107. private static void printArray(int[] arr) {
  108. if (arr == null)
  109. return;
  110. for (int i = 0; i < arr.length; i++)
  111. System.out.print(arr[i] + " ");
  112. System.out.println();
  113. }
  114. }


  手写归并
  1. public static void merge(int[] arr, int L, int M, int R) {
  2. int[] help = new int[R - L + 1];
  3. int i = 0;
  4. int p1 = L;
  5. int p2 = M + 1;
  6. while (p1 <= M && p2 <= R)
  7. help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
  8. while (p1 <= M)
  9. help[i++] = arr[p1++];
  10. while (p2 <= R)
  11. help[i++] = arr[p2++];
  12. for (i = 0; i < help.length; i++)
  13. arr[L + i] = help[i];
  14. }
  15. public static void mergeSort(int[] arr, int L, int R) {
  16. if (L == R)
  17. return;
  18. int mid = L + ((R - L) >> 1);
  19. process(arr, L, mid);
  20. process(arr, mid + 1, R);
  21. merge(arr, L, mid, R);
  22. }
  23. public static void main(String[] args) {
  24. int[] arr1 = {9,8,7,6,5,4,3,2,1};
  25. mergeSort(arr, 0, arr.length - 1);
  26. printArray(arr);
  27. }


  手写堆排
  1. // 堆排序额外空间复杂度O(1)
  2. public static void heapSort(int[] arr) {
  3. if (arr == null || arr.length < 2)
  4. return;
  5. for (int i = arr.length - 1; i >= 0; i--)
  6. heapify(arr, i, arr.length);
  7. int heapSize = arr.length;
  8. swap(arr, 0, --heapSize);
  9. // O(N*logN)
  10. while (heapSize > 0) { // O(N)
  11. heapify(arr, 0, heapSize); // O(logN)
  12. swap(arr, 0, --heapSize); // O(1)
  13. }
  14. }
  15. // arr[index]刚来的数,往上
  16. public static void heapInsert(int[] arr, int index) {
  17. while (arr[index] > arr[(index - 1) / 2]) {
  18. swap(arr, index, (index - 1) / 2);
  19. index = (index - 1) / 2;
  20. }
  21. }
  22. // arr[index]位置的数,能否往下移动
  23. public static void heapify(int[] arr, int index, int heapSize) {
  24. int left = index * 2 + 1; // 左孩子的下标
  25. while (left < heapSize) { // 下方还有孩子的时候
  26. // 两个孩子中,谁的值大,把下标给largest
  27. // 1)只有左孩子,left -> largest
  28. // 2) 同时有左孩子和右孩子,右孩子的值<= 左孩子的值,left -> largest
  29. // 3) 同时有左孩子和右孩子并且右孩子的值> 左孩子的值, right -> largest
  30. int largest = left+1 < heapSize && arr[left+1]> arr[left] ? left+1 : left;
  31. // 父和较大的孩子之间,谁的值大,把下标给largest
  32. largest = arr[largest] > arr[index] ? largest : index;
  33. if (largest == index)
  34. break;
  35. swap(arr, largest, index);
  36. index = largest;
  37. left = index * 2 + 1;
  38. }
  39. }
  40. public static void swap(int[] arr, int i, int j) {
  41. int tmp = arr[i];
  42. arr[i] = arr[j];
  43. arr[j] = tmp;
  44. }
  45. public static void main(String[] args) {
  46. int[] arr1 = {9,8,7,6,5,4,3,2,1};
  47. heapSort(arr1);
  48. printArray(arr1);
  49. }

  手写单例
  1. public class Singleton {
  2. private volatile static Singleton singleton;
  3. private Singleton() {}
  4. public static Singleton getSingleton() {
  5. if (singleton == null) {
  6. synchronized (Singleton.class) {
  7. if (singleton == null) {
  8. singleton = new Singleton();
  9. }
  10. }
  11. }
  12. return singleton;
  13. }
  14. }

  手写LRUcache
  1. // 基于linkedHashMap
  2. public class LRUCache {
  3. private LinkedHashMap<Integer,Integer> cache;
  4. private int capacity; //容量大小
  5. public LRUCache(int capacity) {
  6. cache = new LinkedHashMap<>(capacity);
  7. this.capacity = capacity;
  8. }
  9. public int get(int key) {
  10. //缓存中不存在此key,直接返回
  11. if(!cache.containsKey(key)) {
  12. return -1;
  13. }
  14. int res = cache.get(key);
  15. cache.remove(key); //先从链表中删除
  16. cache.put(key,res); //再把该节点放到链表末尾处
  17. return res;
  18. }
  19. public void put(int key,int value) {
  20. if(cache.containsKey(key)) {
  21. cache.remove(key); //已经存在,在当前链表移除
  22. }
  23. if(capacity == cache.size()) {
  24. //cache已满,删除链表头位置
  25. Set<Integer> keySet = cache.keySet();
  26. Iterator<Integer> iterator = keySet.iterator();
  27. cache.remove(iterator.next());
  28. }
  29. cache.put(key,value); //插入到链表末尾
  30. }
  31. }
  1. //手写双向链表
  2. class LRUCache {
  3. class DNode {
  4. DNode prev;
  5. DNode next;
  6. int val;
  7. int key;
  8. }
  9. Map<Integer, DNode> map = new HashMap<>();
  10. DNode head, tail;
  11. int cap;
  12. public LRUCache(int capacity) {
  13. head = new DNode();
  14. tail = new DNode();
  15. head.next = tail;
  16. tail.prev = head;
  17. cap = capacity;
  18. }
  19. public int get(int key) {
  20. if (map.containsKey(key)) {
  21. DNode node = map.get(key);
  22. removeNode(node);
  23. addToHead(node);
  24. return node.val;
  25. } else {
  26. return -1;
  27. }
  28. }
  29. public void put(int key, int value) {
  30. if (map.containsKey(key)) {
  31. DNode node = map.get(key);
  32. node.val = value;
  33. removeNode(node);
  34. addToHead(node);
  35. } else {
  36. DNode newNode = new DNode();
  37. newNode.val = value;
  38. newNode.key = key;
  39. addToHead(newNode);
  40. map.put(key, newNode);
  41. if (map.size() > cap) {
  42. map.remove(tail.prev.key);
  43. removeNode(tail.prev);
  44. }
  45. }
  46. }
  47. public void removeNode(DNode node) {
  48. DNode prevNode = node.prev;
  49. DNode nextNode = node.next;
  50. prevNode.next = nextNode;
  51. nextNode.prev = prevNode;
  52. }
  53. public void addToHead(DNode node) {
  54. DNode firstNode = head.next;
  55. head.next = node;
  56. node.prev = head;
  57. node.next = firstNode;
  58. firstNode.prev = node;
  59. }
  60. }

  手写线程池
  1. package com.concurrent.pool;
  2. import java.util.HashSet;
  3. import java.util.Set;
  4. import java.util.concurrent.ArrayBlockingQueue;
  5. import java.util.concurrent.BlockingQueue;
  6. public class MySelfThreadPool {
  7. //默认线程池中的线程的数量
  8. private static final int WORK_NUM = 5;
  9. //默认处理任务的数量
  10. private static final int TASK_NUM = 100;
  11. private int workNum;//线程数量
  12. private int taskNum;//任务数量
  13. private final Set<WorkThread> workThreads;//保存线程的集合
  14. private final BlockingQueue<Runnable> taskQueue;//阻塞有序队列存放任务
  15. public MySelfThreadPool() {
  16. this(WORK_NUM, TASK_NUM);
  17. }
  18. public MySelfThreadPool(int workNum, int taskNum) {
  19. if (workNum <= 0) workNum = WORK_NUM;
  20. if (taskNum <= 0) taskNum = TASK_NUM;
  21. taskQueue = new ArrayBlockingQueue<>(taskNum);
  22. this.workNum = workNum;
  23. this.taskNum = taskNum;
  24. workThreads = new HashSet<>();
  25. //启动一定数量的线程数,从队列中获取任务处理
  26. for (int i=0;i<workNum;i++) {
  27. WorkThread workThread = new WorkThread("thead_"+i);
  28. workThread.start();
  29. workThreads.add(workThread);
  30. }
  31. }
  32. public void execute(Runnable task) {
  33. try {
  34. taskQueue.put(task);
  35. } catch (InterruptedException e) {
  36. // TODO Auto-generated catch block
  37. e.printStackTrace();
  38. }
  39. }
  40. public void destroy() {
  41. System.out.println("ready close thread pool...");
  42. if (workThreads == null || workThreads.isEmpty()) return ;
  43. for (WorkThread workThread : workThreads) {
  44. workThread.stopWork();
  45. workThread = null;//help gc
  46. }
  47. workThreads.clear();
  48. }
  49. private class WorkThread extends Thread{
  50. public WorkThread(String name) {
  51. super();
  52. setName(name);
  53. }
  54. @Override
  55. public void run() {
  56. while (!interrupted()) {
  57. try {
  58. Runnable runnable = taskQueue.take();//获取任务
  59. if (runnable !=null) {
  60. System.out.println(getName()+" readyexecute:"+runnable.toString());
  61. runnable.run();//执行任务
  62. }
  63. runnable = null;//help gc
  64. } catch (Exception e) {
  65. interrupt();
  66. e.printStackTrace();
  67. }
  68. }
  69. }
  70. public void stopWork() {
  71. interrupt();
  72. }
  73. }
  74. }
  75. package com.concurrent.pool;
  76. public class TestMySelfThreadPool {
  77. private static final int TASK_NUM = 50;//任务的个数
  78. public static void main(String[] args) {
  79. MySelfThreadPool myPool = new MySelfThreadPool(3,50);
  80. for (int i=0;i<TASK_NUM;i++) {
  81. myPool.execute(new MyTask("task_"+i));
  82. }
  83. }
  84. static class MyTask implements Runnable{
  85. private String name;
  86. public MyTask(String name) {
  87. this.name = name;
  88. }
  89. public String getName() {
  90. return name;
  91. }
  92. public void setName(String name) {
  93. this.name = name;
  94. }
  95. @Override
  96. public void run() {
  97. try {
  98. Thread.sleep(1000);
  99. } catch (InterruptedException e) {
  100. // TODO Auto-generated catch block
  101. e.printStackTrace();
  102. }
  103. System.out.println("task :"+name+" end...");
  104. }
  105. @Override
  106. public String toString() {
  107. // TODO Auto-generated method stub
  108. return "name = "+name;
  109. }
  110. }
  111. }

  手写消费者生产者模式
  1. public class Storage {
  2. private static int MAX_VALUE = 100;
  3. private List<Object> list = new ArrayList<>();
  4. public void produce(int num) {
  5. synchronized (list) {
  6. while (list.size() + num > MAX_VALUE) {
  7. System.out.println("暂时不能执行生产任务");
  8. try {
  9. list.wait();
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. for (int i = 0; i < num; i++) {
  15. list.add(new Object());
  16. }
  17. System.out.println("已生产产品数"+num+" 仓库容量"+list.size());
  18. list.notifyAll();
  19. }
  20. }
  21. public void consume(int num) {
  22. synchronized (list) {
  23. while (list.size() < num) {
  24. System.out.println("暂时不能执行消费任务");
  25. try {
  26. list.wait();
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. }
  30. }
  31. for (int i = 0; i < num; i++) {
  32. list.remove(0);
  33. }
  34. System.out.println("已消费产品数"+num+" 仓库容量" + list.size());
  35. list.notifyAll();
  36. }
  37. }
  38. }
  39. public class Producer extends Thread {
  40. private int num;
  41. private Storage storage;
  42. public Producer(Storage storage) {
  43. this.storage = storage;
  44. }
  45. public void setNum(int num) {
  46. this.num = num;
  47. }
  48. public void run() {
  49. storage.produce(this.num);
  50. }
  51. }
  52. public class Customer extends Thread {
  53. private int num;
  54. private Storage storage;
  55. public Customer(Storage storage) {
  56. this.storage = storage;
  57. }
  58. public void setNum(int num) {
  59. this.num = num;
  60. }
  61. public void run() {
  62. storage.consume(this.num);
  63. }
  64. }
  65. public class Test {
  66. public static void main(String[] args) {
  67. Storage storage = new Storage();
  68. Producer p1 = new Producer(storage);
  69. Producer p2 = new Producer(storage);
  70. Producer p3 = new Producer(storage);
  71. Producer p4 = new Producer(storage);
  72. Customer c1 = new Customer(storage);
  73. Customer c2 = new Customer(storage);
  74. Customer c3 = new Customer(storage);
  75. p1.setNum(10);
  76. p2.setNum(20);
  77. p3.setNum(80);
  78. c1.setNum(50);
  79. c2.setNum(20);
  80. c3.setNum(20);
  81. c1.start();
  82. c2.start();
  83. c3.start();
  84. p1.start();
  85. p2.start();
  86. p3.start();
  87. }
  88. }

  手写阻塞队列
  1. public class blockQueue {
  2. private List<Integer> container = new ArrayList<>();
  3. private volatile int size;
  4. private volatile int capacity;
  5. private Lock lock = new ReentrantLock();
  6. private final Condition isNull = lock.newCondition();
  7. private final Condition isFull = lock.newCondition();
  8. blockQueue(int capacity) {
  9. this.capacity = capacity;
  10. }
  11. public void add(int data) {
  12. try {
  13. lock.lock();
  14. try {
  15. while (size >= capacity) {
  16. System.out.println("阻塞队列满了");
  17. isFull.await();
  18. }
  19. } catch (Exception e) {
  20. isFull.signal();
  21. e.printStackTrace();
  22. }
  23. ++size;
  24. container.add(data);
  25. isNull.signal();
  26. } finally {
  27. lock.unlock();
  28. }
  29. }
  30. public int take() {
  31. try {
  32. lock.lock();
  33. try {
  34. while (size == 0) {
  35. System.out.println("阻塞队列空了");
  36. isNull.await();
  37. }
  38. } catch (Exception e) {
  39. isNull.signal();
  40. e.printStackTrace();
  41. }
  42. --size;
  43. int res = container.get(0);
  44. container.remove(0);
  45. isFull.signal();
  46. return res;
  47. } finally {
  48. lock.unlock();
  49. }
  50. }
  51. }
  52. public static void main(String[] args) {
  53. AxinBlockQueue queue = new AxinBlockQueue(5);
  54. Thread t1 = new Thread(() -> {
  55. for (int i = 0; i < 100; i++) {
  56. queue.add(i);
  57. System.out.println("塞入" + i);
  58. try {
  59. Thread.sleep(500);
  60. } catch (InterruptedException e) {
  61. e.printStackTrace();
  62. }
  63. }
  64. });
  65. Thread t2 = new Thread(() -> {
  66. for (; ; ) {
  67. System.out.println("消费"+queue.take());
  68. try {
  69. Thread.sleep(800);
  70. } catch (InterruptedException e) {
  71. e.printStackTrace();
  72. }
  73. }
  74. });
  75. t1.start();
  76. t2.start();
  77. }

  手写多线程交替打印ABC
  1. package com.demo.test;
  2. import java.util.concurrent.locks.Condition;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. public class syncPrinter implements Runnable{
  5. // 打印次数
  6. private static final int PRINT_COUNT = 10;
  7. private final ReentrantLock reentrantLock;
  8. private final Condition thisCondtion;
  9. private final Condition nextCondtion;
  10. private final char printChar;
  11. public syncPrinter(ReentrantLock reentrantLock, Condition thisCondtion, Condition nextCondition, char printChar) {
  12. this.reentrantLock = reentrantLock;
  13. this.nextCondtion = nextCondition;
  14. this.thisCondtion = thisCondtion;
  15. this.printChar = printChar;
  16. }
  17. @Override
  18. public void run() {
  19. // 获取打印锁 进入临界区
  20. reentrantLock.lock();
  21. try {
  22. // 连续打印PRINT_COUNT次
  23. for (int i = 0; i < PRINT_COUNT; i++) {
  24. //打印字符
  25. System.out.print(printChar);
  26. // 使用nextCondition唤醒下一个线程
  27. // 因为只有一个线程在等待,所以signal或者signalAll都可以
  28. nextCondtion.signal();
  29. // 不是最后一次则通过thisCondtion等待被唤醒
  30. // 必须要加判断,不然虽然能够打印10次,但10次后就会直接死锁
  31. if (i < PRINT_COUNT - 1) {
  32. try {
  33. // 本线程让出锁并等待唤醒
  34. thisCondtion.await();
  35. } catch (InterruptedException e) {
  36. e.printStackTrace();
  37. }
  38. }
  39. }
  40. } finally {
  41. reentrantLock.unlock();
  42. }
  43. }
  44. public static void main(String[] args) throws InterruptedException {
  45. ReentrantLock lock = new ReentrantLock();
  46. Condition conditionA = lock.newCondition();
  47. Condition conditionB = lock.newCondition();
  48. Condition conditionC = lock.newCondition();
  49. Thread printA = new Thread(new syncPrinter(lock, conditionA, conditionB,'A'));
  50. Thread printB = new Thread(new syncPrinter(lock, conditionB, conditionC,'B'));
  51. Thread printC = new Thread(new syncPrinter(lock, conditionC, conditionA,'C'));
  52. printA.start();
  53. Thread.sleep(100);
  54. printB.start();
  55. Thread.sleep(100);
  56. printC.start();
  57. }
  58. }

  交替打印FooBar
  1. //手太阴肺经 BLOCKING Queue
  2. public class FooBar {
  3. private int n;
  4. private BlockingQueue<Integer> bar = new LinkedBlockingQueue<>(1);
  5. private BlockingQueue<Integer> foo = new LinkedBlockingQueue<>(1);
  6. public FooBar(int n) {
  7. this.n = n;
  8. }
  9. public void foo(Runnable printFoo) throws InterruptedException {
  10. for (int i = 0; i < n; i++) {
  11. foo.put(i);
  12. printFoo.run();
  13. bar.put(i);
  14. }
  15. }
  16. public void bar(Runnable printBar) throws InterruptedException {
  17. for (int i = 0; i < n; i++) {
  18. bar.take();
  19. printBar.run();
  20. foo.take();
  21. }
  22. }
  23. }
  24. //手阳明大肠经CyclicBarrier 控制先后
  25. class FooBar6 {
  26. private int n;
  27. public FooBar6(int n) {
  28. this.n = n;
  29. }
  30. CyclicBarrier cb = new CyclicBarrier(2);
  31. volatile boolean fin = true;
  32. public void foo(Runnable printFoo) throws InterruptedException {
  33. for (int i = 0; i < n; i++) {
  34. while(!fin);
  35. printFoo.run();
  36. fin = false;
  37. try {
  38. cb.await();
  39. } catch (BrokenBarrierException e) {}
  40. }
  41. }
  42. public void bar(Runnable printBar) throws InterruptedException {
  43. for (int i = 0; i < n; i++) {
  44. try {
  45. cb.await();
  46. } catch (BrokenBarrierException e) {}
  47. printBar.run();
  48. fin = true;
  49. }
  50. }
  51. }
  52. //手少阴心经 自旋 + 让出CPU
  53. class FooBar5 {
  54. private int n;
  55. public FooBar5(int n) {
  56. this.n = n;
  57. }
  58. volatile boolean permitFoo = true;
  59. public void foo(Runnable printFoo) throws InterruptedException {
  60. for (int i = 0; i < n; ) {
  61. if(permitFoo) {
  62. printFoo.run();
  63. i++;
  64. permitFoo = false;
  65. }else{
  66. Thread.yield();
  67. }
  68. }
  69. }
  70. public void bar(Runnable printBar) throws InterruptedException {
  71. for (int i = 0; i < n; ) {
  72. if(!permitFoo) {
  73. printBar.run();
  74. i++;
  75. permitFoo = true;
  76. }else{
  77. Thread.yield();
  78. }
  79. }
  80. }
  81. }
  82. //手少阳三焦经 可重入锁 + Condition
  83. class FooBar4 {
  84. private int n;
  85. public FooBar4(int n) {
  86. this.n = n;
  87. }
  88. Lock lock = new ReentrantLock(true);
  89. private final Condition foo = lock.newCondition();
  90. volatile boolean flag = true;
  91. public void foo(Runnable printFoo) throws InterruptedException {
  92. for (int i = 0; i < n; i++) {
  93. lock.lock();
  94. try {
  95. while(!flag) {
  96. foo.await();
  97. }
  98. printFoo.run();
  99. flag = false;
  100. foo.signal();
  101. }finally {
  102. lock.unlock();
  103. }
  104. }
  105. }
  106. public void bar(Runnable printBar) throws InterruptedException {
  107. for (int i = 0; i < n;i++) {
  108. lock.lock();
  109. try {
  110. while(flag) {
  111. foo.await();
  112. }
  113. printBar.run();
  114. flag = true;
  115. foo.signal();
  116. }finally {
  117. lock.unlock();
  118. }
  119. }
  120. }
  121. }
  122. //手厥阴心包经 synchronized + 标志位 + 唤醒
  123. class FooBar3 {
  124. private int n;
  125. // 标志位,控制执行顺序,true执行printFoo,false执行printBar
  126. private volatile boolean type = true;
  127. private final Object foo= new Object(); // 锁标志
  128. public FooBar3(int n) {
  129. this.n = n;
  130. }
  131. public void foo(Runnable printFoo) throws InterruptedException {
  132. for (int i = 0; i < n; i++) {
  133. synchronized (foo) {
  134. while(!type){
  135. foo.wait();
  136. }
  137. printFoo.run();
  138. type = false;
  139. foo.notifyAll();
  140. }
  141. }
  142. }
  143. public void bar(Runnable printBar) throws InterruptedException {
  144. for (int i = 0; i < n; i++) {
  145. synchronized (foo) {
  146. while(type){
  147. foo.wait();
  148. }
  149. printBar.run();
  150. type = true;
  151. foo.notifyAll();
  152. }
  153. }
  154. }
  155. }
  156. //手太阳小肠经 信号量 适合控制顺序
  157. class FooBar2 {
  158. private int n;
  159. private Semaphore foo = new Semaphore(1);
  160. private Semaphore bar = new Semaphore(0);
  161. public FooBar2(int n) {
  162. this.n = n;
  163. }
  164. public void foo(Runnable printFoo) throws InterruptedException {
  165. for (int i = 0; i < n; i++) {
  166. foo.acquire();
  167. printFoo.run();
  168. bar.release();
  169. }
  170. }
  171. public void bar(Runnable printBar) throws InterruptedException {
  172. for (int i = 0; i < n; i++) {
  173. bar.acquire();
  174. printBar.run();
  175. foo.release();
  176. }
  177. }
  178. }

¤ 拓展阅读 ¤

3DXR技术 | 终端技术 | 音视频技术

服务端技术 | 技术质量 | 数据算法

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/629496
推荐阅读
相关标签
  

闽ICP备14008679号