天天看點

Java 并發程式設計之Runnable和Thread實作多線程的差別

Java中實作多線程通常有兩種方式(其實從Java5開始有三種了,第三種先不說,ps:

Java 并發程式設計之Runnable和Thread實作多線程的差別

我暫時還沒去研究):

1.繼承Thread類

2.實作Runnable接口

雖說有兩種實作方式,但是很明顯在實際開發中實作Runnable這種方式明顯要比繼承Thread多多了,這是因為Runnable擁有天生的優勢:

1.在多線程通路同一資源的情況下,用Runnable接口建立的線程可以處理同一資源,而用Thread建立的線程則獨自處理,各自擁有自己的資源。

2.避免了Java單繼承帶來的局限性。

第二條優勢實在太明顯了,咱們這裡不談

Java 并發程式設計之Runnable和Thread實作多線程的差別

,咱們舉一個爛大街的例子來說明下第一條優勢。

首先通過繼承Thread實作,上代碼:

public class MyThread extends Thread {
    private int mTicket = 5;

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (mTicket > 0) {
                System.out.println(Thread.currentThread().getName() + "----mTicket = " + mTicket--);
            }
        }

    }
}


public class ThreadDemo {
    public static void main(String arg0[]) {
        ArrayList<Thread> threads = new ArrayList<>();

        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();
        MyThread myThread3 = new MyThread();

        threads.add(myThread1);
        threads.add(myThread2);
        threads.add(myThread3);

        for (Thread thread : threads) {
            thread.start();
        }


        try {
            for (Thread thread : threads) {
                thread.join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();

        }

        System.out.println("所有線程執行完畢!");

    }
}
           

運作結果:

Thread-0----mTicket = 5
Thread-2----mTicket = 5
Thread-2----mTicket = 4
Thread-2----mTicket = 3
Thread-2----mTicket = 2
Thread-2----mTicket = 1
Thread-1----mTicket = 5
Thread-1----mTicket = 4
Thread-0----mTicket = 4
Thread-0----mTicket = 3
Thread-0----mTicket = 2
Thread-0----mTicket = 1
Thread-1----mTicket = 3
Thread-1----mTicket = 2
Thread-1----mTicket = 1
所有線程執行完畢!
           

咱們開啟了三個線程,每個線程各自賣了5張票,它們之間互不幹擾很和諧。但是現實生活中其實并不是這樣的,火車站官網售票是需要多個線程共同完成一個任務的,繼承Thread這種方式實作不了,咱們試試實作Runnable接口這種方式,上代碼:

public class MyRunnable implements Runnable {
    private int mTicket = 5;

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (mTicket > 0) {
                System.out.println(Thread.currentThread().getName() + "----mTicket = " + mTicket--);
            }
        }
    }
}


public class ThreadDemo {
    public static void main(String arg0[]) {
        ArrayList<Thread> threads = new ArrayList<>();

        MyRunnable runnable = new MyRunnable();

        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        Thread thread3 = new Thread(runnable);


        threads.add(thread1);
        threads.add(thread2);
        threads.add(thread3);

        for (Thread thread : threads) {
            thread.start();
        }


        try {
            for (Thread thread : threads) {
                thread.join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();

        }

        System.out.println("所有線程執行完畢!");

    }
}
           

第一次運作結果:

Thread-1----mTicket = 4
Thread-1----mTicket = 3
Thread-1----mTicket = 2
Thread-1----mTicket = 1
Thread-0----mTicket = 5
所有線程執行完畢!
           

第二次運作結果:

Thread-0----mTicket = 5
Thread-0----mTicket = 3
Thread-0----mTicket = 2
Thread-0----mTicket = 1
Thread-2----mTicket = 4
Thread-1----mTicket = 5
所有線程執行完畢!
           

為何出現兩種不同的結果呢,因為3個Thread對象共同執行Runnable對象中的代碼,線程執行的時機又是難以預測的,很有可能會造成線程不安全。這時隻要加上同步鎖,確定同一時刻隻有一個線程在執行for循環裡的代碼,就可以保證線程安全了。咱們修改下MyRunnable的代碼,上代碼:

public class MyRunnable implements Runnable {
    private int mTicket = 5;

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            synchronized (this) {
                if (mTicket > 0) {
                    System.out.println(Thread.currentThread().getName() + "----mTicket = " + mTicket--);
                }
            }

        }
    }
}
           

第一次運作結果:

Thread-0----mTicket = 5
Thread-0----mTicket = 4
Thread-0----mTicket = 3
Thread-0----mTicket = 2
Thread-0----mTicket = 1
所有線程執行完畢!
           

第二次運作結果:

Thread-0----mTicket = 5
Thread-0----mTicket = 4
Thread-1----mTicket = 3
Thread-1----mTicket = 2
Thread-1----mTicket = 1
所有線程執行完畢!
           

嘿嘿,不管運作多少次,發現結果都是一樣的。