Thread和Runnable,你該知道這一點

因爲售票的例子博友們已經列舉太多了,主要取決於是否採用new Thread(Runnable target, String name)構造創建實例,如果不想看例子的,直接看結果分析

1.Thread和Runuable的關係

public interface Runnable {
public abstract void run();
}
public class Thread implements Runnable {

}

從這兩段代碼我們可以知道Runnable是一個接口,而Thread實現了這個接口,即Thread是Runnable的實現類。

2.Thread和Runnable的比較

當要聯網或處理耗時邏輯的時候,我們要開啓子線程,那選用那種方式好呢?Runnable具有以下優勢:
(1)java不支持多繼承,實現Runnable還可以繼承其它類,調用其功能,更具靈活性
(2)增加程序的健壯性,代碼可以被多個線程共享,代碼和數據獨立
(3)適合多個相同的程序代碼的線程去處理同一個資源(有很多博友有這樣的說法,個人存疑,因爲Thread方式也可以達到同樣目的)

還是用售票的例子吧,話不多說,上代碼:

/**
 * Time: xxx
 * Author: xxx
 * Description: 繼承Thread方式實現多線程併發售票
 */
public class TicketThread extends Thread {
    //4張票
    private int ticket = 4;

    @Override
    public void run() {
        for (int i = 0; i < 4; i++) {
            synchronized (this) {
                if (this.ticket>0) {
                    try{
                        Thread.sleep(100);
                        System.out.println(Thread.currentThread().getName()+"賣票--->"+(ticket--));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    }

    public static void main(String[] args) {
        TicketThread ticketThread = new TicketThread();
        new Thread(ticketThread, "線程1").start();
        new Thread(ticketThread, "線程2").start();
        new Thread(ticketThread, "線程3").start();

    }
}

結果如下:
線程2賣票—>3
線程3賣票—>1
線程1賣票—>2

/**
 * Time: xxx
 * Author: xxx
 * Description: 實現Runnable的方式實現多線程賣票
 */
public class TicketRunnable implements Runnable {

    private int ticket = 3;
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            synchronized (this) {
                if (this.ticket>0) {
                    try{
                        Thread.sleep(100);
                        System.out.println(Thread.currentThread().getName()+"賣票--->"+(ticket--));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        TicketRunnable ticketRunnable = new TicketRunnable();
        new Thread(ticketRunnable, "線程1").start();
        new Thread(ticketRunnable, "線程2").start();
        new Thread(ticketRunnable, "線程3").start();

    }
}

線程2賣票—>3
線程3賣票—>1
線程1賣票—>2

有個小細節要注意一下,一定要加上synchronized同步鎖,否則可能會出現其中一個線程出現賣票—>0的情況,這很好理解,如果沒加synchronized,也就是線程不安全,當其中一個線程賣最後一張票ticket=1執行ticket–的時候此時ticket已經爲0了,但是另一個線程已經執行完了if(this.ticket>0),再執行ticket–的時候就會出現賣票—>0的情況的情況。這裏就不演示了,有興趣的博友可以自己試一下,如果一次沒出現,就運行幾次,總會中獎的。

至於博友們採用以下的寫法:

public class TicketThread extends Thread {
    //3張票
    private int ticket = 3;

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
                if (this.ticket>0) {
                    try{
                        Thread.sleep(100);
                        System.out.println("ticket= "+ticket--);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
        }
    }

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

結果:
ticket= 3
ticket= 3
ticket= 3
ticket= 2
ticket= 2
ticket= 2
ticket= 1
ticket= 1
ticket= 1

3. 結果分析

並不是說Runnable支持資源的共享,而Thread不支持。
本質是:構造方法傳參的問題,是要傳參,持有Runnable的引用;還是不傳參,new新的對象
以這種方式使用Thread(不傳參)時,每一個New都會產生一個擁有自己的ticket的對象,即每個對象都有各自的ticket,各操作各的,三個ticket數值變化互不影響。就相當於new了三個對象,把方法調用了三遍。如果傳參,同樣可以達到“資源共享”的效果。在使用Runnable的整個過程中(傳參了),只產生一個能操作ticket的對象,實現多個線程共同處理同一資源(ticket),即實現資源的共享;

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