Java 使用 Thread 类代表线程,所有的线程对象都必须是 Thread 类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流(一段顺序执行的代码)。 Java 使用线程执行体来代表这段程序流。

继承 Thread 类创建线程类

通过继承 Thread 类来创建并启动多线程的步骤如下。

  1. 定义 Thread 类的子类,并重写该类的 run() 方法,该 run() 方法的方法体就代表了线程需要完成的任务。因此把 run() 方法称为线程执行体。
  2. 创建 Thread 子类的实例,即创建了线程对象。
  3. 调用线程对象的 start() 方法来启动该线程。

下面程序示范了通过继承 Thread 类来创建并启动多线程。

  1. //通过继承Thread类来创建线程类
  2. public class FirstThread extends Thread {
  3. private int i = 0;
  4.  
  5. // 重写run()方法的方法体就是线程执行体
  6. public void run() {
  7. for (; i < 100; i++) {
  8. // 当线程类继承Thread时,直接使用this即可获取当前线程
  9. // Thread对象的getName()方法返回当前线程的名字
  10. // 因此可以直接调用getName()方法返回当前线程的名字
  11. System.out.println(getName() + " " + i);
  12. }
  13. }
  14.  
  15. public static void main(String[] args) {
  16. for (int i = 0; i < 100; i++) {
  17. // 调用Thread的currentThread()方法获取当前线程
  18. System.out.println(Thread.currentThread().getName() + " " + i);
  19. if (i == 20) {
  20. // 创建并启动第一个线程
  21. new FirstThread().start();
  22. // 创建并启动第二个线程
  23. new FirstThread().start();
  24. }
  25. }
  26. }
  27. }
  1. main 0
  2. main 1
  3. main 2
  4. main 3
  5. main 4
  6. main 5
  7. main 6
  8. main 7
  9. main 8
  10. main 9
  11. main 10
  12. main 11
  13. main 12
  14. main 13
  15. main 14
  16. main 15
  17. main 16
  18. main 17
  19. main 18
  20. main 19
  21. main 20
  22. main 21
  23. main 22
  24. main 23
  25. main 24
  26. main 25
  27. main 26
  28. main 27
  29. main 28
  30. main 29
  31. main 30
  32. main 31
  33. Thread-1 0
  34. Thread-1 1
  35. main 32
  36. Thread-1 2
  37. Thread-1 3
  38. main 33
  39. Thread-1 4
  40. Thread-1 5
  41. Thread-1 6
  42. Thread-1 7
  43. main 34
  44. main 35
  45. main 36
  46. main 37
  47. main 38
  48. Thread-0 0
  49. Thread-0 1
  50. Thread-0 2
  51. Thread-0 3
  52. Thread-0 4
  53. Thread-1 8
  54. Thread-0 5
  55. main 39
  56. Thread-0 6
  57. Thread-0 7
  58. Thread-0 8
  59. Thread-0 9
  60. Thread-1 9
  61. Thread-0 10
  62. Thread-0 11
  63. Thread-0 12
  64. Thread-0 13
  65. main 40
  66. Thread-0 14
  67. Thread-0 15
  68. Thread-0 16
  69. Thread-0 17
  70. Thread-0 18
  71. Thread-0 19
  72. Thread-0 20
  73. Thread-1 10
  74. Thread-1 11
  75. Thread-1 12
  76. Thread-1 13
  77. Thread-1 14
  78. Thread-1 15
  79. Thread-1 16
  80. Thread-1 17
  81. Thread-1 18
  82. Thread-1 19
  83. Thread-1 20
  84. Thread-1 21
  85. Thread-1 22
  86. Thread-1 23
  87. Thread-1 24
  88. Thread-1 25
  89. Thread-1 26
  90. Thread-1 27
  91. Thread-1 28
  92. Thread-1 29
  93. Thread-1 30
  94. Thread-1 31
  95. Thread-1 32
  96. Thread-1 33
  97. Thread-1 34
  98. Thread-1 35
  99. Thread-1 36
  100. Thread-1 37
  101. Thread-1 38
  102. Thread-1 39
  103. Thread-1 40
  104. Thread-1 41
  105. Thread-1 42
  106. Thread-1 43
  107. Thread-1 44
  108. Thread-1 45
  109. Thread-1 46
  110. Thread-1 47
  111. Thread-1 48
  112. Thread-1 49
  113. Thread-1 50
  114. Thread-1 51
  115. Thread-1 52
  116. Thread-1 53
  117. Thread-1 54
  118. Thread-1 55
  119. Thread-1 56
  120. Thread-1 57
  121. Thread-1 58
  122. Thread-1 59
  123. Thread-1 60
  124. Thread-1 61
  125. Thread-0 21
  126. main 41
  127. Thread-0 22
  128. Thread-0 23
  129. Thread-1 62
  130. Thread-0 24
  131. Thread-0 25
  132. Thread-0 26
  133. Thread-0 27
  134. Thread-0 28
  135. Thread-0 29
  136. Thread-0 30
  137. Thread-0 31
  138. Thread-0 32
  139. Thread-0 33
  140. Thread-0 34
  141. Thread-0 35
  142. Thread-0 36
  143. Thread-0 37
  144. Thread-0 38
  145. Thread-0 39
  146. Thread-0 40
  147. main 42
  148. Thread-0 41
  149. Thread-0 42
  150. Thread-0 43
  151. Thread-0 44
  152. Thread-0 45
  153. Thread-0 46
  154. Thread-0 47
  155. Thread-0 48
  156. Thread-0 49
  157. Thread-0 50
  158. Thread-1 63
  159. Thread-1 64
  160. Thread-1 65
  161. Thread-1 66
  162. Thread-1 67
  163. Thread-1 68
  164. Thread-1 69
  165. Thread-1 70
  166. Thread-1 71
  167. Thread-1 72
  168. Thread-1 73
  169. Thread-1 74
  170. Thread-1 75
  171. Thread-1 76
  172. Thread-1 77
  173. Thread-1 78
  174. Thread-1 79
  175. Thread-1 80
  176. Thread-1 81
  177. Thread-1 82
  178. Thread-1 83
  179. Thread-1 84
  180. Thread-1 85
  181. Thread-1 86
  182. Thread-1 87
  183. Thread-1 88
  184. Thread-1 89
  185. Thread-1 90
  186. Thread-1 91
  187. Thread-1 92
  188. Thread-1 93
  189. Thread-1 94
  190. Thread-1 95
  191. Thread-1 96
  192. Thread-1 97
  193. Thread-1 98
  194. Thread-1 99
  195. Thread-0 51
  196. Thread-0 52
  197. Thread-0 53
  198. Thread-0 54
  199. Thread-0 55
  200. Thread-0 56
  201. Thread-0 57
  202. Thread-0 58
  203. Thread-0 59
  204. Thread-0 60
  205. Thread-0 61
  206. Thread-0 62
  207. Thread-0 63
  208. Thread-0 64
  209. Thread-0 65
  210. Thread-0 66
  211. Thread-0 67
  212. Thread-0 68
  213. Thread-0 69
  214. Thread-0 70
  215. Thread-0 71
  216. Thread-0 72
  217. Thread-0 73
  218. Thread-0 74
  219. Thread-0 75
  220. Thread-0 76
  221. Thread-0 77
  222. Thread-0 78
  223. Thread-0 79
  224. Thread-0 80
  225. Thread-0 81
  226. Thread-0 82
  227. Thread-0 83
  228. Thread-0 84
  229. Thread-0 85
  230. Thread-0 86
  231. Thread-0 87
  232. Thread-0 88
  233. Thread-0 89
  234. Thread-0 90
  235. Thread-0 91
  236. Thread-0 92
  237. Thread-0 93
  238. Thread-0 94
  239. Thread-0 95
  240. Thread-0 96
  241. Thread-0 97
  242. Thread-0 98
  243. Thread-0 99
  244. main 43
  245. main 44
  246. main 45
  247. main 46
  248. main 47
  249. main 48
  250. main 49
  251. main 50
  252. main 51
  253. main 52
  254. main 53
  255. main 54
  256. main 55
  257. main 56
  258. main 57
  259. main 58
  260. main 59
  261. main 60
  262. main 61
  263. main 62
  264. main 63
  265. main 64
  266. main 65
  267. main 66
  268. main 67
  269. main 68
  270. main 69
  271. main 70
  272. main 71
  273. main 72
  274. main 73
  275. main 74
  276. main 75
  277. main 76
  278. main 77
  279. main 78
  280. main 79
  281. main 80
  282. main 81
  283. main 82
  284. main 83
  285. main 84
  286. main 85
  287. main 86
  288. main 87
  289. main 88
  290. main 89
  291. main 90
  292. main 91
  293. main 92
  294. main 93
  295. main 94
  296. main 95
  297. main 96
  298. main 97
  299. main 98
  300. main 99

运行结果

上面程序中的 FirstThread 类继承了 Thread 类,并实现了 run() 方法,该 run() 方法里的代码执行流就是该线程所需要完成的任务。程序的主方法中也包含了一个循环,当循环变量 i 等于20时创建并启动两个新线程。

虽然上面程序只显式地创建并启动了2个线程,但实际上程序有3个线程,即程序显式创建的2个子线程和主线程。前面已经提到,当 Java 程序开始运行后,程序至少会创建一个主线程,主线程的线程执行体不是由 run() 方法确定的,而是由 main() 方法确定的——main() 方法的方法体代表主线程的线程执行体。

注意:进行多线程编程时不要忘记了 Java 程序运行时默认的主线程,main() 方法的方法体就是主线程的线程执行体。

除此之外,上面程序还用到了线程的如下两个方法。

  • Thread.currentThread(): currentThread() 是 Thread 类的静态方法,该方法总是返回当前正在执行的线程对象。
  • getName():该方法是 Thread 类的实例方法,该方法返回调用该方法的线程名字。

提示:程序可以通过 setName(String name) 方法为线程设置名字,也可以通过 getName() 方法返回指定线程的名字。在默认情况下,主线程的名字为 main ,用户启动的多个线程的名字依次为 Thread-0、 Thread-1、 Thread-2、…、 Thread-n 等。

从运行结果可以看出 Thread-0 和 Thread-1 两个线程输出的 i 变量不连续——注意:i 变量是 FirstThread 的实例变量,而不是局部变量,但因为程序每次创建线程对象时都需要创建一个 FirstThread 对象,所以  Thread-0 和 Thread-1 不能共享该实例变量。

注意:使用继承 Thread 类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量。

实现 Runnable 接口来创建线程类

步骤如下:

  1. 定义 Runnable 接口的实现类,并重写该接口的 run() 方法,该 run() 方法的方法体同样是该线程的线程执行体。
  2. 创建 Runnable 实现类的实例,并以此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是真正的线程对象。

代码如下所示:

  1. //创建 Runnable 实现类的对象
  2. SecondThread st = new SecondThread();
  3. //以 Runnable 实现类的对象作为 Thread 的 target 来创建 Thread 对象,即线程对象
  4. new Thread(st);

也可以在创建 Thread 对象时为该 Thread 对象指定一个名字,代码如下所示:

  1. //创建 Thread 对象时指定 target 和新线程的名字
  2. new Thread (st , "新线程1");

提示:Runnable 对象仅仅作为 Thread 对象的 target,Runnable 实现类里包含的 run() 方法仅作为线程执行体。而实际的线程对象依然是 Thread 实例,只是该 Thread 线程负责执行其 target 的 run() 方法。

  1. public class SecondThread implements Runnable {
  2.  
  3. private int i;
  4.  
  5. // run()方法同样是线程执行体
  6. public void run() {
  7. for (; i < 100; i++) {
  8. // 当线程类实现Runnable接口时
  9. // 如果想获取当前线程,只能用Thread.currentThread()方法
  10. System.out.println(Thread.currentThread().getName() + " " + i);
  11. }
  12. }
  13. public static void main(String[] args) {
  14. for (int i = 0; i < 100; i++) {
  15. System.out.println(Thread.currentThread().getName() + " " + i);
  16. if (i == 20) {
  17. SecondThread st = new SecondThread(); // ①
  18. // 通过new Thread(target, name)方法创建新线程
  19. new Thread(st, "线程1").start();
  20. new Thread(st, "线程2").start();
  21. }
  22. }
  23. }
  24. }

上面程序中的粗体字代码部分实现了 run() 方法,也就是定义了该线程的线程执行体。对比 FirstThread 中的 run() 方法体和 SecondThread 中的 run() 方法体不难发现,通过继承 Thread 类来获得当前线程对象比较简单,直接使用 this 就可以了;但通过实现 Runnable 接口来获得当前线程对象,则必须使用 Thread.currentThread() 方法。

提示:Runnable 接口中只包含一个抽象方法 ,从 Java 8 开 始, Runnable 接口使用了@FunctionalInterface 修饰。也就是说,Runnable 接口是函数式接口,可使用 Lambda 表达式创建 Runnable 对象。接下来介绍的 Callable 接口也是函数式接口。

除此之外,上面程序中的粗体字代码创建了两个 Thread 对象,并调用 start() 方法来启动这两个线程。在 FirstThread 和 SecondThread 中创建线程对象的方式有所区别:前者直接创建的 Thread 子类即可代表线程对象;后者创建的 Runnable 对象只能作为线程对象的 target 。

运行上面程序,会看到如下结果:

  1. main 0
  2. main 1
  3. main 2
  4. main 3
  5. main 4
  6. main 5
  7. main 6
  8. main 7
  9. main 8
  10. main 9
  11. main 10
  12. main 11
  13. main 12
  14. main 13
  15. main 14
  16. main 15
  17. main 16
  18. main 17
  19. main 18
  20. main 19
  21. main 20
  22. main 21
  23. main 22
  24. main 23
  25. main 24
  26. main 25
  27. main 26
  28. 线程2 0
  29. 线程2 1
  30. main 27
  31. main 28
  32. 线程2 2
  33. main 29
  34. main 30
  35. main 31
  36. 线程2 3
  37. 线程1 3
  38. main 32
  39. 线程1 5
  40. 线程2 4
  41. 线程1 6
  42. main 33
  43. 线程1 8
  44. 线程2 7
  45. 线程1 9
  46. main 34
  47. 线程1 11
  48. 线程2 10
  49. 线程1 12
  50. main 35
  51. 线程1 14
  52. 线程2 13
  53. 线程1 15
  54. 线程1 17
  55. 线程1 18
  56. 线程1 19
  57. 线程1 20
  58. 线程1 21
  59. 线程1 22
  60. 线程1 23
  61. 线程1 24
  62. 线程1 25
  63. 线程1 26
  64. 线程1 27
  65. main 36
  66. 线程1 28
  67. 线程2 16
  68. 线程1 29
  69. main 37
  70. 线程1 31
  71. 线程2 30
  72. 线程1 32
  73. main 38
  74. 线程1 34
  75. 线程2 33
  76. 线程1 35
  77. main 39
  78. 线程1 37
  79. 线程2 36
  80. 线程1 38
  81. 线程1 40
  82. 线程1 41
  83. 线程1 42
  84. 线程1 43
  85. 线程1 44
  86. 线程1 45
  87. main 40
  88. main 41
  89. main 42
  90. main 43
  91. main 44
  92. main 45
  93. main 46
  94. main 47
  95. main 48
  96. main 49
  97. main 50
  98. main 51
  99. main 52
  100. main 53
  101. main 54
  102. main 55
  103. main 56
  104. 线程1 46
  105. 线程1 47
  106. 线程1 48
  107. 线程1 49
  108. 线程1 50
  109. 线程1 51
  110. 线程1 52
  111. 线程1 53
  112. 线程1 54
  113. 线程1 55
  114. 线程1 56
  115. 线程1 57
  116. 线程1 58
  117. 线程1 59
  118. 线程1 60
  119. 线程1 61
  120. 线程1 62
  121. 线程1 63
  122. 线程1 64
  123. 线程1 65
  124. 线程1 66
  125. 线程1 67
  126. 线程1 68
  127. 线程1 69
  128. 线程1 70
  129. 线程1 71
  130. 线程1 72
  131. 线程1 73
  132. 线程1 74
  133. 线程1 75
  134. 线程1 76
  135. 线程1 77
  136. 线程1 78
  137. 线程1 79
  138. 线程1 80
  139. 线程1 81
  140. 线程1 82
  141. 线程1 83
  142. 线程1 84
  143. 线程1 85
  144. 线程1 86
  145. 线程1 87
  146. 线程1 88
  147. 线程1 89
  148. 线程1 90
  149. 线程1 91
  150. 线程1 92
  151. 线程1 93
  152. 线程1 94
  153. 线程1 95
  154. 线程1 96
  155. 线程1 97
  156. 线程1 98
  157. 线程1 99
  158. 线程2 39
  159. main 57
  160. main 58
  161. main 59
  162. main 60
  163. main 61
  164. main 62
  165. main 63
  166. main 64
  167. main 65
  168. main 66
  169. main 67
  170. main 68
  171. main 69
  172. main 70
  173. main 71
  174. main 72
  175. main 73
  176. main 74
  177. main 75
  178. main 76
  179. main 77
  180. main 78
  181. main 79
  182. main 80
  183. main 81
  184. main 82
  185. main 83
  186. main 84
  187. main 85
  188. main 86
  189. main 87
  190. main 88
  191. main 89
  192. main 90
  193. main 91
  194. main 92
  195. main 93
  196. main 94
  197. main 95
  198. main 96
  199. main 97
  200. main 98
  201. main 99

运行结果

两个子线程的 i 变量是连续的,也就是釆用 Runnable 接口的方式创建的多个线程可以共享线程类的实例变量。这是因为在这种方式下,程序所创建的 Runnable 对象只是线程的 target ,而多个线程可以共享同一个 target ,所以多个线程可以共享同一个线程类(实际上应该是线程的 target 类)的实例变量。

使用 Callable 和 Future 创建线程

前面已经指出,通过实现 Runnable 接口创建多线程时, Thread 类的作用就是把 run() 方法包装成线程执行体。那么是否可以直接把任意方法都包装成线程执行体呢? Java 目前不行!但 Java 的模仿者 C# 可以(C# 可以把任意方法包装成线程执行体,包括有返回值的方法)。

也许受此启发,从 Java 5 开始, Java 提供了 Callable 接口,该接口怎么看都像是 Runnable 接口的增强版, Callable 接口提供了一个 call() 方法可以作为线程执行体,但 call() 方法比 run() 方法功能更强大。

  • call() 方法可以有返回值。
  • call() 方法可以声明抛出异常。

因此完全可以提供一个 Callable 对象作为 Thread 的 target ,而该线程的线程执行体就是该 Callable 对象的 call() 方法。问题是: Callable 接口是 Java 5 新增的接口,而且它不是 Runnable 接口的子接口,所以 Callable 对象不能直接作为 Thread 的 target 。而且 call() 方法还有一个返回值——call() 方法并不是直接调用,它是作为线程执行体被调用的。那么如何获取 call() 方法的返回值呢?

Java 5 提供了 Future 接口来代表 Callable 接口里 call() 方法的返回值,并为 Future 接口提供了一个FutureTask 实现类,该实现类实现了 Future 接口,并实现了 Runnable 接口 ——可以作为 Thread 类的 target 。

在 Future 接口里定义了如下几个公共方法来控制它关联的 Callable 任务。

  • boolean cancel(boolean maylnterruptlfRunning):试图取消该 Future 里关联的 Callable 任务。
  • V   get():返回 Callable 任务里 call() 方法的返回值。调用该方法将导致程序阻塞,必须等到子线程结束后才会得到返回值。
  • V   get(long timeout, TimeUnit unit):返回 Callable 任务里 call() 方法的返回值。该方法让程序最多阻塞 timeout 和 unit 指定的时间,如果经过指定时间后 Callable 任务依然没有返回值,将会抛出 TimeoutException 异常。
  • boolean isCancelled():如果在 Callable 任务正常完成前被取消,则返回 true 。
  • boolean isDone ():如 果 Callable 任务已完成,则返回 true 。

注意:Callable 接口有泛型限制, Callable 接口里的泛型形参类型与 call() 方法返回值类型相同。而且 Callable 接口是函数式接口,因此可使用 Lambda 表达式创建 Callable 对象。

  1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,且该 call() 方法有返回值,再创建 Callable 实现类的实例。从 Java 8 开始,可以直接使用 Lambda 表达式创建 Callable对象。
  2. 使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
  3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
  4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

下面程序通过实现 Callable 接口来实现线程类,并启动该线程。

  1. import java.util.concurrent.Callable;
  2. import java.util.concurrent.FutureTask;
  3.  
  4. public class ThirdThread {
  5. public static void main(String[] args) {
  6. // 创建Callable对象
  7. ThirdThread rt = new ThirdThread();
  8. // 先使用Lambda表达式创建Callable<Integer>对象
  9. // 使用FutureTask来包装Callable对象
  10. FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>) () -> {
  11. int i = 0;
  12. for (; i < 100; i++) {
  13. System.out.println(Thread.currentThread().getName() + " 的循环变量 i 的值: " + i);
  14. }
  15. return i;
  16. });
  17. for (int i = 0; i < 100; i++) {
  18. System.out.println(Thread.currentThread().getName() + " 的循环变量 i 的值: " + i);
  19. if (i == 20) {
  20. // 实质还是以Callable对象来创建并启动线程的
  21. new Thread(task, "有返回值的线程").start();
  22. }
  23. }
  24. try {
  25. System.out.println("子线程的返回值:" + task.get());
  26. } catch (Exception e) {
  27. // TODO Auto-generated catch block
  28. e.printStackTrace();
  29. }
  30. }
  31. }

上面程序中使用 Lambda 表达式直接创建了 Callable 对象,这样就无须先创建 Callable 实现类,再创建 Callable 对象了。实现 Callable 接口与实现 Runnable 接口并没有太大的差别,只是 Callable 的 call() 方法允许声明抛出异常,而且允许带返回值。

上面程序中的粗体字代码是以 Callable 对象来启动线程的关键代码。程序先使用 Lambda 表达式创建一个 Callable 对象,然后将该实例包装成一个 FutureTask 对象。主线程中当循环变量 i 等于20时,程序启动以 FutureTask 对象为 target 的线程。程序最后调用 FutureTask 对象的 get() 方法来返回 call() 方法的返回值——该方法将导致主线程被阻塞,直到 call() 方法结束并返回为止。

运行上面程序,将看到主线程和 call() 方法所代表的线程交替执行的情形,程序最后还会输出 call() 方法的返回值。

  1. main 的循环变量 i 的值: 0
  2. main 的循环变量 i 的值: 1
  3. main 的循环变量 i 的值: 2
  4. main 的循环变量 i 的值: 3
  5. main 的循环变量 i 的值: 4
  6. main 的循环变量 i 的值: 5
  7. main 的循环变量 i 的值: 6
  8. main 的循环变量 i 的值: 7
  9. main 的循环变量 i 的值: 8
  10. main 的循环变量 i 的值: 9
  11. main 的循环变量 i 的值: 10
  12. main 的循环变量 i 的值: 11
  13. main 的循环变量 i 的值: 12
  14. main 的循环变量 i 的值: 13
  15. main 的循环变量 i 的值: 14
  16. main 的循环变量 i 的值: 15
  17. main 的循环变量 i 的值: 16
  18. main 的循环变量 i 的值: 17
  19. main 的循环变量 i 的值: 18
  20. main 的循环变量 i 的值: 19
  21. main 的循环变量 i 的值: 20
  22. main 的循环变量 i 的值: 21
  23. main 的循环变量 i 的值: 22
  24. main 的循环变量 i 的值: 23
  25. main 的循环变量 i 的值: 24
  26. main 的循环变量 i 的值: 25
  27. main 的循环变量 i 的值: 26
  28. main 的循环变量 i 的值: 27
  29. main 的循环变量 i 的值: 28
  30. main 的循环变量 i 的值: 29
  31. main 的循环变量 i 的值: 30
  32. main 的循环变量 i 的值: 31
  33. main 的循环变量 i 的值: 32
  34. main 的循环变量 i 的值: 33
  35. main 的循环变量 i 的值: 34
  36. main 的循环变量 i 的值: 35
  37. main 的循环变量 i 的值: 36
  38. main 的循环变量 i 的值: 37
  39. main 的循环变量 i 的值: 38
  40. main 的循环变量 i 的值: 39
  41. main 的循环变量 i 的值: 40
  42. main 的循环变量 i 的值: 41
  43. main 的循环变量 i 的值: 42
  44. main 的循环变量 i 的值: 43
  45. main 的循环变量 i 的值: 44
  46. main 的循环变量 i 的值: 45
  47. main 的循环变量 i 的值: 46
  48. main 的循环变量 i 的值: 47
  49. main 的循环变量 i 的值: 48
  50. main 的循环变量 i 的值: 49
  51. main 的循环变量 i 的值: 50
  52. main 的循环变量 i 的值: 51
  53. main 的循环变量 i 的值: 52
  54. main 的循环变量 i 的值: 53
  55. main 的循环变量 i 的值: 54
  56. main 的循环变量 i 的值: 55
  57. main 的循环变量 i 的值: 56
  58. main 的循环变量 i 的值: 57
  59. main 的循环变量 i 的值: 58
  60. main 的循环变量 i 的值: 59
  61. main 的循环变量 i 的值: 60
  62. main 的循环变量 i 的值: 61
  63. main 的循环变量 i 的值: 62
  64. main 的循环变量 i 的值: 63
  65. main 的循环变量 i 的值: 64
  66. main 的循环变量 i 的值: 65
  67. main 的循环变量 i 的值: 66
  68. main 的循环变量 i 的值: 67
  69. main 的循环变量 i 的值: 68
  70. main 的循环变量 i 的值: 69
  71. main 的循环变量 i 的值: 70
  72. main 的循环变量 i 的值: 71
  73. main 的循环变量 i 的值: 72
  74. main 的循环变量 i 的值: 73
  75. 有返回值的线程 的循环变量 i 的值: 0
  76. main 的循环变量 i 的值: 74
  77. 有返回值的线程 的循环变量 i 的值: 1
  78. main 的循环变量 i 的值: 75
  79. 有返回值的线程 的循环变量 i 的值: 2
  80. 有返回值的线程 的循环变量 i 的值: 3
  81. 有返回值的线程 的循环变量 i 的值: 4
  82. 有返回值的线程 的循环变量 i 的值: 5
  83. 有返回值的线程 的循环变量 i 的值: 6
  84. 有返回值的线程 的循环变量 i 的值: 7
  85. 有返回值的线程 的循环变量 i 的值: 8
  86. 有返回值的线程 的循环变量 i 的值: 9
  87. 有返回值的线程 的循环变量 i 的值: 10
  88. 有返回值的线程 的循环变量 i 的值: 11
  89. 有返回值的线程 的循环变量 i 的值: 12
  90. 有返回值的线程 的循环变量 i 的值: 13
  91. 有返回值的线程 的循环变量 i 的值: 14
  92. 有返回值的线程 的循环变量 i 的值: 15
  93. 有返回值的线程 的循环变量 i 的值: 16
  94. 有返回值的线程 的循环变量 i 的值: 17
  95. 有返回值的线程 的循环变量 i 的值: 18
  96. 有返回值的线程 的循环变量 i 的值: 19
  97. 有返回值的线程 的循环变量 i 的值: 20
  98. 有返回值的线程 的循环变量 i 的值: 21
  99. 有返回值的线程 的循环变量 i 的值: 22
  100. 有返回值的线程 的循环变量 i 的值: 23
  101. 有返回值的线程 的循环变量 i 的值: 24
  102. 有返回值的线程 的循环变量 i 的值: 25
  103. 有返回值的线程 的循环变量 i 的值: 26
  104. 有返回值的线程 的循环变量 i 的值: 27
  105. 有返回值的线程 的循环变量 i 的值: 28
  106. 有返回值的线程 的循环变量 i 的值: 29
  107. 有返回值的线程 的循环变量 i 的值: 30
  108. 有返回值的线程 的循环变量 i 的值: 31
  109. 有返回值的线程 的循环变量 i 的值: 32
  110. 有返回值的线程 的循环变量 i 的值: 33
  111. 有返回值的线程 的循环变量 i 的值: 34
  112. 有返回值的线程 的循环变量 i 的值: 35
  113. 有返回值的线程 的循环变量 i 的值: 36
  114. 有返回值的线程 的循环变量 i 的值: 37
  115. 有返回值的线程 的循环变量 i 的值: 38
  116. 有返回值的线程 的循环变量 i 的值: 39
  117. 有返回值的线程 的循环变量 i 的值: 40
  118. 有返回值的线程 的循环变量 i 的值: 41
  119. 有返回值的线程 的循环变量 i 的值: 42
  120. 有返回值的线程 的循环变量 i 的值: 43
  121. 有返回值的线程 的循环变量 i 的值: 44
  122. 有返回值的线程 的循环变量 i 的值: 45
  123. 有返回值的线程 的循环变量 i 的值: 46
  124. 有返回值的线程 的循环变量 i 的值: 47
  125. 有返回值的线程 的循环变量 i 的值: 48
  126. 有返回值的线程 的循环变量 i 的值: 49
  127. 有返回值的线程 的循环变量 i 的值: 50
  128. 有返回值的线程 的循环变量 i 的值: 51
  129. 有返回值的线程 的循环变量 i 的值: 52
  130. 有返回值的线程 的循环变量 i 的值: 53
  131. 有返回值的线程 的循环变量 i 的值: 54
  132. 有返回值的线程 的循环变量 i 的值: 55
  133. 有返回值的线程 的循环变量 i 的值: 56
  134. 有返回值的线程 的循环变量 i 的值: 57
  135. 有返回值的线程 的循环变量 i 的值: 58
  136. 有返回值的线程 的循环变量 i 的值: 59
  137. 有返回值的线程 的循环变量 i 的值: 60
  138. 有返回值的线程 的循环变量 i 的值: 61
  139. 有返回值的线程 的循环变量 i 的值: 62
  140. 有返回值的线程 的循环变量 i 的值: 63
  141. 有返回值的线程 的循环变量 i 的值: 64
  142. 有返回值的线程 的循环变量 i 的值: 65
  143. 有返回值的线程 的循环变量 i 的值: 66
  144. 有返回值的线程 的循环变量 i 的值: 67
  145. 有返回值的线程 的循环变量 i 的值: 68
  146. 有返回值的线程 的循环变量 i 的值: 69
  147. 有返回值的线程 的循环变量 i 的值: 70
  148. 有返回值的线程 的循环变量 i 的值: 71
  149. 有返回值的线程 的循环变量 i 的值: 72
  150. 有返回值的线程 的循环变量 i 的值: 73
  151. 有返回值的线程 的循环变量 i 的值: 74
  152. 有返回值的线程 的循环变量 i 的值: 75
  153. 有返回值的线程 的循环变量 i 的值: 76
  154. 有返回值的线程 的循环变量 i 的值: 77
  155. 有返回值的线程 的循环变量 i 的值: 78
  156. 有返回值的线程 的循环变量 i 的值: 79
  157. 有返回值的线程 的循环变量 i 的值: 80
  158. 有返回值的线程 的循环变量 i 的值: 81
  159. 有返回值的线程 的循环变量 i 的值: 82
  160. 有返回值的线程 的循环变量 i 的值: 83
  161. 有返回值的线程 的循环变量 i 的值: 84
  162. 有返回值的线程 的循环变量 i 的值: 85
  163. 有返回值的线程 的循环变量 i 的值: 86
  164. 有返回值的线程 的循环变量 i 的值: 87
  165. 有返回值的线程 的循环变量 i 的值: 88
  166. 有返回值的线程 的循环变量 i 的值: 89
  167. 有返回值的线程 的循环变量 i 的值: 90
  168. 有返回值的线程 的循环变量 i 的值: 91
  169. 有返回值的线程 的循环变量 i 的值: 92
  170. 有返回值的线程 的循环变量 i 的值: 93
  171. 有返回值的线程 的循环变量 i 的值: 94
  172. 有返回值的线程 的循环变量 i 的值: 95
  173. 有返回值的线程 的循环变量 i 的值: 96
  174. 有返回值的线程 的循环变量 i 的值: 97
  175. 有返回值的线程 的循环变量 i 的值: 98
  176. 有返回值的线程 的循环变量 i 的值: 99
  177. main 的循环变量 i 的值: 76
  178. main 的循环变量 i 的值: 77
  179. main 的循环变量 i 的值: 78
  180. main 的循环变量 i 的值: 79
  181. main 的循环变量 i 的值: 80
  182. main 的循环变量 i 的值: 81
  183. main 的循环变量 i 的值: 82
  184. main 的循环变量 i 的值: 83
  185. main 的循环变量 i 的值: 84
  186. main 的循环变量 i 的值: 85
  187. main 的循环变量 i 的值: 86
  188. main 的循环变量 i 的值: 87
  189. main 的循环变量 i 的值: 88
  190. main 的循环变量 i 的值: 89
  191. main 的循环变量 i 的值: 90
  192. main 的循环变量 i 的值: 91
  193. main 的循环变量 i 的值: 92
  194. main 的循环变量 i 的值: 93
  195. main 的循环变量 i 的值: 94
  196. main 的循环变量 i 的值: 95
  197. main 的循环变量 i 的值: 96
  198. main 的循环变量 i 的值: 97
  199. main 的循环变量 i 的值: 98
  200. main 的循环变量 i 的值: 99
  201. 子线程的返回值:100

运行结果

创建线程的三种方式对比

通过继承 Thread 类或实现 Runnable、Callable 接口都可以实现多线程,不过实现 Runnable 接口与实现 Callable 接口的方式基本相同,只是 Callable 接口里定义的方法有返回值,可以声明抛出异常而已。因此可以将实现 Runnable 接口和实现 Callable 接口归为一种方式。这种方式与继承 Thread 方式之间的主要差别如下。

采用实现 Runnable 、 Callable 接口的方式创建多线程的优缺点:

  • 线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。
  • 在这种方式下,多个线程可以共享同一个 target 对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将 CPU 、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
  • 劣势是,编程稍稍复杂,如果需要访问当前线程,则必须使用 Thread.currentThread() 方法。

采用继承 Thread 类的方式创建多线程的优缺点:

  • 劣势是,因为线程类已经继承了 Thread 类,所以不能再继承其他父类。
  • 优势是,编写简单,如果需要访问当前线程,则无须使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。

鉴于上面分析,因此一般推荐采用实现 Runnable 接口、Callable 接口的方式来创建多线程。

Java 线程的创建和启动的更多相关文章

  1. Java线程:创建与启动

    Java线程:创建与启动 一.定义线程   1.扩展java.lang.Thread类.   此类中有个run()方法,应该注意其用法: public void run() 如果该线程是使用独立的 R ...

  2. Java线程的创建及启动

    1.继承Thread类,重写该类的run()方法. package samTest; import java.util.Scanner; /** * Created by Sam on 2018-01 ...

  3. 漫谈并发编程(二):java线程的创建与基本控制

    java线程的创建 定义任务           在java中使用任务这个名词来表示一个线程控制流的代码段,用Runnable接口来标记一个任务,该接口的run方法为线程运行的代码段. public ...

  4. 03_线程的创建和启动_实现Runnable接口方式

    [线程的创建和启动的步骤(实现Runnable接口方式)] 1.定义Runnable接口的实现类,并重写其中的run方法.run()方法的方法体是线程执行体. class SonThread  imp ...

  5. JAVA - 线程从创建到死亡的几种状态都有哪些?

    JAVA - 线程从创建到死亡的几种状态都有哪些? 新建( new ):新创建了一个线程对象. 可运行( runnable ):线程对象创建后,其他线程(比如 main 线程)调用了该对象 的 sta ...

  6. Netty 学习(七):NioEventLoop 对应线程的创建和启动源码说明

    Netty 学习(七):NioEventLoop 对应线程的创建和启动源码说明 作者: Grey 原文地址: 博客园:Netty 学习(七):NioEventLoop 对应线程的创建和启动源码说明 C ...

  7. JAVA学习笔记16——线程的创建和启动

    Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例.每个线程的作用是完成一定的任务,实际上就是执行一段程序流(一段顺序执行的代码).Java使用线程执行体来代表这段 ...

  8. 04_线程的创建和启动_使用Callable和Future的方式

    [简述] 从java5开始,java提供了Callable接口,这个接口可以是Runnable接口的增强版, Callable接口提供了一个call()方法作为线程执行体,call()方法比run() ...

  9. 02_线程的创建和启动_继承Thread方式

    [简述] java使用Thread类代表线程,所有的线程都必须是Thread或者其子类的实例. 每个线程的任务就是完成一定的任务,实际上就是执行一段程序流. [创建并启动多线程的步骤(集成Thread ...

随机推荐

  1. xpath 解析 及案例

    xpath解析 编码流程: 1.实例化一个etree对象,且将页面源码加载到该对象中 2.使用xpath函数,且在函数中必须作用一个xpath表达式进行标签的定位 3.使用xpath进行属性和文本的提 ...

  2. centos7下安装docker(15.3跨主机网络-macvlan)

    除了ovrlay,docker还开发了另一个支持跨主机容器的driver:macvlan macvlan本身是linu kernel模块,其功能是允许在同一物理网卡上配置多了MAC地址,即:多个int ...

  3. centos7 mongodb安装

    参考文档 http://www.runoob.com/mongodb/mongodb-connections.html https://www.cnblogs.com/layezi/p/7290082 ...

  4. 深入学习Redis:Redis内存模型

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 一.Redis内存统计 工欲善其事必先利其器,在说明Redis内存之前首先说明如何统计 ...

  5. 爬取伯乐在线文章(二)通过xpath提取源文件中需要的内容

    爬取说明 以单个页面为例,如:http://blog.jobbole.com/110287/ 我们可以提取标题.日期.多少个评论.正文内容等 Xpath介绍 1. xpath简介 (1) xpath使 ...

  6. 测试 Flask 应用

    测试 Flask 应用 没有经过测试的东西都是不完整的 这一箴言的起源已经不可考了,尽管他不是完全正确的,但是仍然离真理不远.没有测试过的应用将会使得提高现有代码质量很困难,二不测试应用程序的开发者, ...

  7. 论文笔记(一)---翻译 Rich feature hierarchies for accurate object detection and semantic segmentation

    论文网址: https://arxiv.org/abs/1311.2524 RCNN利用深度学习进行目标检测. 摘要 可以将ImageNet上的进全图像分类而训练好的大型卷积神经网络用到PASCAL的 ...

  8. kafka+storm结合存在的一些问题与解决方法

    在配置kafka和storm的时候, 经常的会出现一些问题, 主要在以下几个: 1.  打jar包上去storm集群的时候会出现jar包冲突,类似于log4j或者sf4j的报错信息. 2. kafka ...

  9. UIToolBar - 官方文档

    继承关系:UIToolBar -> UIView -> UIResponder -> NSObject. toolBar是一个工具栏,用于显示一个或多个按钮.其按钮叫做toolBar ...

  10. 【转】强化学习(一)Deep Q-Network

    原文地址:https://www.hhyz.me/2018/08/05/2018-08-05-RL/ 1. 前言 虽然将深度学习和增强学习结合的想法在几年前就有人尝试,但真正成功的开端就是DeepMi ...