在總結JDBC數據庫連接池的時候,發現Java多線程這塊掌握得不是很好,因此回頭看了下多線程的內容。做一下多線程模塊的學習和總結,穩固一下多線程這塊的基礎。關於多線程的一些理論知識,這裡不想啰嗦太多,可以進行下搜索了解。
使用Java多線程,總的來說方法有兩種:①繼承Thread類,重寫run方法 ②把相關的類實現Runnable(可運行)接口,重寫run方法。③實現Callable接口(相對用得較少)
1 package com.scl.thread;
2
3 public class TestThread
4 {
5 public static void main(String[] args) throws Exception
6 {
7 String mainName = Thread.currentThread().getName();
8 System.out.println(mainName + "線程開始運行");
9
10 // 方法1 使用繼承對線程進行調用
11 MyThread myThread = new MyThread();
12 myThread.setName("wft");
13 myThread.start();
14 myThread.join();
15
16 // 方法2 繼承Runnable接口進行調用
17 Thread thread = new Thread(new MyRunnable());
18 thread.start();
19
20 System.out.println(mainName + "線程結束");
21 }
23 }
24
25 // 使用runnable開啟線程
26 class MyRunnable implements Runnable
27 {
28 @Override
29 public void run()
30 {
31 // 獲取調用線程的名字
32 String curName = Thread.currentThread().getName();
33 System.out.println(curName + "線程啟動");
34 try
35 {
36 Thread.sleep(1000);
37 System.out.println(curName + "正在運行");
38 }
39 catch (InterruptedException e)
40 {
41 e.printStackTrace();
42 }
43 System.out.println(curName + "線程結束");
44 }
45 }
46
47 // 使用Thread進行run方法的編寫,開啟線程
48 class MyThread extends Thread
49 {
50 // Thread.currentThread()調用這個方法的線程名稱。
51 String s = Thread.currentThread().getName(); // 調用這個類的是main方法調用,run方法交由新線程操作
52
53 public MyThread()
54 {
55 System.out.println("Thread.currentThread().getname()=" + Thread.currentThread().getName());
56 // 線程沒運行前初始化的名字.因為類的初始化跟線程運行無關,這裡返回的是處世話的名字
57 System.out.println("This.getName=" + this.getName());
58 }
59
60 @Override
61 public void run()
62 {
63 // run方法交由新線程操作,所以調用的名稱不是main
64 String strName = Thread.currentThread().getName();
65 System.out.println("this.getName " + this.getName());
66 // main
67 System.out.println("do u no s?" + s);
68
69 System.out.println(strName + " 開始數數");
70 for (int i = 0; i < 100; i++)
71 {
72 System.out.println(strName + " " + i);
73 }
74 System.out.println(strName + " 數數完畢");
75 }
76
77 // 返回設置的name
78 public String bName()
79 {
80 return this.getName();
81 }
82 }
最應該注意的是給線程標注名稱的時候,應該多使用Thread.currentThread().getname() 來獲取線程名稱。使用this.getName方法很容易出錯。
主要該注意的有如下兩個:
① 獲取名稱不在run方法裡面,永遠返回名稱為main。因為在構造線程類的時候,主線程main會進行代碼的初始化。run方法由新創建起來的線程去調用,這樣run方法裡面的名稱才能被正確獲取到
② 在繼承類的構造函數內,如上面MyThread的構造函數內調用getName()方法。該方法是由JDK 源碼Thread類繼承而來。new一個Thread實例時,系統���調用Thread
下的方法,代碼如下
//Thread 類構造函數
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
// 私有的init方法
private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
init(g, target, name, stackSize, null);
}
//init 方法. 更詳細內容見JDK源碼Thread類363行
private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc)
所以在構造器內的初始化的時候調用init方法給線程設置name字段。導致參數名稱修改且以"Thread-"開頭。初始化完畢後,如果用戶設置過相關的線程名稱,系統會把該字段進行修改,可以運用上述代碼中的bName()函數進行驗證。
在使用Java多線程的時候,不要直接調用某個實例下的run方法,如果直接使用run方法。則直接調用,不會開辟新的線程進行功能操作。
繼承Thread和實現Runnable接口的區別:
① 繼承Thread能夠簡單地實現線程開辟,但由於Java只支持單繼承。繼承Thread方法實現線程會有比較多的局限性
② 實現Runnable接口能夠很好的將線程代碼和業務邏輯代碼分離,但代碼結構相對復雜。一般都會使用實現Runnable方法創建線程。
① join( ) 多線程是有cpu分配執行時間片段,如果要控制線程操作流程,則需要使用到join方法。join方法會阻塞主線程,主線程會等待join方法的線程執行完畢再繼續執行後續的操作。同時join方法可以添加時間,讓主線程等待一段時間,如果時間過了。則不再等待繼續執行。如:thread.join( ),當主線程編譯到該代碼時,主線程會停止下來等待thread線程執行完畢後,主線程再繼續後續操作
② sleep( ) 不釋放鎖資源,讓調用的線程進行等待。如:thread.sleep( ) ,但代碼執行到這一句時,thread會停止下來等待,直到被喚醒或者等到到sleep到的時間點後再進行後續操作。
③ wait( )和notify( )、notifyAll( ) 。這三個方法主要處理線程同步數據時使用。數據同步,也就是幾條線程在同時修改一份共享數據。如火車站5名銷售員同時在售賣火車票,火車票是固定數量的前提下,如何實現這5名銷售員同時知道火車票庫存、同一路段火車票售賣、車票信息共享等。
wait方法使當前線程暫停執行並釋放對象鎖標志。notify和notifyall方法會喚醒幾個等待的線程,讓線程重新獲取鎖代碼以便後續進行。線程狀態圖表如下:
關於使用鎖以及線程狀態內容,將在下一篇文章中進行總結(http://www.linuxidc.com/Linux/2016-07/133403.htm),上述內容若有問題,煩請指出糾正。