溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java多線程是什么意思

發(fā)布時(shí)間:2021-08-26 14:15:31 來(lái)源:億速云 閱讀:270 作者:chen 欄目:編程語(yǔ)言

這篇文章主要講解了“Java多線程是什么意思”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Java多線程是什么意思”吧!

多線程(multiple thread)是計(jì)算機(jī)實(shí)現(xiàn)多任務(wù)并行處理的一種方式。

在單線程情況下,計(jì)算機(jī)中存在一個(gè)控制權(quán),并按照順序依次執(zhí)行指令。單線程好像是一個(gè)只有一個(gè)隊(duì)長(zhǎng)指揮的小隊(duì),整個(gè)小隊(duì)同一個(gè)時(shí)間只能執(zhí)行一個(gè)任務(wù)。

單線程

在多線程情境下,計(jì)算機(jī)中有多個(gè)控制權(quán)。多個(gè)控制權(quán)可以同時(shí)進(jìn)行,每個(gè)控制權(quán)依次執(zhí)行一系列的指令。多線程好像是一個(gè)小隊(duì)中的成員同時(shí)執(zhí)行不同的任務(wù)。

可參考 Linux多線程與同步,并對(duì)比 Python多線程與同步

多線程

傳統(tǒng)意義上,多線程是由操作系統(tǒng)提供的功能。對(duì)于單核的CPU,硬件中只存在一個(gè)線程。在操作系統(tǒng)的控制下,CPU會(huì)在不同的任務(wù)間(線程間)切換,從而造成多任務(wù)齊頭并進(jìn)的效果。這是單CPU分時(shí)復(fù)用機(jī)制下的多線程?,F(xiàn)在,隨著新的硬件技術(shù)的發(fā)展,硬件本身開始提供多線程支持,比如多核和超線程技術(shù)。然而,硬件的多線程還是要接受操作系統(tǒng)的統(tǒng)一管理。在操作系統(tǒng)之上的多線程程序依然通用。

多個(gè)線程可以并存于同一個(gè)進(jìn)程空間。在JVM的一個(gè)進(jìn)程空間中,一個(gè)棧(stack)代表了方法調(diào)用的次序。對(duì)于多線程來(lái)說(shuō),進(jìn)程空間中需要有多個(gè)棧,以記錄不同線程的調(diào)用次序。多個(gè)?;ゲ挥绊?,但所有的線程將共享堆(heap)中的對(duì)象。

創(chuàng)建線程

Java中“一切皆對(duì)象”,線程也被封裝成一個(gè)對(duì)象。我們可以通過(guò)繼承Thread類來(lái)創(chuàng)建線程。線程類中的的run()方法包含了該線程應(yīng)該執(zhí)行的指令。我們?cè)谘苌愔懈采w該方法,以便向線程說(shuō)明要做的任務(wù):

public class Test
{    public static void main(String[] args)
    {
        NewThread thread1 = new NewThread();
        NewThread thread2 = new NewThread();
        thread1.start(); // start thread1
        thread2.start(); // start thread2    }
}/**
 * create new thread by inheriting Thread */class NewThread extends Thread {    private static int threadID = 0; // shared by all
    /**
     * constructor     */
    public NewThread() {        super("ID:" + (++threadID));
    }    /**
     * convert object to string     */
    public String toString() {        return super.getName();
    }    /**
     * what does the thread do?     */
    public void run() {
        System.out.println(this);
    }
}

(++是Java中的累加運(yùn)算符,即讓變量加1。這里++出現(xiàn)在threadID之前,說(shuō)明先將threadID加1,再對(duì)周邊的表達(dá)式求值

toString是Object根類的方法,我們通過(guò)覆蓋該方法,來(lái)將對(duì)象轉(zhuǎn)換成字符串。當(dāng)我們打印該對(duì)象時(shí),Java將自動(dòng)調(diào)用該方法。)

可以看到,Thread基類的構(gòu)建方法(super())可以接收一個(gè)字符串作為參數(shù)。該字符串是該線程的名字,并使用getName()返回。

定義類之后,我們?cè)趍ain()方法中創(chuàng)建線程對(duì)象。每個(gè)線程對(duì)象為一個(gè)線程。創(chuàng)建線程對(duì)象后,線程還沒有開始執(zhí)行。

我們調(diào)用線程對(duì)象的start()方法來(lái)啟動(dòng)線程。start()方法可以在構(gòu)造方法中調(diào)用。這樣,我們一旦使用new創(chuàng)建線程對(duì)象,就立即執(zhí)行。

Thread類還提供了下面常用方法:

join(Thread tr)   等待線程tr完成

setDaemon()       設(shè)置當(dāng)前線程為后臺(tái)daemon (進(jìn)程結(jié)束不受daemon線程的影響)

Thread類官方文檔:  http://docs.oracle.com/javase/6/docs/api/java/lang/Thread.html

Runnable

實(shí)現(xiàn)多線程的另一個(gè)方式是實(shí)施Runnable接口,并提供run()方法。實(shí)施接口的好處是容易實(shí)現(xiàn)多重繼承(multiple inheritance)。然而,由于內(nèi)部類語(yǔ)法,繼承Thread創(chuàng)建線程可以實(shí)現(xiàn)類似的功能。我們?cè)谙旅娼o出一個(gè)簡(jiǎn)單的例子,而不深入:

public class Test
{    public static void main(String[] args)
    {
        Thread thread1 = new Thread(new NewThread(), "first");
        Thread thread2 = new Thread(new NewThread(), "second");
        thread1.start(); // start thread1
        thread2.start(); // start thread2    }
}/**
 * create new thread by implementing Runnable */class NewThread implements Runnable {    /**
     * convert object to string     */
    public String toString() {        return Thread.currentThread().getName();
    }    /**
     * what does the thread do?     */
    public void run() {
        System.out.println(this);
    }
}

synchronized

多任務(wù)編程的難點(diǎn)在于多任務(wù)共享資源。對(duì)于同一個(gè)進(jìn)程空間中的多個(gè)線程來(lái)說(shuō),它們都共享堆中的對(duì)象。某個(gè)線程對(duì)對(duì)象的操作,將影響到其它的線程。

在多線程編程中,要盡力避免競(jìng)爭(zhēng)條件(racing condition),即運(yùn)行結(jié)果依賴于不同線程執(zhí)行的先后。線程是并發(fā)執(zhí)行的,無(wú)法確定線程的先后,所以我們的程序中不應(yīng)該出現(xiàn)競(jìng)爭(zhēng)條件。

然而,當(dāng)多任務(wù)共享資源時(shí),就很容易造成競(jìng)爭(zhēng)條件。我們需要將共享資源,并造成競(jìng)爭(zhēng)條件的多個(gè)線程線性化執(zhí)行,即同一時(shí)間只允許一個(gè)線程執(zhí)行。

(可更多參考 Linux多線程與同步)

下面是一個(gè)售票程序。3個(gè)售票亭(Booth)共同售賣100張票(Reservoir)。每個(gè)售票亭要先判斷是否有余票,然后再賣出一張票。如果只剩下一張票,在一個(gè)售票亭的判斷和售出兩個(gè)動(dòng)作之間,另一個(gè)售票亭賣出該票,那么第一個(gè)售票亭(由于已經(jīng)執(zhí)行過(guò)判斷)依然會(huì)齒形賣出,造成票的超賣。為了解決該問(wèn)題,判斷和售出兩個(gè)動(dòng)作之間不能有“空隙”。也就是說(shuō),在一個(gè)線程完成了這兩個(gè)動(dòng)作之后,才能有另一個(gè)線程執(zhí)行。

在Java中,我們將共享的資源置于一個(gè)對(duì)象中,比如下面r(Reservoir)對(duì)象。它包含了總共的票數(shù);將可能造成競(jìng)爭(zhēng)條件的,針對(duì)共享資源的操作,放在synchronized(同步)方法中,比如下面的sellTicket()。synchronized是方法的修飾符。在Java中,同一對(duì)象的synchronized方法只能同時(shí)被一個(gè)線程調(diào)用。其他線程必須等待該線程調(diào)用結(jié)束,(余下的線程之一)才能運(yùn)行。這樣,我們就排除了競(jìng)爭(zhēng)條件的可能。

在main()方法中,我們將共享的資源(r對(duì)象)傳遞給多個(gè)線程:

public class Test
{    public static void main(String[] args)
    {
        Reservoir r = 
new Reservoir(100);
        Booth b1 = 
new Booth(r);
        Booth b2 = 
new Booth(r);
        Booth b3 = 
new Booth(r);
    }
}/**
 * contain shared resource
 */class Reservoir {    private int total;    public Reservoir(int t) 
    {        this.total = t;
    }    /**
     * Thread safe method
     * serialized access to Booth.total     */
    public synchronized boolean sellTicket() 
    {        if(this.total > 0) {            this.total = 
this.total - 1;            return true; 
// successfully sell one        }        else {            return false; 
// no more tickets        }
    }
}/**
 * create new thread by inheriting Thread */class Booth 
extends Thread {    private static int threadID = 0; 
// owned by Class object
    private Reservoir release;      // sell this reservoir 
    private int count = 0;          // owned by this thread object
    /**
     * constructor     */
    public Booth(Reservoir r) {        super("ID:" + (++threadID));        this.release = r;          // all threads share the same reservoir
        this.start();
    }    /**
     * convert object to string     */
    public String toString() {        return super.getName();
    }    /**
     * what does the thread do?     */
    public void run() {        while(true) {            if(this.release.sellTicket()) {                this.count = 
this.count + 1;
                System.out.println(this.getName() + ": sell 1");                try {
                    sleep((int) Math.random()*100);   
// random intervals                }                catch (InterruptedException e) {                    throw new RuntimeException(e);
                }
            }            else {                break;
            }
        }
        System.out.println(this.getName() + " I sold:" + count);
    }
}

(Math.random()用于產(chǎn)生隨機(jī)數(shù))

Java的每個(gè)對(duì)象都自動(dòng)包含有一個(gè)用于支持同步的計(jì)數(shù)器,記錄synchronized方法的調(diào)用次數(shù)。線程獲得該計(jì)數(shù)器,計(jì)數(shù)器加1,并執(zhí)行synchronized方法。如果方法內(nèi)部進(jìn)一步調(diào)用了該對(duì)象的其他synchronized方法,計(jì)數(shù)器加1。當(dāng)synchronized方法調(diào)用結(jié)束并退出時(shí),計(jì)數(shù)器減1。其他線程如果也調(diào)用了同一對(duì)象的synchronized方法,必須等待該計(jì)數(shù)器變?yōu)?,才能鎖定該計(jì)數(shù)器,開始執(zhí)行。Java中的類同樣也是對(duì)象( Class類對(duì)象)。Class類對(duì)象也包含有計(jì)數(shù)器,用于同步。

關(guān)鍵代碼

上面,我們利用synchronized修飾符同步了整個(gè)方法。我們可以同步部分代碼,而不是整個(gè)方法。這樣的代碼被稱為關(guān)鍵代碼(critical section)。我們使用下面的語(yǔ)法:

synchronized (syncObj) {
  ...;
}

花括號(hào)中包含的是想要同步的代碼,syncObj是任意對(duì)象。我們將使用syncObj對(duì)象中的計(jì)數(shù)器,來(lái)同步花括號(hào)中的代碼。

感謝各位的閱讀,以上就是“Java多線程是什么意思”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Java多線程是什么意思這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI