Java面向對象程序設計第9章1-9

Java面向對象程序設計第9章1-9

1. 線程和進程的聯繫和區別是什麼?

聯繫:

  1. 一個進程能夠包括多個線程。

區別:

進程: 進程是一個具備必定獨立功能的程序關於某個數據集合的一次運行活動,它是系統進行資源分配和調度的一個獨立單位html

線程: 線程是進程的一個實體,是CPU調度和分配的基本單位。線程基本不擁有系統資源,與同一個進程的其餘線程共享進程中所擁有的全部資源。java


2. 什麼是前臺線程,什麼是後臺線程?

應用程序必須運行完全部的前臺線程才能夠退出;而對於後臺線程,應用程序則能夠不考慮其是否已經運行完畢而直接退出,全部的後臺線程在應用程序退出時都會自動結束面試


3. 建立線程有幾種方法?它們之間的區別是什麼?

第一種方式直接繼承線程Thread類建立對象
1.Thread子類沒法再從其它類繼承(java語言單繼承)。
2.編寫簡單,run()方法的當前對象就是線程對象,可直接操做。c#

public MyThread extends Thread{
     …… 
     public void run() {  線程體邏輯 }   
}
//建立及啓動線程:
MyThread t = new MyThread();
t.start();

第二種方式:使用Runnable接口建立線程
1.能夠實現多個線程資源共享
2.線程體run()方法所在的類能夠從其它類中繼承一些有用的屬性和方法api

public MyThread implements Runnable {
             …… 
             public void run() {  線程體邏輯 }    
        }
//建立及啓動線程:
MyThread t = new MyThread();
Thread t1 = new Thread(t);
t1.start();

4.線程的生命週期有哪些狀態?哪些方法能夠改變這些狀態?

1572082134052

  1. 建立狀態:線程對象已經建立,尚未在其上調用start()方法。
  2. 可運行狀態:當線程有資格運行,但調度程序尚未把它選定爲運行線程時線程所處的狀態。當start()方法調用時,線程首先進入可運行狀態。在線程運行以後或者從阻塞、等待或睡眠狀態回來後,也返回到可運行狀態。
  3. 運行狀態:線程調度程序從可運行池中選擇一個線程做爲當前線程時線程所處的狀態。這也是線程進入運行狀態的惟一一種方式。
  4. 阻塞狀態:這是線程有資格運行時它所處的狀態。如執行了join/sleep/wait方法,會讓出CPU,只有當引發阻塞的緣由消除時,線程才能轉入就緒狀態。
  5. 死亡態:當線程的run()方法完成時就認爲它死去。或者拋出一個未捕獲到的Exception或Error。

5.什麼是線程安全?爲何會產生線程安全問題?如何解決線程安全問題?

線程安全:線程安全就是多線程訪問時,採用了加鎖機制,當一個線程訪問該類的某個數據時,進行保護,其餘線程不能進行訪問直到該線程讀取完,其餘線程纔可以使用。不會出現數據不一致或者數據污染。 線程不安全就是不提供數據訪問保護,有可能出現多個線程前後更改數據形成所獲得的數據是髒數據
緣由是因爲不一樣線程獲取到資源時進行運算,但將來得及寫入時,線程改變,則另外線程讀入的資源就是錯誤,致使全部線程寫入讀入不一樣步。
解決辦法:緩存

使用監視器,使用關鍵字synchronized監視同步代碼塊,當一個線程得到監視權限時,別的線程就沒法得到,保證每時每刻只有一個線程調用某一方法

6.什麼是線程的同步通訊?同步通訊又是如何實現的?

線程同步通訊是但願實現兩個或多個線程之間的某種制約關係
實現:首先是用監視器synchronized來保證每次只有一個線程調用方法,其次引入一個boolean型標誌來判斷該線程是否執行或wait,兩個線程時使用notify(),多個線程時用notifyAll()來讓出監視器並喚醒其餘線程。這樣就實現了線程之間的關係。安全

//使用wait和notifyAll實現線程間同步通訊 (兩個存錢線程,一個取錢線程)
class Account{
    volatile private int value;
    //布爾標誌              
    volatile private boolean isMoney = false;   
    
    //put設爲同步方法
    synchronized void put(int i) {
      while(isMoney){
        try{ wait();} //線程等待
        catch(){Exception e}{} 
      }
      value = value + i; 
      System.out.println("存入"+i+" 帳上金額爲:"+value);
      isMoney = true;//設置標誌
      notifyAll(); //喚醒等待資源的全部線程
   }
   synchronized int get(int i) {//同步方法 
    while(!isMoney) ){
        try { wait();}
        catch(){Exception e}{}
     }
     if (value>i)
          value = value - i;         
     else {  
          i = value;
          value = 0;                    
     }
     System.out.println("取走"+i+" 帳上金額爲:"+value);
     isMoney = false;
     notifyAll(); 
     return i;                      
    } 
}

class  Save implements Runnable{
  private Account a1;     
  public Save(Account a1){this.a1 = a1;}
  public void run(){
      while(true){ a1.put(100);}
  }
}

class Fetch implements Runnable {
  private Account a1;
  public Fetch(Account a1) { this.a1 = a1 ;}
  public void run(){       
       while(true){ a1.get(100);}
  }
}

public class TestCommunicate{
 public static void main(String[] args){
    Account a1 = new Account();
    new Thread(new Save(a1)).start(); new Thread(new Save(a1)).start();      
    new Thread(new Fetch(a1)).start();
 }}

7.什麼是死鎖?

若是多個線程都處於等待狀態,彼此須要對方所佔用的監視器全部權,多線程

就構成死鎖(deadlock),Java即不能發現死鎖也不能避免死鎖。ide

方法一:{
synchronized(A) {
     ….
    synchronized(B) {
        ….
    }
}
}

方法二:{
synchronized(B) {
     ….
    synchronized(A) {
        ….
    }
}
}

注意函數

  1. 可能發生死鎖的代碼執行中不必定會死鎖,由於線程之間的執行存

    在很大的隨機性。

  2. 線程方法suspend()、resume()、stop()因爲存在引發死鎖的可能,

    於是逐漸不用(Deprecated)。


8.如何讓某個對象的A方法內的一個代碼塊和另外一個方法B實現同步?

class Account                              
{
    volatile private int value;
    void put(int i)                       
    {
        synchronized(this) {
        value = value + i; 
        System.out.println("存入"+i+" 帳上金額爲:"+value);
        }
    }
    synchronized int get(int i)                         
    {   
        if (value>i)
                value = value - i;             
            else                               
            {   i = value;
                value = 0;                     
            }
        System.out.println("取走"+i+" 帳上金額爲:"+value);
      return i;                      
    }    
}
class  Save implements Runnable            
{
    int a=2000;
    private Account a1;     
    public Save(Account a1)
    {
        this.a1 = a1;
    }
    public void run()
    {
        while(a-->0){
            a1.put(100);            
        }
    }
}
class Fetch implements Runnable            
{
    int a=2000;
    private Account a1;
    public Fetch(Account a1)
    {this.a1 = a1 ;}
    public void run()
    {       
        while(a-->0){       
            a1.get(100);            
        }       
    }
}
public class Test{
    public static void main(String[] args){
       Account a1 = new Account();
       new Thread(new Save(a1)).start(); 
       new Thread(new Fetch(a1)).start();
    }
}

put方法的代碼塊被監視,get函數被監視,保證了value的正確性,輸出結果爲存錢取錢的隨機順序, 這裏沒有設置存取的制約關係。


9.設計一個程序產生兩個線程A與B,B線程執行10秒鐘後,被A線程停止。

public class test {//如何中斷線程?答案是添加一個開關。
    private volatile static boolean on = false;

    //內部類
    public class MyThreadTwo implements Runnable {
        @Override
        public void run() {
            try {
                System.out.println("into ---" + Thread.currentThread().getName());
                Thread.sleep(10000);//因爲等待時間10秒,輸出多,上面的into語句被覆蓋了
                System.out.println("out " + Thread.currentThread().getName());
                test.on=true;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void start() {
        //一個Thread的構造函數接受一個Runnable參數,而傳入的lambda表達式正好符合其run()函數,
        // 因此Java編譯器推斷它爲Runnable。
        Thread thread1 = new Thread(() -> {
            while (!on) {
                System.out.println(Thread.currentThread().getName());
            }
        });
        Thread thread2 = new Thread(new MyThreadTwo());

        thread1.start();
        thread2.start();
    }

    public static void main(String[] args) {
        test test = new test();
        test.start();
    }
}

10.補充(選作):編寫一個基於多線程的生產者/消費者程序,各產生10個生產者和消費者線程,共享一個緩衝區隊列(長度自設),生產者線程將產品放入到緩衝區,消費者線程從緩衝區取出產品。

class Cache {
    volatile private int value;
    final private int productSize;

    public Cache(int pro) {
        productSize = pro;
    }

    synchronized void put() {//存
        while (value == productSize) {//緩存區產品長度滿時等待,不存
            try {
                wait();//線程等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        value = value + 1;
        System.out.println(Thread.currentThread().getName() + "存了" + 1+", 現value=" + value);
        notifyAll();//喚醒等待資源的全部線程
    }

    synchronized void get() {//取
        while (value == 0) {//緩存區產品長度空時等待,不取
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        value = value - 1;
        System.out.println(Thread.currentThread().getName() + "取了" + 1+", 現value=" + value);
        notifyAll();
    }
}

//生產者
class produce implements Runnable {
    private Cache a1;

    public produce(Cache a1) {
        this.a1 = a1;
    }

    public void run() {
        while (true) {
            a1.put();
        }
    }
}

//消費者
class consumer implements Runnable {
    private Cache a1;

    public consumer(Cache a1) {
        this.a1 = a1;
    }

    public void run() {
        while (true) {
            a1.get();
        }
    }
}

public class test{
    public static void main(String[] args) {
        Cache a1=new Cache(20);
        //10個生產者
        new Thread(new produce(a1)).start();
        new Thread(new produce(a1)).start();
        new Thread(new produce(a1)).start();
        new Thread(new produce(a1)).start();
        new Thread(new produce(a1)).start();
        new Thread(new produce(a1)).start();
        new Thread(new produce(a1)).start();
        new Thread(new produce(a1)).start();
        new Thread(new produce(a1)).start();
        new Thread(new produce(a1)).start();

        //10個消費者
        new Thread(new consumer(a1)).start();
        new Thread(new consumer(a1)).start();
        new Thread(new consumer(a1)).start();
        new Thread(new consumer(a1)).start();
        new Thread(new consumer(a1)).start();
        new Thread(new consumer(a1)).start();
        new Thread(new consumer(a1)).start();
        new Thread(new consumer(a1)).start();
        new Thread(new consumer(a1)).start();
        new Thread(new consumer(a1)).start();
    }
}

在這裏開始對volatile不是很瞭解其機制,記得老師講了i--的操做三條指令,詳細瞭解了下,具體看:

面試官最愛的volatile關鍵字

相關文章
相關標籤/搜索