一、 填空題
- 處於運行狀態的線程在某些情況下,如執行了sleep(睡眠)方法,或等待I/O設備等資源,將讓出CPU並暫時停止自己的運行,進入____阻塞_____狀態。
- 處於新建狀態的線程被啟動后,將進入線程隊列排隊等待CPU,此時它已具備了運行條件,一旦輪到享用CPU資源就可以獲得執行機會。上述線程是處於 就緒 狀態。
解析: 線程的生命周期—五個階段
新建狀態---- new關鍵字創建線程對象
就緒狀態---- start()方法運行之前
運行狀態---- run()方法的運行
堵塞狀態--- interrupt() sleep() join() yield() wait() 等方法
死亡狀態--- stop()方法(已過時) /或run()方法運行結束
- 一個正在執行的線程可能被人為地中斷,讓出CPU的使用權,暫時中止自己的執行,進入 阻塞 狀態。
解析:人為的中斷正在執行的線程---- 執行wait() 、interrupt() 、sleep()方法等
- 在Java中編寫實現多線程應用有兩種途徑:一種是繼承Thread類創建線程,另一種是實現 Runnable 接口創建線程。
解析: 繼承Thread類來創建線程,不能實現資源的共享,除非資源使用static修飾。
實現Runnable接口創建線程,此時還需要借助Thread類,因為Runnable接口
沒有start()方法,同時Thread類是Runnable接口的子類….
- 在線程控制中,可以調用_____join()____方法,阻塞當前正在執行的線程,等插隊線程執行完后后再執行阻塞線程。
- 多線程訪問某個共享資源可能出現線程安全問題,此時可以使用______synchronized_____關鍵字來實現線程同步,從而避免安全問題出現,但會影響性能,甚至出現死鎖。
- 在線程通信中,調用wait( )可以是當前線程處於等待狀態,而為了喚醒一個等待的線程,需要調用的方法是____notify()/notifyAll()__________。
解析:線程等待使用wait()方法,此方法最先是存在於Object類,Thread類繼承Object類,可以獲取此方法,同時notify()方法和notifyAll()方法---線程喚醒,最先也是存在於
Object類當中。
- 在線程通信中,可以調用wait()、notify()、notifyAll()三個方法實現線程通信,這三個方法都是____Object____類提供的public方法,所以任何類都具有這三個方法。
二、 選擇題
1. |
下列關於Java線程的說法正確的是( A )。(選擇一項) |
|
|
|
|
|
A |
每一個Java線程可以看成由代碼、一個真實的CPU以及數據三部分組成 |
|
B. |
創建線程的兩種方法中,從Thread類中繼承方式可以防止出現多父類的問題 |
|
C. |
Thread類屬於java.util程序包 |
|
D. |
使用new Thread(new X()).run();方法啟動一個線程 解析: A、語句正確。 B、創建線程兩種方法:一是繼承Thread類,一是實現Runnable接口 根據Java語言類的單繼承特性,防止出現多繼承,但是可以實現多 重繼承,也可以出現多父類。 C、Thread類是存在於java.lang包下 D、啟動一個線程是調用start()方法,之后JVM會默認的調用run()方法 run()方法是線程的主體,核心代碼放到此方法當中。 |
2. |
以下選項中可以填寫到橫線處,讓代碼正確編譯和運行的是( AD )。(選擇一項) |
|
|
public class Test implements Runnable { public static void main(String[] args) { ___________________________________ t.start(); System.out.println("main"); } public void run() { System.out.println("thread1!"); } } |
|
|
|
|
|
A. |
Thread t = new Thread(new Test()); |
|
B. |
Test t = new Test(); |
|
C. |
Thread t = new Test(); |
|
D. |
Thread t = new Thread(); 解析: 線程的創建有兩種方式,其中之一就是實現Runnable接口。 此接口只有一個抽象方法---run()方法,啟動線程又需要start()方法 此時還需要借助Thread類,根據Thread類的構造方法: Public Thread(Runnable run) 需要傳入Runnable接口對象 此題還有一個方法: 直接創建Thread類對象,調用start()方法 所以D選項也是正確的,但是此題重點還是考的子類實現Runnable接口 根據Thread類的構造方法啟動線程。 |
3. |
如下代碼創建一個新線程並啟動線程,問:四個選項中可以保證正確代碼創建target對象,並能編譯正確的是( C )?(選擇一項) |
|
|
public static void main(String[] args) { Runnable target=new MyRunnable( ); Thread myThread=new Thread(target); } |
|
|
|
|
|
A |
public class MyRunnable extends Runnable { public void run( ) { } } |
|
B. |
public class MyRunnable extends Runnable { void run( ) { } } |
|
C. |
public class MyRunnable implements Runnable { public void run( ) { } } |
|
D. |
public class MyRunnable implements Runnable { void run( ) { } 解析:接口是用來實現的,不是繼承,所以AB兩個選項錯誤。 根據Java多態機制,子類可以為父類(接口)實例化對象 根據子類實現接口的規則,子類必須重寫接口當中的抽象方法 此時又復習到了方法重寫的規則:子類重寫的方法它的權限必須 大於等於父類方法權限(除去private外)。根據以上得出C是對的
|
4. |
當線程調用start( )后,其所處狀態為( C )。(選擇一項) |
|
|
|
|
|
A |
阻塞狀態 |
|
B. |
運行狀態 |
|
C. |
就緒狀態 |
|
D. |
新建狀態 解析:線程的五個狀態也就是它的生命周期: 新建狀態----通過new關鍵字來創建線程對象 就緒狀態----調用start()方法 運行狀態----調用run()方法 阻塞狀態----調用sleep()、wait()、join() 、yield()、interrupt ()等方法 消亡狀態----調用stop()方法,但是此方法已經過時。
|
5. |
下列關於Thread類提供的線程控制方法的說法中,錯誤的是( C )。(選擇一項) |
|
|
|
|
|
A |
線程A中執行線程B的join()方法,則線程A等待直到B執行完成 |
|
B. |
線程A通過調用interrupt()方法來中斷其阻塞狀態 |
|
C. |
若線程A調用方法isAlive()返回值為false,則說明A正在執行中,也可能是可運行狀態 |
|
D. |
currentThread()方法返回當前線程的引用 解析: A: join()方法—強制加入,加入的線程執行完畢之后才能執行其他線程 B:interrupt()方法---線程中斷,題中的說法有些牽強 C:isAlive()方法---判斷一個線程是否在活動,如果在活動返回真,反之假 D:currentThread()方法返回正在執行的線程 從以上得知,C明顯錯誤。 |
6. |
下列關於線程的優先級說法中,正確的是( BD )。(選擇兩項) |
|
|
|
|
|
A |
線程的優先級是不能改變的 |
|
B. |
線程的優先級是在創建線程時設置的 |
|
C. |
在創建線程后的任何時候都可以重新設置 |
|
D. |
線程的優先級的范圍在1-10之間 |
解析:線程的優先級可以通過setPriority(int newPriority)
的方法進行設置
線程一共有三個優先級,分別是:最低優先級(1) 中等優先級(5)
最高優先級(10),咱們經常寫的main方法就是中等優先級線程。
A:線程的優先級是可以更改也可以獲取,但是有一點,即使設置為最高
優先級也不一定先執行,只是它優先執行的幾率比較高。
B:線程的優先級是在創建時進行設置,通過setPriority()
方法設置
C:正在執行的線程是不允許重新設置線程優先級的。
D:線程的優先級范圍是1—10 符合要求
7. |
以下選項中關於Java中線程控制方法的說法正確的是( AD )。(選擇二項) |
|
|
|
|
|
A. |
join ( ) 的作用是阻塞指定線程等到另一個線程完成以后再繼續執行 |
|
B. |
sleep ( ) 的作用是讓當前正在執行線程暫停,線程將轉入就緒狀態 |
|
C. |
yield ( ) 的作用是使線程停止運行一段時間,將處於阻塞狀態 |
|
D. |
setDaemon( )的作用是將指定的線程設置成后台線程 解析: A:join()方法—線程的強制加入,加入的線程執行完畢之后再執行其他線程 B:sleep()方法—線程休眠,等時間過時,線程處於運行狀態 C:yield()方法—線程禮讓,讓出CUP資源,其他線程先執行,有些與join() 方法類似 D:setDaemon()方法--將指定的線程設置成后台線程,對的。 題的答案是:AD,但是個人感覺C選項也是對的…… |
8. |
在多個線程訪問同一個資源時,可以使用( A )關鍵字來實現線程同步,保證對資源安全訪問。(選擇一項) |
|
|
|
|
|
A. |
Synchronized |
|
B. |
Transient |
|
C. |
Static |
|
D. |
Yield |
解析:關於同步,有兩種實現方式,一種是同步方法,一種是同步代碼塊
無論怎樣,都需要使用到synchronized關鍵字。
同步方法:
權限修飾符 synchronized 返回值類型 方法名稱(參數列表){
N行代碼;
}
同步代碼塊:
Synchronized(對象){
N行代碼;
}
9. |
Java中線程安全問題是通過關鍵字( C )解決的?。(選擇一項) |
|
|
|
|
|
A. |
Finally |
|
B. |
wait( ) |
|
C. |
Synchronized |
|
D. |
notify( ) |
10. |
以下說法中關於線程通信的說法錯誤的是( D )?。(選擇一項) |
|
|
|
|
|
A. |
可以調用wait()、notify()、notifyAll()三個方法實現線程通信 |
|
B. |
wait()、notify()、notifyAll()必須在synchronized方法或者代碼塊中使用 |
|
C. |
wait()有多個重載的方法,可以指定等待的時間 |
|
D. |
wait()、notify()、notifyAll()是Object類提供的方法,子類可以重寫 解析: A選項: 在線程通信中,可以調用wait()、notify()、notifyAll()三個方法實現線程通信,這三個方法都是Object類提供的public方法,所以任何類都具有這三個方法。 B選項:在編程題的第二題當中,wait()方法、notify()方法、notifyAll()方法都是寫在同步方法當中,具體可以查看此類的源碼。 C選項:Object類當中的wait方法的重載如下: public final void wait() public final void wait(long timeout) public final void wait(long timeout, int nanos) 三個方法都拋出異常-- InterruptedException D選項 : 看A選項的解釋 以上三個方法都使用final修飾,所以子類是不能重寫的 |
三、 判斷題
- 進程是線程Thread內部的一個執行單元,它是程序中一個單一順序控制流程。( × )
解析:線程是進程的一個執行單位,進程包含線程。
- Thread類實現了Runnable接口。( √ )
解析:Thread類在JDK當中的定義如下:
Public class Thread extends Object implements Runnable
從定義可知,它是Runnable接口的子類。
- 一個進程可以包括多個線程。兩者的一個主要區別是:線程是資源分配的單位,而進程CPU調度和執行的單位。( × )
解析:不管是進程還是線程,都是通過循環獲得自己執行的時間片,獲得CUP資源。
- 用new關鍵字建立一個線程對象后,該線程對象就處於新生狀態。處於新生狀態的線程有自己的內存空間,通過調用start進入就緒狀態。( √ )
解析:具體解析看選擇題第四題的解釋。
- A線程的優先級是10,B線程的優先級是1,那么當進行調度時一定會先調用A( × )
解析:線程的執行,不是誰的優先級高就先執行,只是它的概率會高。
- 線程可以用yield使低優先級的線程運行。( × )
解析:yield()方法是線程禮讓,可以讓出自己執行其他線程,由於線程的執行存在嚴重
的隨機性,不能確定使低優先級的線程執行。
- Thread.sleep( )方法調用后,當等待時間未到,該線程所處狀態為阻塞狀態。當等待時間已到,該線程所處狀態為運行狀態。( √ )
解析:sleep()方法是線程休眠,處於阻塞狀態,當時間一到,就會執行處於運行狀態。
如果真要追究用詞的嚴謹性,應該不是”等待時間”,而是”休眠時間”。
- 當一個線程進入一個對象的一個synchronized方法后,其它線程不可以再進入該對象同步的其它方法執行。( √ )
解析:對的,可以查看一下編程題第二題的源碼。
- wait方法被調用時,所在線程是會釋放所持有的鎖資源, sleep方法不會釋放。( √ )
解析:sleep 是線程類(Thread)的方法,導致此線程暫停執行指定時間,給執行機會給其他線程,但是監控狀態依然保持,到時后會自動恢復,所以調用sleep 不會釋放對象鎖。
wait 是Object 類的方法,對此對象調用wait 方法導致本線程放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象發出notify 方法(或notifyAll)后本線程才進入對象鎖定池准備獲得對象鎖進入運行狀態。
- wait、notify、notifyAll是在Object類中定義的方法。( √ )
解析:具體可以查看API幫助文檔,查看。
- notify是喚醒所在對象wait pool中的第一個線程。( × )
解析:喚醒的不一定是第一個線程。
JDK已經明確說明喚醒哪個thread是隨意決定的,沒有特定順序
The choice is arbitrary and occurs at the discretion of the implementation
四、 簡答題
- 簡述程序、進程和線程的聯系和區別。
- 創建線程的兩種方式分別是什么?各有什么優缺點。
- sleep、yield、join方法的區別?
- synchronize修飾的語句塊,如下面的代碼。是表示該代碼塊運行時必須獲得account對象的鎖。如果沒有獲得,會有什么情況發生?
synchronized (account) { if(account.money-drawingNum<0){ return; } } |
- 請你簡述sleep( )和wait( )有什么區別?
- 死鎖是怎么造成的?用文字表達。再寫一個代碼示例。
- Java中實現線程通信的三個方法及其作用。
- 為什么不推薦使用stop和destroy方法來結束線程的運行?
五、 編碼題
1.設計一個多線程的程序如下:設計一個火車售票模擬程序。假如火車站要有100張火車票要賣出,現在有5個售票點同時售票,用5個線程模擬這5個售票點的售票情況。
1 public class CharPrint extends Thread 2 { 3 // 定義屬性
4 private Printer p; 5 //構造方法
6 public CharPrint(Printer p) 7 { 8 super(); 9 this.p = p; 10 } 11 //重寫run()方法
12 public void run() 13 { 14 char c = 'A';//定義起始位置 15 //通過for循環對其進行輸出
16 while (c <= 'Z') 17 { 18 p.print(c); 19 c++;//控制while循環當中的條件表達式
20 } 21 } 22 } 23 public class NumberPrint extends Thread 24 { 25 //定義屬性
26 private Printer p; 27 //構造方法
28 public NumberPrint(Printer p) 29 { 30 this.p = p; 31 } 32 //重寫run()方法
33 public void run() 34 { 35 int i = 1;//定義起始位置
36 while (i <= 52) 37 { 38 p.print(i); 39 i++;//控制while循環當中的條件表達式
40 } 41 } 42 } 43 public class Printer 44 { 45 //定義一個int類型變量---用於計數
46 int index = 1; 47 //同步的方法---print()
48 public synchronized void print(int i) 49 { 50 //判斷是否是3的倍數
51 while (index % 3 == 0) 52 { 53 try
54 { 55 wait();//線程等待
56 } catch (InterruptedException e) 57 { 58 // TODO Auto-generated catch block
59 e.printStackTrace(); 60 } 61 } 62 //打印輸出數字
63 System.out.print("" + i); 64 //控制while循環中的條件表達式
65 index++; 66 //線程喚醒
67 notifyAll(); 68 } 69 public synchronized void print(char c) 70 { 71 //判斷是否是3的倍數
72 while (index % 3 != 0) 73 { 74 try
75 { 76 wait(); 77 } catch (InterruptedException e) 78 { 79 // TODO Auto-generated catch block
80 e.printStackTrace(); 81 } 82 } 83 //打印字母
84 System.out.print("" + c); 85 //控制while循環中的條件表達式
86 index++; 87 //喚醒線程
88 notifyAll(); 89 } 90 } 91 public class Test 92 { 93 public static void main(String[] args) 94 { 95 //創建Printer對象,為NumberPrint()類構造方法做准備
96 Printer p = new Printer(); 97 //創建線程---子類為父類實例化對象
98 Thread t1 = new NumberPrint(p); 99 Thread t2 = new CharPrint(p); 100 //啟動線程
101 t1.start(); 102 t2.start(); 103 } 104 }
2.編寫兩個線程,一個線程打印1-52的整數,另一個線程打印字母A-Z。打印順序為12A34B56C….5152Z。即按照整數和字母的順序從小到大打印,並且每打印兩個整數后,打印一個字母,交替循環打印,直到打印到整數52和字母Z結束。
要求:
1) 編寫打印類Printer,聲明私有屬性index,初始值為1,用來表示是第幾次打印。
2) 在打印類Printer中編寫打印數字的方法print(int i),3的倍數就使用wait()方法等待,否則就輸出i,使用notifyAll()進行喚醒其它線程。
3) 在打印類Printer中編寫打印字母的方法print(char c),不是3的倍數就等待,否則就打印輸出字母c,使用notifyAll()進行喚醒其它線程。
4) 編寫打印數字的線程NumberPrinter繼承Thread類,聲明私有屬性private Printer p;在構造方法中進行賦值,實現父類的run方法,調用Printer類中的輸出數字的方法。
5) 編寫打印字母的線程LetterPrinter繼承Thread類,聲明私有屬性private Printer p;在構造方法中進行賦值,實現父類的run方法,調用Printer類中的輸出字母的方法。
6) 編寫測試類Test,創建打印類對象,創建兩個線程類對象,啟動線程。
1 public class TicketSalSystem implements Runnable 2 { 3 // 定義變量---票數/票號
4 public int ticket = 100; 5 public int count = 0; 6 // 重寫run()方法
7 public void run() 8 { 9 // 定義while循環, 循環售票
10 while (ticket > 0) 11 { 12 // 根據題的要求,實現同步,此時定義同步代碼塊
13 synchronized (this) 14 {// 傳入對象,使用this代替當前類對象 15 // 判斷是否還有票,如果大於零說明還有票可賣
16 if (ticket > 0) 17 { 18 // 線程休眠0.5秒
19 try
20 { 21 Thread.sleep(500); 22 } catch (InterruptedException e) 23 { 24 e.printStackTrace(); 25 } 26 count++; // 票號++
27 ticket--;// 循環售票,賣一張少一張 28 // 輸出當前的售票窗口和票號
29 System.out.println(Thread.currentThread().getName() 30 + "\t當前票號:" + count); 31 } 32 } 33 } 34 } 35 } 36 public class TicketTest 37 { 38 public static void main(String[] args) 39 { 40 // 創建線程類對象
41 TicketSalSystem st = new TicketSalSystem(); 42 // 啟動5次線程
43 for (char i = 'A'; i <= 'F'; i++) 44 { 45 /*
46 * 創建匿名Thread類對象 47 * 1、根據Thread類構造方法,傳入Runnable接口對象 48 * TicketSalSystem類是Runnable接口的子類,子類對象st可以讓父類接收 49 * 2、Thread類的構造方法如下: 50 * public Thread(Runnable run,String name) 51 * 在創建線程的同時,為線程名稱 52 * 3、啟動線程是使用start方法,啟動之后JVM會默認的區調用run()方法 53 */
54 new Thread(st, "售票口" + i).start(); 55 } 56 } 57 }
六、 可選題
1.設計4個線程,其中兩個線程每次對j增加1,另外兩個線程對j每次減少1。
要求:使用內部類實現線程,對j增減的時候不考慮順序問題。
1 public class ThreadTest61 2 { 3 //聲明成員變量j
4 private int j; 5 public static void main(String[] args) 6 { 7 //創建ThreadTest61的對象
8 ThreadTest61 tt = new ThreadTest61(); 9 //創建內部線程類對象
10 Inc inc = tt.new Inc(); 11 Dec dec = tt.new Dec(); 12 for (int i = 0; i < 2; i++) 13 { 14 // 創建線程對象,啟動線程
15 Thread t = new Thread(inc); 16 t.start(); 17 // 創建線程對象,啟動線程
18 t = new Thread(dec); 19 t.start(); 20 } 21 } 22 //實現同步對j的值加
23 private synchronized void inc() 24 { 25 //調用此方法一次,對j就增加一次
26 j++; 27 System.out.println(Thread.currentThread().getName() + "-inc:" + j); 28 } 29 //實現同步對j的值減
30 private synchronized void dec() 31 { 32 //調用此方法一次,對j就減一次
33 j--; 34 System.out.println(Thread.currentThread().getName() + "-dec:" + j); 35 } 36 //內部類實現Runnable接口,重寫run()方法
37 class Inc implements Runnable 38 { 39 public void run() 40 { 41 //for循環當中調用inc()方法,實現每次對j加1
42 for (int i = 0; i < 100; i++) 43 { 44 //調用加的方法
45 inc(); 46 } 47 } 48 } 49 //內部類實現Runnable接口,重寫run()方法
50 class Dec implements Runnable 51 { 52 public void run() 53 { 54 //for循環當中調用inc()方法,實現每次對j減1
55 for (int i = 0; i < 100; i++) 56 { 57 //調用減的方法
58 dec(); 59 } 60 } 61 } 62 }
2.編寫多線程程序,模擬多個人通過一個山洞的模擬。這個山洞每次只能通過一個人,每個人通過山洞的時間為5秒,有10個人同時准備過此山洞,顯示每次通過山洞人的姓名和順序。
1 public class ThreadTest62 2 { 3 public static void main(String[] args) 4 { 5 /*創建一個山洞對象,為Thread類構造方法做准備 6 * public Thread(Runnable run,String name) 7 */
8 Tunnel tul = new Tunnel(); 9 //創建十個過山洞線程,並為線程命名
10 Thread p1 = new Thread(tul, "p1"); 11 Thread p2 = new Thread(tul, "p2"); 12 Thread p3 = new Thread(tul, "p3"); 13 Thread p4 = new Thread(tul, "p4"); 14 Thread p5 = new Thread(tul, "p5"); 15 Thread p6 = new Thread(tul, "p6"); 16 Thread p7 = new Thread(tul, "p7"); 17 Thread p8 = new Thread(tul, "p8"); 18 Thread p9 = new Thread(tul, "p9"); 19 Thread p10 = new Thread(tul, "p10"); 20 //啟動十個線程
21 p1.start(); 22 p2.start(); 23 p3.start(); 24 p4.start(); 25 p5.start(); 26 p6.start(); 27 p7.start(); 28 p8.start(); 29 p9.start(); 30 p10.start(); 31 } 32 } 33 //創建山洞類並實現Runnable接口,重寫run()方法
34 class Tunnel implements Runnable 35 { 36 //定義變量,初始化過山洞人數
37 int crossedNum = 0; 38 //重寫run()方法,在此方法當中調用cross()方法
39 public void run() 40 { 41 cross(); 42 } 43 //定義過山洞的同步方法
44 public synchronized void cross() 45 { 46 //每個人通過山洞的時間為5秒
47 try
48 { 49 Thread.sleep(5000); 50 } catch (InterruptedException e) 51 { 52 e.printStackTrace(); 53 } 54 //人數統計,過一個人增加一個人
55 crossedNum++; 56 //顯示每次通過山洞人的姓名
57 System.out.println(Thread.currentThread().getName() 58 + "通過了山洞,這是第" + crossedNum + "個用戶"); 59 } 60 }