Java 多線程程序設計


課程  Java面向對象程序設計 

 

一、實驗目的

掌握多線程程序設計

二、實驗環境

1微型計算機一台 

2WINDOWS操作系統,Java SDK,Eclipse開發環境

三、實驗內容 

1、Java有兩種實現多線程的方式:通過Runnable接口、通過Thread直接實現,請掌握這兩種實現方式,並編寫示例程序。

2、多線程是並發執行的,交替占有cpu執行,請編寫示例程序,並觀察輸出結果。

3、編寫程序實現生產者消費者問題代碼,采用線程同步機制來解決多線程共享沖突問題。

四、實驗步驟和結果

  1、Java有兩種實現多線程的方式:通過Runnable接口、通過Thread直接實現,請掌握這兩種實現方式,並編寫示例程序。

 (1)通過Runnable接口實現多線程:

      定義實現java.lang.Runnable接口的類,Runnable接口中只有一個run()方法,用來定義線程運行體。代碼(MyRunner.java)如下:

package CreateThread;
import java.util.TreeMap;

public class MyRunner implements Runnable{  //實現Runnable接口
     public void run(){
         for (int i = 0; i <20; i++) {  //要在線程中執行的代碼
            System.out.println("MyRunner:"+i);
        }
     }
    public static void main(String[] args) {
        Thread thread1=new Thread(new MyRunner());
        thread1.start();
    }
}

程序執行結果截圖如下:

(2)通過Thread實現多線程:將類定義為Thread類的子類,並重寫run()方法。代碼(MyThread.java)如下

package CreateThread;
public class MyThread extends Thread {
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println("MyThread:"+i);
        }
    }
    public static void main(String[] args) {
        Thread thread2=new MyThread();
        thread2.start();
    }
}

 2、多線程是並發執行的,交替占有cpu執行,編寫示例程序如下,主線程先執行,然后啟動兩個新線程,但這兩個新線程並沒有立刻得到執行,而是統一由JVM根據時間片來調度,調度到哪個線程,就由哪個線程執行片刻。並且這兩個新線程應該是交替顯示結果,如果沒有交替顯示,可能是機器性能較好,在單位時間片內已經完成了循環運算,我們可以將循環次數更改為2000或更高值再行測試。多運行幾次,每次的輸出結果都不可能相同,說明JVM調度線程的執行順序是隨機的。

測試代碼(ConcurrentExecutionThread.java)如下所示:

package CreateThread;
public class ConcurrentExecutionThread {
    public static void main(String[] args) {
        System.out.println("主線程開始執行");
        Thread thread1=new Thread(new MyRunner());
        thread1.start();
        System.out.println("啟動一個新線程(thread1)...");
        
        Thread thread2=new MyThread();
        thread2.start();
        System.out.println("啟動另一個新線程(thread2)...");
        System.out.println("主線程執行完畢");
    }
}

程序運行結果為:

 

3、編寫程序實現生產者消費者問題代碼,采用線程同步機制來解決多線程共享沖突問題。

(1)編寫產品類

     生產者要生產產品,消費者要消費產品,所以產品要提供一個含有標識的id屬性,另外要在生產或消費時打印產品的詳細內容,所以需要重寫toString()方法,產品類(Products.java)代碼如下:

package SynchronizedThread;
//編寫產品類
public class Products {
     int id;
    public Products() {
        super();
    }
    public Products(int id) {
        super();
        this.id = id;
    }
    public String toString() {
        return "Products [id=" + id + "]";
    }
}

  (2)編寫店員類(Clerk.java)

  店員一次只能持有10份產品,如果生產者生產的產品多於10份,則會讓當前的正在此對象上操作的線程等待。一個線程訪問addProduct方法時,它已經拿到這個鎖了,當遇到產品大於10份時,它會阻塞。而且,只有鎖定對象后才可以用wait方法,否則會出錯。並且,notify與wait一般是一一對應的。

package SynchronizedThread;
//編寫店員類
public class Clerk {
    int index=0;//默認為0個產品
    Products[] pro=new Products[10];
    //生產者生產出來的產品交給店員
    public synchronized void addProduct(Products pd){
        while (index==pro.length) {
            try {
                this.wait();//產品已滿,請稍后再生產
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notify();//通知等待區的消費者可以取產品了
        pro[index]=pd;
        index++;
    }
    //消費者從店員處取產品
    public synchronized Products getProduct(){
        while (index==0) {
            try {
                this.wait();  //缺貨,請稍后再取。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notify();//通知等待區的生產者可以生產產品了
        index--;
        return pro[index];
    }
}

 (3)編寫生產者線程類(Producer.java)

    生產者與店員有關系,所以店員類被當做屬性引入進來,通過構造器完成初始化店員類對象的任務。為了凸顯效果,每生產一個產品后讓線程睡眠一會兒。

package SynchronizedThread;
//編寫生產者線程類
public class Producer implements Runnable { //生產者線程要執行的任務
    private Clerk clerk;
    public Producer() {
        super();
    }
    public Producer(Clerk clerk) {
        super();
        this.clerk = clerk;
    }
    public void run() {
        System.out.println("生產者開始生產產品");
        for (int i = 0; i <15; i++) {//注意此處的循環次數一定要大於pro數組的長度(10)
            Products pd=new Products(i);
            clerk.addProduct(pd);//生產產品
            System.out.println("生產了:"+pd);
            try {  //睡眠時間用隨機產生的值來設置
                Thread.sleep((int)(Math.random()*10)*100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

(4)編寫消費者線程類(Consumer.java)

   消費者與店員也有關系,所以店員類被當做屬性引入進來,同樣通過構造器完成初始化店員類對象的任務。

package SynchronizedThread;
//編寫消費者線程類
public class Consumer implements Runnable {//消費者線程要執行的任務
    private Clerk clerk;
    public Consumer() {
        super();
    }
    public Consumer(Clerk clerk) {
        super();
        this.clerk = clerk;
    }
    public void run() {
        System.out.println("消費者開始取走產品");
        for (int i = 0; i <15; i++) {//注意此處的循環次數一定要大於pro數組的長度(10)
        //取產品
        Products pd=clerk.getProduct();
        System.out.println("消費了:"+pd);
        try {           //睡眠時間用隨機產生的值來設置
            Thread.sleep((int)(Math.random()*10)*100);
        } catch (InterruptedException e) {
                e.printStackTrace();
        }
        }
    } 
}

 (5)編寫生產者消費者問題的測試類( ProducerAndConsumerTest.java)

     創建生產者和消費者線程,然后分別調度線程。

package SynchronizedThread;
//生產者消費者問題測試
public class ProducerAndConsumerTest {
    public static void main(String[] args) {
        Clerk clerk=new Clerk();
        Thread producerThread=new Thread(new Producer(clerk));//創建生產者線程
        Thread consumerThread=new Thread(new Consumer(clerk));//創建消費者線程
        producerThread.start();
        consumerThread.start();
    }
}

  (6)運行程序,在控制台得到的輸出結果如下所示:

五、實驗總結

  1.本次實驗按時按量完成。

  2.線程和進程的區別:

   在操作系統中能同時運行多個任務(程序)叫做多進程;在同一應用程序中多條執行路徑並發執行叫做多線程。

(1)每個進程都有獨立的代碼和數據空間(進程上下文),進程間的切換開銷大。

(2)同一進程內的多個線程共享相同的代碼和數據空間,每個線程有獨立的運行棧和程序計數器(PC),線程間的切換開銷小。

通常,在以下情況中可能要使用到多線程:

(1)程序需要同時執行兩個或多個任務。

(2)程序需要實現一些需要等待的任務時,如用戶輸入、文件讀寫操作、網絡操作、搜索等。

(3)需要一些后台運行的程序時。

  3.線程同步的方法:為了共享區域的安全,可以通過關鍵字synchronized來加保護傘,以保證數據的安全。synchronized主要應用於同步代碼塊和同步方法中。

(1) 同步方法:synchronized放在方法聲明中,表示整個方法為同步方法。

(2) 同步代碼塊:把線程體內執行的方法中會操作到共享數據的語句封裝在{}之內,然后用synchronized放在某個對象前面修飾這個代碼塊。

如果一個線程調用了synchronized修飾的方法,它就能夠保證該方法在執行完畢前不會被另一個線程打斷,這種運行機制叫作同步線程機制。

  4.在生產者線程類與消費者線程類中,設置讓線程睡眠的時間如果是一樣的,運行結果中會出現“生產一個消費一個,生產與消費時成對出現的“的這個不符合現實的現象。這時需要修改線程的睡眠時間,把睡眠時間用隨機產生的值來設置。這樣之后,再次運行程序,可以看到有時候生產了多個產品后,消費者才開始消費。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM