JAVA多線程學習筆記及練習題


1、java中有幾種方法可以實現一個線程(jdk5.0之前)?用什麼關鍵字修飾同步方法? stop()和suspend()方法爲何不推薦使用?

答:有兩種實現方法,分別是繼承Thread類與實現Runnable接口。

用synchronized關鍵字修飾同步方法,反對使用stop(),是因爲它不安全。它會解除由線程獲取的所有鎖定,而且如果對象處於一種不連貫狀態,那麼其他線程能在那種狀態下檢查和修改它們。結果很難檢查出真正的問題所在。
suspend()方法容易發生死鎖。調用suspend()的時候,目標線程會停下來,但卻仍然持有在這之前獲得的鎖定。此時,其他任何線程都不能訪問鎖定的資源,除非被"掛起"的線程恢復運行。對任何線程來說,如果它們想恢復目標線程,同時又試圖使用任何一個鎖定的資源,就會造成死鎖。所以不應該使用suspend(),而應在自己的Thread類中置入一個標誌,
指出線程應該活動還是掛起。若標誌指出線程應該掛起,便用wait()命其進入等待狀態。若標誌指出線程應當恢復,則用一個notify()重新啓動線程。

2、sleep() 和 wait() 有什麼區別?

答:
sleep是線程類(Thread)的方法,導致此線程暫停執行指定時間,給執行機會給其他線程,但是監控狀態依然保持,到時後會自動恢復。調用sleep不會釋放對象鎖。

wait是Object類的方法,對此對象調用wait方法導致本線程放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象發出notify方法(或notifyAll)後本線程才進入對象鎖定池準備獲得對象鎖進入運行狀態。

3、同步和異步有何異同,在什麼情況下分別使用他們?舉例說明。

答:
如果數據將在線程間共享。例如正在寫的數據以後可能被另一個線程讀到,或者正在讀的數據可能已經被另一個線程寫過了,那麼這些數據就是共享數據,必須進行同步存取。
當應用程序在對象上調用了一個需要花費很長時間來執行的方法,並且不希望讓程序等待方法的返回時,就應該使用異步編程,在很多情況下采用異步途徑往往更有效率。

4、啓動一個線程是用run()還是start()?

答:啓動一個線程是調用start()方法,使線程所代表的虛擬處理機處於可運行狀態,這意味着它可以由JVM調度並執行。這並不意味着線程就會立即運行。run()方法就是正常的對象調用方法的執行,並不是使用分線程來執行的。 

5、當一個線程進入一個對象的一個synchronized方法後,其它線程是否可進入此對象的其它方法?

答:不能,一個對象的一個synchronized方法只能由一個線程訪問。

6、請說出你所知道的線程同步的方法。

答:wait():使一個線程處於等待狀態,並且釋放所持有的對象的lock。

sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要捕捉InterruptedException異常。

notify():喚醒一個處於等待狀態的線程,注意的是在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM確定喚醒哪個線程,而且不是按優先級。

notityAll():喚醒所有處入等待狀態的線程,注意並不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。

7、多線程有幾種實現方法,都是什麼?同步有幾種實現方法,都是什麼?

答:多線程有兩種實現方法,分別是繼承Thread類與實現Runnable接口 

同步的實現方面有兩種,分別是synchronized,wait與notify

8、線程的基本概念、線程的基本狀態以及狀態之間的關係

答:線程指在程序執行過程中,能夠執行程序代碼的一個執行單位,每個程序至少都有一個線程,也就是程序本身。

Java中的線程有四種狀態分別是:創建、就緒、運行、阻塞、結束

9、簡述synchronized和java.util.concurrent.locks.Lock的異同 ?

答:主要相同點:Lock能完成synchronized所實現的所有功能

主要不同點:Lock有比synchronized更精確的線程語義和更好的性能。synchronized會自動釋放鎖,而Lock一定要求程序員手工釋放,並且必須在finally從句中釋放。 

案例:三個線程間的通訊

  public class Demo01 {
      public static void main(String[] args) {
         //三個線程間的通訊
         MyTask task = new MyTask();
         new Thread(){
             public void run() {
                while(true){
                    try {
                       task.task1();
                    } catch (InterruptedException
  e1) {
                       e1.printStackTrace();
                    }
                    try {
                       Thread.sleep(10);
                    } catch (InterruptedException
  e) {
                       e.printStackTrace();
                    }
                }
             };
         }.start();
         new Thread(){
             public void run() {
                while(true){
                    try {
                       task.task2();
                    } catch (InterruptedException
  e1) {
                       e1.printStackTrace();
                    }
                    try {
                       Thread.sleep(10);
                    } catch (InterruptedException
  e) {
                       e.printStackTrace();
                    }
                }
             };
         }.start();
         new Thread(){
             public void run() {
                while(true){
                    try {
                       task.task3();
                    } catch (InterruptedException
  e1) {
                       e1.printStackTrace();
                    }
                    try {
                       Thread.sleep(10);
                    } catch (InterruptedException
  e) {
                       e.printStackTrace();
                    }
                }
             };
         }.start();
      }
  }
   
  class MyTask{
      
      //標識 1:可以執行任務1,2:可以執行任務2, 3:可以執行任務3 
      int flag = 1;
      
      public synchronized void task1() throws InterruptedException{
         if(flag != 1){
             this.wait();//當前線程等待
             //this.wait(timeout);
         }
         
         System.out.println("1.銀行信用卡自動還款任務...");
         flag = 2;
         //this.notify();//喚醒隨機線程
         this.notifyAll();//喚醒所有等待線程
         
      }
      
      public synchronized void task2() throws InterruptedException{
         
         if(flag != 2){
             this.wait();//線程等待
         }
         
         System.out.println("2.銀行儲蓄卡自動結算利息任務...");
         flag = 3;
         //this.notify();//喚醒其它線程
         this.notifyAll();
      }
      
      public synchronized void task3() throws InterruptedException{
             if(flag != 3){
                this.wait();//線程等待
             }
             
             System.out.println("3.銀行短信提醒任務...");
             flag = 1;
             //this.notify();//喚醒其它線程
             this.notifyAll();
      }
  }
  
 

一、判斷題

1.C 和 Java 都是多線程語言。(

2.如果線程死亡,它便不能運行。( )

3.在 Java 中,高優先級的可運行線程會搶佔低優先級線程。(

4.程序開發者必須創建一個線程去管理內存的分配。( )

5.一個線程在調用它的 start 方法,之前,該線程將一直處於出生期。(

6.當調用一個正在進行線程的
stop()方法時,該線程便會進入休眠狀態。( )

7.如果線程的 run 方法執行結束或拋出一個不能捕獲的例外,線程便進入等待狀態。(

8.一個線程可以調用 yield 方法使其他線程有機會運行。(

【答案】

1.難度:容易

答案:錯誤

知識點:C 是單線程語言。

2.難度:容易

答案:正確

知識點:線程死亡就意味着它不能運行。

3.難度:適中

答案:正確

知識點:線程優先級的使用。

4.難度:適中

答案:錯誤

知識點:Java 提供了一個系統線程來管理內存的分配。

5.難度:容易

答案:正確

知識點:出生期的概念。

6.難度:適中

答案:錯誤

知識點:應該是 sleep 方法。

7.難度:適中

答案:錯誤

知識點:如果線程的 run 方法執行結束或拋出一個不能捕獲的例外,線程便進入死亡狀態。

8.難度:適中

答案:正確

知識點:yield 方法總是讓高優先級的就緒線程先運行。

二、選擇題

1.Java 語言中提供了一個▁▁線程,自動回收動態分配的內存。

A 異步

B 消費者

C 守護

D 垃圾收集

2.當▁▁方法終止時,能使線程進入死亡狀態。

A run

B setPrority

C yield

D sleep

3.用▁▁方法可以改變線程的優先級。

A run

B setPrority

C yield

D sleep

4.線程通過▁▁方法可以使具有相同優先級線程獲得處理器。

A run

B setPrority

C yield

D sleep

5.線程通過▁▁方法可以休眠一段時間,然後恢復運行。

A run

B setPrority

C yield

D sleep

6.▁▁方法使對象等待隊列的第一個線程進入就緒狀態。

A run

B notify

C yield

D sleep

7.方法 resume( )負責重新開始▁▁線程的執行。

A 被 stop( )方法停止

B 被 sleep( )方法停止

C 被 wait( )方法停止

D 被 suspend( )方法停止

8.▁▁方法可以用來暫時停止當前線程的運行。

A stop( )

B sleep( )

C wait( )

D suspend()

【答案】

1.難度:容易

答案:D

知識點:垃圾線程的使用。

2.難度:容易

答案:A

知識點:run 方法的使用。

3.難度:容易

答案:B

知識點:setPrority 方法的使用。

4.難度:容易

答案:C

知識點:yield 方法的使用。

5.難度:容易

答案:D

知識點:sleep 方法的使用。

6.難度:容易

答案:B

知識點:notify 方法的使用。

7.難度:適中

答案:D

知識點:一個線程被用 suspend( )方法,將該線程掛起。並通過調用 resume( )方法來重新開始線程的執行。

但是該方法容易導致死鎖,應儘量避免使用。

8.難度:適中

答案:BCD

知識點:當調用 stop( )方法後,當前的線程不能重新開始運行。

Java爲什麼要引入線程機制,線程、程序、進程之間的關係是怎樣的。

答:線程可以彼此獨立的執行,它是一種實現併發機制的有效手段,可以同時使用多個線程來完成不同的任務,並且一般用戶在使用多線程時並不考慮底層處理的細節。

程序是一段靜態的代碼,是軟件執行的藍本。進程是程序的一次動態執行過程,即是處於運行過程中的程序。

線程是比進程更小的程序執行單位,一個進程可以啓動多個線程同時運行,不同線程之間可以共享相同的內存區域和數據。多線程程序是運行時間後嗣可能出現在一個進程之內的、有一個以上線程同時運行的情況的程序。

Runnable接口包括哪些抽象方法?Thread類有哪些主要域和方法?

答:Runnable接口中僅有run()抽象方法。

Thread類主要域有:MAX_PRIORITY,MIN_PRIORITY,NORM_PRIORITY。

主要方法有start(),run(),sleep(),currentThread(),setPriority(),getPriority(),join()等。

創建線程有哪兩種方式(jdk5.0之前)?試寫出每種的具體的流程。比較兩種創建方式的不同,哪個更優。

1—繼承Thread類

1)  定義類繼承Thread類。

2)  覆蓋Thread類中的run方法。

3)  創建Thread子類對象,即創建了線程對象。

4)  調用線程對象start方法:啓動線程,調用run方法。



2—實現Runnable接口

1)定義類,實現Runnable接口。

2)覆蓋Runnable接口中的run方法。

3)通過Thread類建立線程對象。

4)將Runnable接口的子類對象作爲實際參數傳遞給Thread類的構造方法中。

5)調用Thread類的start方法:開啓線程,調用Runnable子類接口的run方法。

【區別】

繼承Thread: 線程代碼存放Thread子類run方法中。

實現Runnable:線程代碼存在接口的子類的run方法。

【實現方法的好處】

1)避免了單繼承的侷限性

2)多個線程可以共享同一個接口子類的對象,非常適合多個相同線程來處理同一份資源。

創建多線程題目

編寫一個繼承Thread類的方式實現多線程的程序。該類MyThread有兩個屬性,一個字符串WhoAmI代表線程名,一個整數delay代表該線程隨機要休眠的時間。構造有參的構造器,線程執行時,顯示線程名和要休眠時間。

另外,定義一個測試類TestThread,創建三個線程對象以展示執行情況。
  
  class MyThread extends Thread {
      private String whoAmI;
      private int delay;
   
      public MyThread(String s, int d) {
         whoAmI = s;
         delay = d;
      }
   
      public void run() {
         try {
             sleep(delay);
         } catch (InterruptedException ie) {
         }
         System.out.println("Hello!I am" + whoAmI + ",I slept" + delay + "milliseconds");
      }
  }
   
  public class TestThread {
      public static void main(String[] args) {
         MyThread t1 = new MyThread("Thread-1", (int) (Math.random() * 100));
         MyThread t2 = new MyThread("Thread-2", (int) (Math.random() * 100));
         MyThread t3 = new MyThread("Thread-3", (int) (Math.random() * 100));
         t1.start();
         t2.start();
         t3.start();
      }
  }
   
  

在{1}添加什麼代碼,可以保證如下代碼輸出100

提示:t.wait() 或 t.jion() 或 t.yield() 或 t.interrupt()?




 public class
  Test {
      public static void main(String[] args) {
          MyThread m = new MyThread();
          Thread t = new Thread(m);
          t.start();
          
                  {1}              
          
          int j = m.i;
          System.out.println(j);
      }
  }
  class MyThread implements Runnable{
      int i;
      public void
  run(){
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
          i=100;
      }
  }

答案:t.join()

利用多線程設計一個程序,同時輸出 50 以內的奇數和偶數,以及當前運行的線程名。

  
  public class ThreadPrint extends Thread {
      int k = 1;
   
      public void run() {
         int i = k;
         while (i < 50) {
             System.out.println(Thread.currentThread().getName() + "-----" + i);
             i += 2;
         }
          System.out.println(Thread.currentThread().getName() + " end!");
      }
   
      public static void main(String[] args) {
         ThreadPrint t1 = new ThreadPrint();
         ThreadPrint t2 = new ThreadPrint();
         t1.k = 1;
         t2.k = 2;
         t1.start();
         t2.start();
      }
  }
  
 


 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章