Java 多線程詳解(二)

上一篇Java 多線程詳解(一)講解了線程的一些基本概念和多線程的實現方式。接下來將講解實現多線程主要的兩種方式的區別和多線程的常用操作方法等。

一、Thread類和Runnable接口實現多線程兩種方式的區別

Thread類和Runnable接口都可以做爲同一功能的方式來實現多線程,那麼從Java的實際開發而言,肯定優先考慮使用Runnable接口,因爲可以有效的避免單繼承的侷限,除了這個,這兩種方式是否還有其他聯繫呢?

我們先來看Thread類的定義結構:

public class Thread extends Object implements Runnable

發現Thread類也是Runnable接口的子類,這樣的話,上一篇中Runnable接口實現多線程的程序的結構就有了以下形式:

1

這時所表現出來的代碼模式非常類似於代理設計模式,但是它並不是嚴格意義上代理設計模式。因爲從嚴格來講代理設計模式之中,代理主題所能夠使用的方法依然是接口中定義的run()方法,而此處代理主題調用的是start()方法,所以只能夠說形式上類似於代理設計模式,但本質上還是有差別的。

除了以上的聯繫之外,對於Runnable和Thread類還有一個區別:使用Runnable接口可以更加方便的表示出數據共享的概念。(這裏的數據共享是指多個線程訪問同一個資源的操作)

我們還是來看賣票程序:
首先,通過繼承Thread類實現多線程賣票程序:

package com.wz.threaddemo;

class MyThread extends Thread { // 線程的主體類
    private int ticket = 5; // 一共5張票

    @Override
    public void run() { // 線程的主方法
        for (int x = 0; x < 20; x++) {
            if (this.ticket > 0) {
                System.out.println(this.getName()+"賣票,ticket = " + this.ticket--);
            }
        }
    }
}

public class TestDemo {
    public static void main(String[] args) throws Exception {
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();
        MyThread mt3 = new MyThread();

        mt1.start();
        mt2.start();
        mt3.start();
    }
}

運行結果:

Thread-2賣票,ticket = 5
Thread-1賣票,ticket = 5
Thread-0賣票,ticket = 5
Thread-1賣票,ticket = 4
Thread-2賣票,ticket = 4
Thread-1賣票,ticket = 3
Thread-0賣票,ticket = 4
Thread-0賣票,ticket = 3
Thread-0賣票,ticket = 2
Thread-0賣票,ticket = 1
Thread-1賣票,ticket = 2
Thread-1賣票,ticket = 1
Thread-2賣票,ticket = 3
Thread-2賣票,ticket = 2
Thread-2賣票,ticket = 1

本程序創建了三個Thread類的對象,並且分別調用了三次start()方法啓動線程, 從程序輸出結果可見,一共買出了15張票,也就是每一個線程對象各自賣各自的5張票。
此時的內存關係圖如下:

2

然後再來看通過用Runnable接口來實現多線程賣票程序:

package com.wz.threaddemo;

class MyThread implements Runnable { // 線程的主體類
    private int ticket = 5; // 一共5張票

    @Override
    public void run() { // 線程的主方法
        for (int x = 0; x < 10; x++) {
            if (this.ticket > 0) {
                System.out.println("賣票,ticket = " + this.ticket--);
            }
        }
    }
}

public class TestDemo {
    public static void main(String[] args) throws Exception {
        MyThread mt = new MyThread();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}

運行結果:

賣票,ticket = 5
賣票,ticket = 3
賣票,ticket = 4
賣票,ticket = 1
賣票,ticket = 2

此時也屬於三個線程對象,不同的是這三個對象都直接佔用了同一個MyThread類對象,即這三個線程對象都直接訪問了同一個數據資源
以下是內存分配圖:

3

要說明的是,使用繼承Thread類實現多線程的方法也可以實現同樣的功能,程序如下:

package com.wz.threaddemo;

class MyThread extends Thread { // 線程的主體類
    private int ticket = 5; // 一共5張票

    @Override
    public void run() { // 線程的主方法
        for (int x = 0; x < 10; x++) {
            if (this.ticket > 0) {
                System.out.println("賣票,ticket = " + this.ticket--);
            }
        }
    }
}

public class TestDemo {
    public static void main(String[] args) throws Exception {
        MyThread mt = new MyThread();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}

運行結果:

賣票,ticket = 5
賣票,ticket = 3
賣票,ticket = 4
賣票,ticket = 1
賣票,ticket = 2

使用繼承Thread類實現多線程的方法也可以實現同樣的功能,把MyThread對象交給另一個Thread對象來運行,其實和Runnable對象也是類似的,啓動線程的是那個Thread對象,而MyThread只是被調用了run()方法而已。

小結:Thread類和Runnable接口實現多線程兩種方式的聯繫和區別?

聯繫:
多線程的兩種實現方式都需要一個線程的主類,這個類可以實現Runnable接口或繼承Thread類,但不管使用何種方式都必須在子類之中覆寫run()方法,此方法爲線程的主方法。

區別:
(1)Thread類是Runnable接口的子類,使用Runnable接口實現多線程可以避免單繼承侷限;
(2)Runnable接口實現的多線程可以比Thread類實現的多線程更加清楚的描述數據共享的概念。代碼能夠被多個線程共享,代碼與數據是獨立的。

二、多線程的常用操作方法

1、currentThread()方法:返回代碼段正在被哪個線程調用的信息。

public static Thread currentThread()

實例如下:

package com.wz.threaddemo;

class MyThread implements Runnable { 

    @Override
    public void run() { 
        System.out.println(Thread.currentThread());
    }
}

public class TestDemo {
    public static void main(String[] args) throws Exception {
        System.out.println(Thread.currentThread());

        MyThread mt = new MyThread();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}

運行結果:

Thread[main,5,main]
Thread[Thread-0,5,main]
Thread[Thread-1,5,main]
Thread[Thread-2,5,main]

2、setName()/getName()方法和getId()方法:

(1)setName()/getName()方法:設置/取得線程的名稱;

public final void setName(String name)
public final void setName(String name)

(2)getId()方法:取得線程的唯一標識。

public final void setName(String name)

實例如下:

package com.wz.threaddemo;

class MyThread implements Runnable { 

    @Override
    public void run() { 
        System.out.println();
    }
}

public class TestDemo {
    public static void main(String[] args) throws Exception {
        MyThread mt = new MyThread();

        Thread t1 = new Thread(mt);
        t1.setName("線程1");
        System.out.println(t1.getName() + ","+t1.getId());

        Thread t2 = new Thread(mt);
        t2.setName("線程2");
        System.out.println(t2.getName() + ","+t2.getId());

        Thread t3 = new Thread(mt);
        t3.setName("線程3");
        System.out.println(t3.getName() + ","+t3.getId());
    }
}

運行結果:

線程1,10
線程2,11
線程3,12

3、setPriority(int p)/getPriority()方法

(1)setPriority(int p)方法:設置優先級

public final void setPriority(int newPriority)

(2)getPriority()方法:獲取優先級

public final int getPriority()

優先級常量:

最高優先級:MAX_PRIORITY:

public static final int MAX_PRIORITY = 10;

默認優先級:NORM_PRIORITY

public static final int NORM_PRIORITY = 5;

最低優先級:MIN_PRIORITY

public static final int MIN_PRIORITY = 1;

4、sleep()方法:在指定的毫秒數內,讓“正在執行的線程”休眠(暫停執行)
這裏“正在執行的線程”是指

this.currentThread()

返回的線程。

public static void sleep(long millis)
                  throws InterruptedException

調用:

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

未完待續。。。

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