西西軟件下載最安全的下載網(wǎng)站、值得信賴的軟件下載站!

首頁(yè)編程開發(fā)java → java多線程編程小結(jié)

java多線程編程小結(jié)

相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來(lái)源:西西整理時(shí)間:2012/12/9 23:33:22字體大。A-A+

作者:西西點(diǎn)擊:0次評(píng)論:1次標(biāo)簽: 多線程

  • 類型:服務(wù)器區(qū)大。21KB語(yǔ)言:中文 評(píng)分:6.6
  • 標(biāo)簽:
立即下載

每個(gè)Java程序都有一個(gè)默認(rèn)的主線程。Java程序總是從主類的main方法開始執(zhí)行。當(dāng)JVM加載代碼,發(fā)現(xiàn)main方法后就啟動(dòng)一個(gè)線程,這個(gè)線程就稱作"主線程",該線程負(fù)責(zé)執(zhí)行main方法。在main方法中再創(chuàng)建的線程就是其他線程。

如果main方法中沒(méi)有創(chuàng)建其他線程,那么當(dāng)main方法返回時(shí)JVM就會(huì)結(jié)束Java應(yīng)用程序。但如果main方法中創(chuàng)建了其他線程,那么JVM就要在主線程和其他線程之間輪流切換,保證每個(gè)線程都有機(jī)會(huì)使用CPU資源,main方法返回(主線程結(jié)束)JVM也不會(huì)結(jié)束,要一直等到該程序所有線程全部結(jié)束才結(jié)束Java程序(另外一種情況是:程序中調(diào)用了Runtime類的exit方法,并且安全管理器允許退出操作發(fā)生。這時(shí)JVM也會(huì)結(jié)束該程序)。

線程的常用方法:
start():線程調(diào)用該方法將啟動(dòng)線程,從新建態(tài)進(jìn)入就緒隊(duì)列,一旦享用CPU資源就可以脫離創(chuàng)建它的線程,獨(dú)立開始自己的生命周期。

run():Thread類的run()方法與Runnable接口中的run()方法功能和作用相同,都用來(lái)定義線程對(duì)象被調(diào)度后所進(jìn)行的操作,都是系統(tǒng)自動(dòng)調(diào)用而用戶不得引用的方法。run()方法執(zhí)行完畢,線程就成死亡狀態(tài),即線程釋放了分配給它的內(nèi)存(死亡態(tài)線程不能再調(diào)用start()方法)。在線程沒(méi)有結(jié)束run()方法前,不能讓線程再調(diào)用start()方法,否則將發(fā)生IllegalThreadStateException異常。

sleep(int millsecond):有時(shí),優(yōu)先級(jí)高的線程需要優(yōu)先級(jí)低的線程做一些工作來(lái)配合它,此時(shí)為讓優(yōu)先級(jí)高的線程讓出CPU資源,使得優(yōu)先級(jí)低的線程有機(jī)會(huì)運(yùn)行,可以使用sleep(int millsecond)方法。線程在休眠時(shí)被打斷,JVM就拋出InterruptedException異常。因此,必須在try-catch語(yǔ)句塊中調(diào)用sleep方法。

isAlive():當(dāng)線程調(diào)用start()方法并占有CPU資源后該線程的run()方法開始運(yùn)行,在run()方法沒(méi)有結(jié)束之前調(diào)用isAlive()返回true,當(dāng)線程處于新建態(tài)或死亡態(tài)時(shí)調(diào)用isAlive()返回false。
注意:一個(gè)已經(jīng)運(yùn)行的線程在沒(méi)有進(jìn)入死亡態(tài)時(shí),不要再給它分配實(shí)體,由于線程只能引用最后分配的實(shí)體,先前的實(shí)體就成為了"垃圾",并且不能被垃圾回收機(jī)制收集。

currentThread():是Thread類的類方法,可以用類名調(diào)用,返回當(dāng)前正在使用CPU資源的線程。

interrupt():當(dāng)線程調(diào)用sleep()方法處于休眠狀態(tài),一個(gè)占有CPU資源的線程可以讓休眠的線程調(diào)用interrupt()方法"吵醒"自己,即導(dǎo)致線程發(fā)生IllegalThreadStateException異常,從而結(jié)束休眠,重新排隊(duì)等待CPU資源。


GUI線程:JVM在運(yùn)行包含圖形界面應(yīng)用程序時(shí),會(huì)自動(dòng)啟動(dòng)更多線程,其中有兩個(gè)重要的線程:AWT-EventQueue和AWT-Windows。AWT-EventQueue線程負(fù)責(zé)處理GUI事件,AWT-Windows線程負(fù)責(zé)將窗體或組件繪制到桌面。

線程同步:(用synchronized修飾某個(gè)方法,該方法修改需要同步的變量;或用volatile修飾基本變量)
當(dāng)兩個(gè)或多個(gè)線程同時(shí)訪問(wèn)一個(gè)變量,并且一個(gè)線程需要修改這個(gè)變量時(shí),應(yīng)對(duì)這樣的問(wèn)題進(jìn)行處理,否則可能發(fā)生混亂。

要處理線程同步,可以把修改數(shù)據(jù)的方法用關(guān)鍵字synchronized修飾。一個(gè)方法使用synchronized修飾,當(dāng)一個(gè)線程A使用這個(gè)方法時(shí),其他線程想使用該方法時(shí)就必須等待,直到線程A使用完該方法。所謂同步就是多個(gè)線程都需要使用一個(gè)synchronized修飾的方法。

volatile比同步簡(jiǎn)單,只適合于控制對(duì)基本變量(整數(shù)、布爾變量等)的單個(gè)實(shí)例的訪問(wèn)。java中的volatile關(guān)鍵字與C++中一樣,用volatile修飾的變量在讀寫操作時(shí)不會(huì)進(jìn)行優(yōu)化(取cache里的值以提高io速度),而是直接對(duì)主存進(jìn)行操作,這表示所有線程在任何時(shí)候看到的volatile變量值都相同。


在同步方法中使用wait()、notify()、notifyAll()方法:
當(dāng)一個(gè)線程使用的同步方法中用到某個(gè)變量,而此變量又需要其他線程修改后才能符合本線程需要,那么可以在同步方法中使用wait()方法。中斷方法的執(zhí)行,使本線程等待,暫時(shí)讓出CPU資源,并允許其他線程使用這個(gè)同步方法。其他線程如果在使用這個(gè)同步方法時(shí)不需要等待,那么它使用完這個(gè)同步方法時(shí)應(yīng)當(dāng)用notifyAll()方法通知所有由于使用這個(gè)同步方法而處于等待的線程結(jié)束等待。曾中斷的線程就會(huì)從中斷處繼續(xù)執(zhí)行,并遵循"先中斷先繼續(xù)"的原則。如果用的notify()方法,那么只是通知等待中的線程中某一個(gè)結(jié)束等待。

計(jì)時(shí)器線程Timer:(Timer還有很多高級(jí)操作,詳細(xì)見(jiàn)JDK,這里做個(gè)概述)
java.swing.Timer類用于周期性地執(zhí)行某些操作。有兩個(gè)常用構(gòu)造函數(shù)
public Timer(int delay, ActionListener listener):參數(shù)listener是計(jì)時(shí)器的監(jiān)視器,計(jì)時(shí)器發(fā)生振鈴的事件是ActionEvent類型事件,當(dāng)振鈴事件發(fā)生,監(jiān)視器會(huì)監(jiān)視到這個(gè)事件并回調(diào)ActionListener接口中的actionPerformed(ActionEvent e)方法。

public Timer(int delay):使用該構(gòu)造方法,計(jì)時(shí)器要再調(diào)用addActionListener(ActionListener listener)方法獲得監(jiān)視器。

如果想讓計(jì)時(shí)器只震動(dòng)一次,可以讓計(jì)時(shí)器調(diào)用setRepeats(boolean b)方法,參數(shù)b取false即可。
計(jì)時(shí)器還可以調(diào)用setInitialDelay(int delay)方法設(shè)置首次振鈴的延時(shí),如果沒(méi)有設(shè)置首次振鈴默認(rèn)延時(shí)為構(gòu)造函數(shù)中的參數(shù)delay。
還可以調(diào)用getDelay()和setDelay(int delay)獲取和設(shè)置延時(shí)。
計(jì)時(shí)器創(chuàng)建后調(diào)用start()啟動(dòng),調(diào)用stop()停止,即掛起,調(diào)用restart()重新啟動(dòng)計(jì)時(shí)器,即恢復(fù)線程。

java有點(diǎn)不同,實(shí)現(xiàn)多線程有兩種方式:繼承類Thread, 和 實(shí)現(xiàn)接口Runnable。

thread類有一個(gè)run函數(shù),它是線程的入口,當(dāng)啟動(dòng)一個(gè)新線程是,就從這個(gè)函數(shù)開始執(zhí)行;

public class ThreadTest  extends Thread{

    public void run()
    {
        for (int i=0;i<5;i++)
        {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
    
    public static void main(String[] args)
    {
        ThreadTest test1 = new ThreadTest();
        ThreadTest test2 = new ThreadTest();
        
        test1.start();
        test2.start();
    }
}

注意線程是調(diào)用start()來(lái)開始的。

Runnable有一點(diǎn)點(diǎn)不同,實(shí)現(xiàn)這個(gè)類的時(shí)候和Thread一樣,但是創(chuàng)建線程的時(shí)候還是放入一個(gè)Thread中創(chuàng)建:

public class RunnableTest implements Runnable
{

    private int cnt;
    
    public RunnableTest(int n)
    {
        super();
        cnt = n;
    }
    
    public void run()
    {
        for (int i=0;i<5;i++)
        {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" "+i+" "+--cnt);
        }
    }
    
    public static void main(String[] args)
    {
        RunnableTest t = new RunnableTest(10);
        Thread ta = new Thread(t);
        Thread tb = new Thread(t);
        
        ta.start();
        tb.start();
    }
}

Thread和Runnable基本用法都是差不多的,但是從上面的例子可以看出,Runnable比較方便進(jìn)程間數(shù)據(jù)的共享(公用了一個(gè)cnt計(jì)量單位),所以一樣都使用Runnable。
線程常用的幾個(gè)函數(shù):

join():  強(qiáng)制等待線程執(zhí)行完畢。 例如如果在主線程里面加一句ta.join(),那么主線程會(huì)一直等待ta執(zhí)行返回才接著執(zhí)行后面的代碼。但是在join之前已經(jīng)創(chuàng)建的其他線程,則不會(huì)受影響,繼續(xù)執(zhí)行。

interrupt(): 挺有意思的一個(gè)函數(shù),如果線程在執(zhí)行sleep()函數(shù) ,則打斷它(以讓sleep()函數(shù)拋出一個(gè)異常的方式中斷),執(zhí)行后面的語(yǔ)句。

setDaemon(true):設(shè)置為守護(hù)進(jìn)程。守護(hù)進(jìn)程的優(yōu)先級(jí)是最低的,如果一個(gè)程序只剩下守護(hù)進(jìn)程,那么它就中斷所有守護(hù)進(jìn)程而退出。 最常見(jiàn)的情況是,如果創(chuàng)建的全部是守護(hù)進(jìn)程,那么當(dāng)主函數(shù)main執(zhí)行完畢后,就立刻終止所有線程而退出。

synchronized同步符號(hào)用法: 保證同一時(shí)刻,某段代碼只有一個(gè)線程在執(zhí)行。

wait()和notify(),notifyAll():讓線程等待/喚醒。這兩個(gè)函數(shù)比較奇怪,目前我不是很熟練,要配合synchronized符號(hào)使用,請(qǐng)看下面這個(gè)生產(chǎn)者-消費(fèi)者例子(網(wǎng)上直接貼來(lái)的,寫的挺好的):

public class tt {

    public static void main(String[] args) {
        Storage s = new Storage();
        Producer p = new Producer(s);
        Consumer c = new Consumer(s);
        Thread tp = new Thread(p);
        Thread tc = new Thread(c);
        tp.start();
        tc.start();

    }
}

class Consumer implements Runnable {//消費(fèi)者    
    Storage s = null;
    public Consumer(Storage s){
        this.s = s;
    }
    public void run() {
        for(int i=0; i<20; i++){
            Product p = s.pop();//取出產(chǎn)品
            try {
                Thread.sleep((int)(Math.random()*1500));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
    }

}

class Producer implements Runnable {//生產(chǎn)者
    Storage s = null;
    
    public Producer(Storage s){
        this.s = s;
    }

    public void run() {
        for(int i=0; i<20; i++){
            Product p = new Product(i);
            s.push(p);    //放入產(chǎn)品
//            System.out.println("生產(chǎn)者放入:" + p);
            try {
                Thread.sleep((int)(Math.random()*1500));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

class Product {
    int id;
    
    public Product(int id){
        this.id = id;
    }
    
    public String toString(){//重寫toString方法
        return "產(chǎn)品:"+this.id;
    }
}

 
class Storage {
    int index = 0;
    Product[] products = new Product[5];
    
    public synchronized void push(Product p){//放入
        while(index==this.products.length){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.products[index] = p;
        System.out.println("生產(chǎn)者放入"+index+"位置:" + p);
        index++;
        this.notifyAll();
    }
    
    public synchronized Product pop(){//取出
        while(this.index==0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        index--;
        this.notifyAll();
        System.out.println("消費(fèi)者從"+ index+ "位置取出:" + this.products[index]);
        return this.products[index];
    }
}

    相關(guān)評(píng)論

    閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

    • 8 喜歡喜歡
    • 3 頂
    • 1 難過(guò)難過(guò)
    • 5 囧
    • 3 圍觀圍觀
    • 2 無(wú)聊無(wú)聊

    熱門評(píng)論

    最新評(píng)論

    發(fā)表評(píng)論 查看所有評(píng)論(1)

    昵稱:
    表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
    字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過(guò)審核才能顯示)
    推薦文章

    沒(méi)有數(shù)據(jù)