201621123031 《Java程序設計》第11周學習總結

做業11-多線程


1. 本週學習總結

1.1 以你喜歡的方式(思惟導圖或其餘)概括總結多線程相關內容。

2. 書面做業

本次PTA做業題集多線程java

1. 源代碼閱讀:多線程程序BounceThread

1.1 BallRunnable類有什麼用?爲何代碼中須要調用Thread.sleep進行休眠?

答:BallRunnable類實現了Runnable接口,支持了多線程,在其中用於給線程指派任務,在類中的run方法裏經過循環調用小球的移動函數move和重畫函數repaint來實現小球的移動軌跡。還有用了Treep.sleep()來使線程休眠一段時間。
使用Tread.sleep讓線程進行休眠是爲了延緩線程完成的時間,這樣可讓咱們看到小球的移動,否則不進行休眠的話,這個程序會在一個很快的時間內完成,沒有辦法看到小球的移動軌跡,而且sleep()爲Thread類的靜態方法,調用它不會建立一個新線程。安全

1.2 Ball.java只作了兩件事,這兩件事分別是什麼?BallComponent對象是幹什麼的?其內部的ArrayList有什麼用?程序運行過程當中,生成了幾個BallComponent對象?該程序使用了多線程技術,每一個小球是分別在不一樣的線程中進行繪製嗎?

答:Ball.java完成的兩件事:多線程

  • 定義了move函數實現小球的移動方法
  • 定義了getShape函數來獲取小球的大小和座標

BallComponent對象也實現了兩件事:dom

  • 添加一個小球
  • 畫出一個小球

其內部的ArrayList用於存放添加的小球
程序中只生成了一個BallComponent對象
經過源代碼能夠看出每按一次start按鈕,addBall方法都會啓動一個新線程,所以每一個小球都是在本身的線程中繪製的。eclipse

1.3 選作:程序改寫:程序運行時,每一個小球都是從固定位置出發。如何改寫該程序,使得當點擊start時,每一個小球能夠從不一樣位置出發、以不一樣的步進移動?

答:可使用Math.random()方法將ball.java中的xydxdy的值設置成隨機數來達到每次點擊start時使小球以不一樣位置出發和以不一樣步前進。

函數

1.4 選作:不一樣小球的移動軌跡如出一轍。改造程序,使得每一個小球運行軌跡不徹底同樣,好比有的能夠走餘弦軌跡、有的能夠走方波軌跡、有的走布朗運動、有的走五角星,等等。

2. 實驗總結:題集(多線程)

2.1 題目:Thread、PrintTask、Runnable與匿名內部類。

並回答:a)經過定義Runnable接口的實現類來實現多線程程序比經過繼承自Thread類實現多線程程序有何好處?b) 6-1,6-3,6-11實驗總結。
答:(a).使用實現Runnable接口來實現多繼承的程序的好處:性能

  • 1.Java中不支持多繼承,一個類能夠繼承多個接口,可是隻能繼承一個父類,所以使用實現接口的方法能夠避免繼承的侷限。
  • 2.使用Runnable接口適合於資源的共享,以下兩張圖片:


能夠看出,雖然兩個程序中都建立了兩個線程,可是使用Runnable實現多線程使兩個線程一塊兒完成兩個任務,達到資源共享的目的。
(b).Thread實驗總結
這題中要注意MyThread類有一個有參構造函數,用於接收循環次數,而後要將標識信息放在for循環外,其餘就是普通的打印輸出了。
Runnable實驗總結
這題使用了匿名內部類來實現Runnable接口的run方法來完成程序。獲取當前的線程名字要用Thread.currentThread().getName(),而後就是按要求輸出就OK了。
PrintTask實驗總結
這題……除了類名和最後輸出的標識信息,其餘都和6-1同樣hhhh學習

2.2 使用Lambda表達式改寫6-3

Thread t1 = new Thread(() -> {
            System.out.println(mainThreadName);
            System.out.println(Thread.currentThread().getName());
            System.out.println(Arrays.toString(Thread.class.getInterfaces()));
        });

2.3 題目:6-2(Runnable與中止線程)。回答:須要怎樣才能正確地中止一個運行中的線程?

答:在Java的Thread類中提供了一個stop()方法用來終止線程,不過,由於這個方法會將執行到一半的線程強行終止,,不能保證線程的資源正確釋放,因此已經被廢棄了。
如今在咱們須要中止一個線程的時候通常使用一個boolean類型的變量來終止線程,這樣可使用while語句,在運行中經過改變boolean標記值來使while循環退出來達到中止線程的做用。
咱們還可使用interrupt方法來終止線程,可是,這種方法並不適用於一個正在運行的線程,它是用於中止一個受到阻塞的線程,經過使受阻塞的線程拋出異常來退出阻塞的狀態。
所以仍是使用boolean類型的變量來終止線程的方法比較好。測試

2.4 選作:6-8(CountDownLatch)實驗總結

這題就是跟着註釋一步步寫下來就能夠了。
CountDownLatch類是一個同步計數器,構造時傳入int參數,用於表示任務的個數。這個參數是計數器的初始值。
使用Executors類的newFixedThreadPool方法能夠建立一個固定線程數的線程池。線程

2.5 選作:6-9(集合同步問題)實驗總結

要想讓ArrayList等達到線程安全,只須要用synchronizedList修飾list便可。synchronizedList經過對部分操做加上synchronized來保證線程安全。

2.6 選作:較難:6-10(Callable),並回答爲何有Runnable了還須要Callable?實驗總結。

3. 互斥訪問

3.1 修改TestUnSynchronizedThread.java源代碼使其能夠同步訪問。(關鍵代碼截圖,需出現學號)

其實只要在Counter類的對id進行操做的兩個方法前加上synchronized關鍵字就能夠了,它能夠經過給共享資源上一道鎖來使在同一個時間內只容許一個線程訪問共享資源,以此來實現同步機制。

3.2 選作:進一步使用執行器改進相應代碼(關鍵代碼截圖,需出現學號)


4. 互斥訪問與同步訪問

完成題集6-4(互斥訪問)與6-5(同步訪問)

4.1 除了使用synchronized修飾方法實現互斥同步訪問,還有什麼辦法可使用synchronized實現互斥同步訪問,使用代碼說明(請出現相關代碼及學號)?

答:還能夠經過synchronized的同步代碼塊來實現互斥訪問。
修改後的方法以下:

4.2 同步代碼塊與同步方法有何區別?

答:同步方法直接在方法名前面加synchonrized關鍵字來實現對對象的加鎖,而同步代碼塊則是在方法的內部使用synchonrized關鍵字來加鎖,所以,同步方法的做用範圍比同步代碼塊大,其性能則不如同步代碼塊。

4.3 實現互斥訪問的原理是什麼?請使用對象鎖概念並結合相應的代碼塊進行說明。當程序執行synchronized同步代碼塊或者同步方法時,線程的狀態是怎麼變化的?

答:實現互斥訪的原理是程序經過給共享資源上一道鎖,讓給定時間內只容許一個線程來訪問共享資源。
下面經過分析一個從書上看到的模擬火車站售票系統的程序來講明同步機制運行時的線程狀態,代碼以下

public class ThreadSafeTest implements Runnable {
    int num = 10;       //設置當前總票數
    
    public void run() {
        while (true) {
            synchronized ("") {
                if (num > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println("tickets" + --num);
                }
            }
        }
    }
    
    public static void main(String[] args) {
        ThreadSafeTest t = new ThreadSafeTest();    //實例化類對象
        Thread tA = new Thread(t);  // 以該類對象分別實例化4個線程
        Thread tB = new Thread(t);
        Thread tC = new Thread(t);
        Thread tD = new Thread(t);
        tA.start(); // 分別啓動進程
        tB.start();
        tC.start();
        tD.start();
    }
}

當咱們運行程序的時候,一個線程先開始工做,給num上了一把鎖,而後其餘三個線程就不能再訪問這個區域,等到tA執行完售票的操做將票數減1並輸出後,對象鎖被釋放,而後下一個線程又開始工做,再給num上了一把鎖,以此類推,直到票數爲0後結束進程。

4.4 Java多線程中使用什麼關鍵字實現線程之間的通訊,進而實現線程的協同工做?

  • synchronized 關鍵字
  • wait/notify方法
  • volatile關鍵字

5. 線程間的合做:生產者消費者問題

5.1 運行MyProducerConsumerTest.java。正常運行結果應該是倉庫還剩0個貨物。多運行幾回,觀察結果,並回答:結果正常嗎?哪裏不正常?爲何?

能夠看出運行結果不正常,最後還有出現貨物有剩餘的狀況,這是由於Producer和customer的存取速度不同,致使二者不能很好的進行交互,從而使最後剩於貨物結果出現異常。

5.2 使用synchronized, wait, notify解決該問題(關鍵代碼截圖,需出現學號)




運行結果:

5.3 選作:使用Lock與Condition對象解決該問題。

運行結果:

6. 面向對象設計做業-圖書館管理系統

6.1 系統的功能模塊表格,表格中體現出每一個模塊的負責人。

學生 負責任務
陳曉菲 圖書管理模塊
廖文姑 菜單類及測試類
賈海濤 用戶管理模塊

6.2 運行視頻

6.3 講解本身負責的模塊,並粘貼本身負責模塊的關鍵代碼(出現學號及姓名)。

我負責的是圖書管理模塊,裏面包含兩個類,圖書館類和圖書類,用於進行書籍的管理。
圖書類:

//201621123031
public class Book {
    private String name;
    private long ID;
    private String category;
    
    public Book(String name,long ID,String category){
…………
    }
    /*
*get()和set()和方法
*/

圖書館類:

public class Library {
    public enum LendOrBack{ //用於變動借書還書後的書籍變化
        LEND,BACK
    }
    private static Map<Book,Integer> books=new TreeMap<Book,Integer>(); //用於存儲圖書信息及數量
    static{
        initializeBookStore();  
    }
    private static void initializeBookStore(){
        Book book1 = new Book("紅與黑",111,"文學類");
        Book book2 = new Book("平凡的世界",112,"文學類");
        Book book3 = new Book("雙城記",113,"文學類");
        Book book4 = new Book("資本論",114,"經濟類");
        Book book5 = new Book("孫子兵法",115,"軍事類");
        Book book6 = new Book("人間詞話",116,"藝術類");
        Book book7 = new Book("詩經",117,"藝術類");
        Book book8 = new Book("楚辭",118,"藝術類");
        Book book9 = new Book("戰國策",119,"歷史類");
        Book book10 = new Book("國家地理",120,"地理類");
        books.put(book1, 30);
        books.put(book2, 50);
        books.put(book3, 40);
        books.put(book4, 10);
        books.put(book5, 20);
        books.put(book6, 15);
        books.put(book7, 35);
        books.put(book8, 40);
        books.put(book9, 5);
        books.put(book10, 10);
        
    }

    public static Book peekBook(long ID) {  //根據ID獲取某本書籍的信息
        Iterator<Map.Entry<Book, Integer>> m = books.entrySet().iterator();
        while(m.hasNext()){
            Map.Entry<Book, Integer> e = m.next();
            if(e.getKey().getID()==ID&&e.getValue()>0)
                return e.getKey();
        }
        return null;
    }
    public static ArrayList<Book> peekBooks(String name) {  //根據書名獲取某本書籍的信息
………………              
    }
    
    public static void add(Book book,int num){  //添加書籍
        if(books.get(book)==null){
            books.put(book, num);
        }
        else{
            books.put(book, books.get(book)+num);
        }
        
    }
    public static void remove(Book book){   //移除書籍
        books.remove(book);
    }
    
    public static void lendOrBack(Book book,LendOrBack action){ //進行借書還書操做時書籍數量的變動
        switch(action) {
        
        case LEND:{
            books.put(book, books.get(book)-1);
            break;
        }
        case BACK:{
            books.put(book, books.get(book)+1);
            break;
        }
        
        
        }
    }

    
    public static void display(){   //展現圖書館的藏書
        Iterator<Map.Entry<Book, Integer>> m = books.entrySet().iterator();
        
        System.out.println("     書名   \t\t\tID\t\t類別\t\t數量");
        while(m.hasNext()){
            Map.Entry<Book, Integer> e = m.next();
            if(e.getValue()>0)
                System.out.println(e.getKey().toString()+"\t\t"+e.getValue());
        }
        System.out.println();
        
    }

}

7. 選作:使用其餘方法解決題目5的生產者消費者問題。

7.1 使用BlockingQueue解決生產者消費者問題關鍵代碼截圖


7.2 說明爲何不須要顯示的使用wait、notify就能夠解決同步問題。這樣解決相比較wait、notify有什麼優勢嗎?

答:BlockingQueue是阻塞隊列,當隊列中沒有數據的狀況下,消費者線程會被自動阻塞,直到有數據進入隊列,而當隊列中填滿數據的時候,生產者的全部線程都會被自動阻塞,直到隊列中有空的位置,線程纔會被自動喚醒。所以它不須要使用wait、notify就能夠達到解決同步問題的方法,這樣的方法比wait、notify更簡單且更方便。

3.碼雲及PTA

題目集:多線程

3.1. 碼雲代碼提交記錄

  • 在碼雲的項目中,依次選擇「統計-Commits歷史-設置時間段」, 而後搜索並截圖
  • 必須出現幾個要素:提交日期-用戶名(姓名與學號)-不提交說明

3.2 截圖"多線程"PTA提交列表

須要有兩張圖(1. 排名圖。2.PTA提交列表圖)


3.3 統計本週完成的代碼量

須要將每週的代碼統計狀況融合到一張表中。

周次 總代碼量 新增代碼量 總文件數 新增文件數
2 607 607 15 15
3 1642 1035 33 18
5 2044 402 42 9
6 2874 830 57 15
7 3161 287 63 6
8 4299 1138 72 9
9 4831 532 81 9
10 5475 644 93 12
11 5958 483 102 9
12 6819 861 116 14

5. 選作:學會使用Eclipse進行調試

觀看相關調試視頻

5.1 簡述使用Eclipse進行調試須要幾步?調試時F5, F6, F7快鍵鍵各有什麼不一樣?什麼狀況該使用哪一個快捷鍵?

使用eclipse調試的步驟:

  • 在須要調試的程序的左邊欄上雙擊來設置斷點
  • 點擊調試按鈕進入調試透視圖
  • 以debug方式運行程序進行調試

F5:單步執行程序,遇到方法時進入。在碰到方法內部出現問題的時候,就要用F5去深刻方法內部來進行調試。
F6:單步執行程序,遇到方法時跳過。在不用理會深層操做的時候使用F6。
F7:單步執行程序,從當前方法跳出。當進入某個方法內部想跳出來時使用F7。

5.2 實驗任務書中的題目5:使用Eclipse進行調試中的5.1,如何使用Eclipse的調試功能發現當讀取「藍山蘭」這行不會出錯的緣由?

5.3 任務書5.2,截圖證實你會使用條件斷點。

5.4 選作:調試MessageBoard.zip中的系統直至能夠正常運行。說明你是怎麼找到該系統中的錯誤?使用到了什麼調試技巧?

相關文章
相關標籤/搜索