1. 程式人生 > 其它 >java基礎知識回顧之java Thread類--java執行緒實現常見的兩種方式(一)

java基礎知識回顧之java Thread類--java執行緒實現常見的兩種方式(一)

java基礎知識回顧之java Thread類--java執行緒實現常見的兩種方式(一)

建立執行緒的第一種方式:
/ * 步驟:
* 1.繼承Thread類
* 2. 重寫Thread的run方法
* 目的:將自定義的程式碼儲存在run方法中,執行自定義執行緒
* start 1.啟動執行緒,2呼叫run方法
* 為什麼要覆蓋run方法?
* Thread類用於描述執行緒,該類定義了一個功能,用於儲存執行緒要執行的程式碼,該儲存功能就是run方法
* 也就是說Thread中的run方法,用於儲存執行緒要執行的程式碼。
* 主執行緒要執行的方法存放在main裡面,這是虛擬機器定義的
* 虛擬機器開啟主執行緒,也就是執行main方法裡面的程式碼:
*

*
*/

程式碼演示:

*發現執行結果每一次都不同,因為多個執行緒都在獲取CPU的使用權,cpu執行到誰,誰就執行,明確
* 一點,cpu在某一個時刻,只能執行一個程式(多核除外),CPU在做快速的切換,達到看上去是同時執行的
* 多執行緒的執行行為在互相的搶奪CPU的執行權。
* 這是多執行緒的一個特性:隨機性

public class HelloThread extends Thread {
    private String name;
    public HelloThread(String name){
        this.name=name;
    }
    public void run(){
        for(int i=0;i<=60;i++){
            System.out.println(name+"執行"+i);
        }
    }

    public static void main(String[]args){
        HelloThread h1 = new HelloThread("A副執行緒");//建立一個執行緒
        //HelloThread h2 = new HelloThread("B");
        //兩個執行緒併發的執行
        h1.start();//開啟執行緒,並執行該執行緒的run方法
        //h1.run();//僅僅是物件呼叫方法,是主執行緒的順序執行
        for(int i=0;i<60;i++){
            System.out.println("主執行緒執行中。。。。。。。。。。"+i);
        }
        //h2.start();
    }
}

總結:通過程式碼的測試:說明開啟執行緒是使用start方法,虛擬機器會自動呼叫run方法,等到搶到CUP使用權,執行緒從就緒狀態轉為執行狀態,而不能直接呼叫run()方法,因為在main方法裡面如果呼叫run方法,相當於jvm主執行緒執行了一個普通的方法。所以是不能實現多執行緒,只是主執行緒在順序的往下執行。

java基礎知識回顧之java Thread類--java執行緒實現常見的兩種方式實現Runnable介面(二)

建立執行緒的第二中方式:

/**
*
步驟:

1定義類實現Runnable介面
2.實現Runnable介面中的run方法。
3.通過Thread類建立執行緒物件,並將Runnable 介面的子類物件作為實際引數傳給Thread類的構造方法、
4.呼叫Thread類的start方法開啟執行緒,並呼叫Runnable介面子類的run方法
為什麼要將Runnable介面子類的物件傳遞給Thread類的構造方法?
因為執行緒的任務(要執行的程式碼)都封裝在Runnable介面的子類物件的run方法中,所以線上程物件建立的時候就必須明確要執行的任務。
*
*/

package com.lp.ecjtu.Thread;

public class HelloRunnableThread  implements Runnable{
    private String name;
    

    public String getName() {
        return name;
    }


    public void setName(String name) {
        this.name = name;
    }
    
    public HelloRunnableThread(String name){
        this.name = name;
    }

    @Override
    public void run() {
        for(int i=0;i<=100;i++){
            System.out.println(name+"執行"+i);
        }
    }
    public static void main(String[] args){
        HelloRunnableThread h1 = new HelloRunnableThread("A");
        HelloRunnableThread h2 = new HelloRunnableThread("B");
        Thread t1 = new Thread(h1);//h1為傳入執行緒構造方法的介面
        Thread t2 = new Thread(h2);
        System.out.println("執行緒啟動之前---》"+t1.isAlive());//測試執行緒是否處於活動狀態
        t1.start();//開啟執行緒,進入可執行狀態
        System.out.println("執行緒啟動之後---》"+t1.isAlive());
        t2.start();
    }
}

輸出:

執行緒啟動之前---》false
執行緒啟動之後---》true
A執行0
B執行0
B執行1
B執行2
B執行3
B執行4
B執行5
B執行6
B執行7
B執行8
B執行9
B執行10
B執行11
B執行12
B執行13
A執行1
B執行14
A執行2
B執行15

java基礎知識回顧之java Thread類學習(三)--java執行緒實現常見的兩種方式實現好處:

總結:實現Runnable介面比繼承Thread類更有優勢:

1.因為java只能單繼承,實現Runnable介面可以避免單繼承的侷限性

2.繼承Thread類,多個執行緒不能處理或者共享同一個資源,但是實現Runnable介面可以處理同一個資源。

下面我們做個測試:驗證下。車站的售票系統售票的例子,車站的各個售票口相當於各個執行緒,我們先使用第一種方法幾繼承Thread類的方式實現:

程式碼如下:

package com.lp.ecjtu.Thread;
/**
 * 
 * @author Administrator
 * 車站的售票系統售票的例子,車站的各個售票口相當於各個執行緒。
 * 
 */
public class TicketsThread  extends Thread{
    //用靜態變數存放這100張票,這樣就不會賣重複
    private  int tickets = 100;
    public void run(){
        while(true){
            if(tickets > 0){
                System.out.println(Thread.currentThread().getName()+"***sale***"+(--tickets));
            }
        }
    }
    /**
     * @param args
     */
    public static void main(String[] args) {
        // 不合理的買票程式,因為,不同執行緒都可以賣同一張票,
        //現實生活中不是這樣的,視窗1買完第99張票,視窗2不可以賣了。
        TicketsThread t1 = new TicketsThread();
        TicketsThread t2 = new TicketsThread();
        TicketsThread t3 = new TicketsThread();
        t1.start();
        t2.start();
        t3.start();
    }

}

程式碼的原理圖如下:

總結:不合理的賣票方法,不同執行緒都可以賣同一張票,現實生活中不是這樣的,視窗1賣完99號票,視窗2就不可以再賣這張票了。

怎麼解決呢?使用實現Runnable介面的方法就可以解決,程式碼如下:

public class TicketsRunnable implements Runnable {
    private int ticket=100;
    //Object obj = new Object();
    public TicketsRunnable(){
        System.out.println("*****************************");
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        
        while(true){
            //synchronized(obj){  //同步程式碼塊
                if(ticket > 0){//當執行緒0被調起的時候,當執行到這條判斷語句的時候,執行緒1被調起搶了CPU資源,執行緒0進入凍結狀態。
                    try {
                        Thread.sleep(100);//中斷當前活躍的執行緒,或者執行的執行緒
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在賣票"+ticket--);
                    //System.out.println(Thread.currentThread().getId());
                    //System.out.println(Thread.currentThread().getName());
                }
            //}
            
        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        TicketsRunnable runna = new TicketsRunnable();
        Thread t1 = new Thread(runna);
        Thread t2 = new Thread(runna);
        Thread t3 = new Thread(runna);
        t1.start();
        t2.start();
        t3.start();
    }

}

上面程式碼簡單記憶體圖理解如下:

總結:三個視窗再賣100張票,直到賣完,3個執行緒才停止。賣的票的號碼沒有重複,真正實現了現實生活中的賣票的效果。但是存在一個執行緒安全問題,通過測試執行發現,打印出了0,-1,-2等錯票,多執行緒執行出現了安全問題,這就需要引入執行緒鎖機制,下一篇部落格詳細的講解怎樣避免這一問題。