基本概念

  • 程序(program)=数据结构 + 算法,一段静态的代码
  • 进程(process):程序的一次执行过程。

程序是静态的,进程是动态的
进程是资源分配的基本单位,系统在运行时会为每个进程分配不同的内存区域。

  • 线程(Thread):程序内部的一条执行路径

若一个进程同一时间并行执行多个线程,就是支持多线程的
线程是调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
若一个进程中的多个线程共享相同的内存单元/内存地址空间->虽然使得线程之间的通信更简便,高效。但多个线程操作共享的系统资源可能会带来安全隐患。

Java中,方法区和堆一个进程一份,虚拟栈和程序计数器一个线程一份。

单核CPU:一种假的多线程,并发
多核CPU:并行

多线程的优势

  1. 提高应用程序的响应。对图形化界面更有意义,可以增强用户体验。
  2. 提高CPU的利用率
  3. 改善程序结构。将既长又复杂的进程分成多个线程,独立运行,利于理解和修改

什么时候用多线程

  • 程序需要同时执行两个或多个线程
  • 程序需要实现一些需要等待的任务时,例如,用户输入,文件读写操作,网络操作,搜索等。
  • 需要一些后台运行的程序

线程的创建和使用

方式1:继承Thread类

  1. package vip.zdkk.java;
  2. /**
  3. * @author: zdkk
  4. * @create 2022-05-11 17:18
  5. *
  6. * 多线程的创建方式1:
  7. * 1. 继承Thread类
  8. * 2. 重写Thread类run方法 -> 写需要执行的操作
  9. * 3. 创建Thread类子类的对象
  10. * 4. 通过此对象调用start()方法 -> ①启动当前线程 ②调用当前线程的run()
  11. * 不能用该对象再调用start()方法,会抛出 IllegalThreadStateException 异常,需要重新创建一个对象
  12. *
  13. * 例:遍历100以内所有的偶数
  14. */
  15. class MyThread extends Thread {
  16. @Override
  17. public void run() {
  18. for (int i = 0; i < 100; i++) {
  19. if ((i % 2) == 0) {
  20. System.out.println(Thread.currentThread().getName() + " " + i);
  21. }
  22. }
  23. }
  24. }
  25. public class ThreadTest {
  26. public static void main(String[] args) {
  27. MyThread t1 = new MyThread();
  28. t1.start();
  29. for (int i = 0; i < 100; i++) {
  30. if ((i % 2) == 1) {
  31. System.out.println(Thread.currentThread().getName() + " " + i);
  32. }
  33. }
  34. new MyThread().start();
  35. }
  36. }
  1. // 输出
  2. main 1
  3. Thread-0 0
  4. Thread-0 2
  5. Thread-0 4
  6. Thread-0 6
  7. main 3
  8. Thread-0 8
  9. main 5
  10. main 7
  11. main 9
  12. Thread-0 10
  13. Thread-0 12
  14. Thread-0 14
  15. Thread-0 16
  16. Thread-0 18
  17. Thread-0 20
  18. Thread-0 22
  19. Thread-0 24
  20. Thread-0 26
  21. Thread-0 28
  22. main 11
  23. main 13
  24. main 15
  25. main 17
  26. main 19
  27. main 21
  28. main 23
  29. main 25
  30. main 27
  31. main 29
  32. main 31
  33. main 33
  34. main 35
  35. main 37
  36. main 39
  37. Thread-0 30
  38. main 41
  39. main 43
  40. main 45
  41. main 47
  42. main 49
  43. main 51
  44. main 53
  45. main 55
  46. main 57
  47. Thread-0 32
  48. main 59
  49. main 61
  50. main 63
  51. main 65
  52. Thread-0 34
  53. Thread-0 36
  54. Thread-0 38
  55. Thread-0 40
  56. Thread-0 42
  57. Thread-0 44
  58. Thread-0 46
  59. Thread-0 48
  60. Thread-0 50
  61. Thread-0 52
  62. Thread-0 54
  63. Thread-0 56
  64. main 67
  65. Thread-0 58
  66. Thread-0 60
  67. Thread-0 62
  68. Thread-0 64
  69. Thread-0 66
  70. Thread-0 68
  71. main 69
  72. Thread-0 70
  73. Thread-0 72
  74. Thread-0 74
  75. Thread-0 76
  76. main 71
  77. Thread-0 78
  78. Thread-0 80
  79. Thread-0 82
  80. Thread-0 84
  81. main 73
  82. Thread-0 86
  83. Thread-0 88
  84. Thread-0 90
  85. Thread-0 92
  86. main 75
  87. Thread-0 94
  88. main 77
  89. main 79
  90. main 81
  91. main 83
  92. main 85
  93. Thread-0 96
  94. main 87
  95. main 89
  96. main 91
  97. Thread-0 98
  98. main 93
  99. main 95
  100. main 97
  101. main 99
  102. Thread-1 0
  103. Thread-1 2
  104. Thread-1 4
  105. Thread-1 6
  106. Thread-1 8
  107. Thread-1 10
  108. Thread-1 12
  109. Thread-1 14
  110. Thread-1 16
  111. Thread-1 18
  112. Thread-1 20
  113. Thread-1 22
  114. Thread-1 24
  115. Thread-1 26
  116. Thread-1 28
  117. Thread-1 30
  118. Thread-1 32
  119. Thread-1 34
  120. Thread-1 36
  121. Thread-1 38
  122. Thread-1 40
  123. Thread-1 42
  124. Thread-1 44
  125. Thread-1 46
  126. Thread-1 48
  127. Thread-1 50
  128. Thread-1 52
  129. Thread-1 54
  130. Thread-1 56
  131. Thread-1 58
  132. Thread-1 60
  133. Thread-1 62
  134. Thread-1 64
  135. Thread-1 66
  136. Thread-1 68
  137. Thread-1 70
  138. Thread-1 72
  139. Thread-1 74
  140. Thread-1 76
  141. Thread-1 78
  142. Thread-1 80
  143. Thread-1 82
  144. Thread-1 84
  145. Thread-1 86
  146. Thread-1 88
  147. Thread-1 90
  148. Thread-1 92
  149. Thread-1 94
  150. Thread-1 96
  151. Thread-1 98
  152. Process finished with exit code 0

问题一:我们启动一个线程,必须调用start(),不能调用run()的方式启动线程。
问题二:如果再启动一个线程,必须重新创建一个Thread子类的对象,调用此对象的start().

方式2:实现Runnable接口

  1. package vip.zdkk.java;
  2. /**
  3. * @author: zdkk
  4. * @create 2022-05-11 22:55
  5. *
  6. * 创建多线程的方法2:实现Runnable接口
  7. * 1. 创建一个实现Runnable接口的类
  8. * 2. 实现类实现Runnable接口中的抽象方法run()
  9. * 3. 创建实现类的对象
  10. * 4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
  11. * 5. 通过Thread类的对象调用start()
  12. * 调用该Thread对象的run方法,而此run方法内调用了传给Thread类实例的Runnable类型的对象的run方法
  13. */
  14. public class ThreadTest2 {
  15. public static void main(String[] args) {
  16. // 3. 创建实现类的对象
  17. MyThread2 m1 = new MyThread2();
  18. // 4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
  19. // 5. 通过Thread类的对象调用start(),实际调用的是该Thread类对象的run方法,然后在方法内部调用m1的run方法
  20. new Thread(m1).start();
  21. // 再启动一个线程
  22. new Thread(m1).start();
  23. }
  24. }
  25. // 1. 创建一个实现Runnable接口的类
  26. class MyThread2 implements Runnable {
  27. // 2. 实现类实现Runnable接口中的抽象方法run()
  28. @Override
  29. public void run() {
  30. for (int i = 0; i < 100; i++) {
  31. if ((i % 2) == 0) {
  32. System.out.println(Thread.currentThread().getName() + " " + i);
  33. }
  34. }
  35. }
  36. }

第二种方式的优势:

  • 类的继承更贴合实际,而不是通通继承Thread
  • 共享数据不用写成static类型

开发中:优先选择:实现Runnable接口的方式

线程的调度

基于时间片的抢占式调度策略

线程的优先级等级:

  1. MAX_PRIORITY = 10;
  2. MIN_PRIORITY = 1;
  3. NORM_PRIORITY = 5

默认是NORM_PRIORITY
高优先级的线程要抢占低优先级线程CPU的执行权。但只是从概率上讲,高优先级的线程高概率被执行。