Java高併發編程四--線程的經常使用方法

作個筆記,java線程經常使用的方法,耐心看完.javascript

編號 方法 說明
1 public void start() 使該線程開始執行;Java 虛擬機調用該線程的 run 方法。
2 public void run() 若是該線程是使用獨立的 Runnable 運行對象構造的,則調用該 Runnable 對象的 run 方法;不然,該方法不執行任何操做並返回。
3 public final void setName(String name) 改變線程名稱,使之與參數 name 相同。
4 public final void setPriority(int priority) 更改線程的優先級。
5 public final void setDaemon(boolean on) 將該線程標記爲守護線程或用戶線程。
6 public final void join(long millisec) 等待該線程終止的時間最長爲 millis 毫秒。
7 public void interrupt() 中斷線程。
8 public final boolean isAlive() 測試線程是否處於活動狀態。
9 public static void yield() 暫停當前正在執行的線程對象,並執行其餘線程。
10 public static void sleep(long millisec) 在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行),此操做受到系統計時器和調度程序精度和準確性的影響。
11 public static Thread currentThread() 返回對當前正在執行的線程對象的引用。

 

一.靜態方法

1.currentThread()方法

currentThread()方法能夠返回代碼段正在被哪一個線程調用的信息。java

public class Run1{
    public static void main(String[] args){                 
    System.out.println(Thread.currentThread().getName());
    }
}

2.sleep()方法

方法sleep()的做用是在指定的毫秒數內讓當前「正在執行的線程」休眠(暫停執行)。這個「正在執行的線程」是指this.currentThread()返回的線程。面試

sleep方法有兩個重載版本:多線程

sleep(long millis)     //參數爲毫秒
sleep(long millis,int nanoseconds)    //第一參數爲毫秒,第二個參數爲納秒

sleep至關於讓線程睡眠,交出CPU,讓CPU去執行其餘的任務。
可是有一點要很是注意,sleep方法不會釋放鎖,也就是說若是當前線程持有對某個對象的鎖,則即便調用sleep方法,其餘線程也沒法訪問這個對象。看下面這個例子就清楚了:ide

public class Test {
 
    private int i = 10;
    private Object object = new Object();
 
    public static void main(String[] args) throws IOException  {
        Test test = new Test();
        MyThread thread1 = test.new MyThread();
        MyThread thread2 = test.new MyThread();
        thread1.start();
        thread2.start();
    } 
 
    class MyThread extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                i++;
                System.out.println("i:"+i);
                try {
                    System.out.println("線程"+Thread.currentThread().getName()+"進入睡眠狀態");
                    Thread.currentThread().sleep(10000);
                } catch (InterruptedException e) {
                    // TODO: handle exception
                }
                System.out.println("線程"+Thread.currentThread().getName()+"睡眠結束");
                i++;
                System.out.println("i:"+i);
            }
        }
    }
}

 

從上面輸出結果能夠看出,當Thread-0進入睡眠狀態以後,Thread-1並無去執行具體的任務。只有當Thread-0執行完以後,此時Thread-0釋放了對象鎖,Thread-1纔開始執行。測試

注意,若是調用了sleep方法,必須捕獲InterruptedException異常或者將該異常向上層拋出。當線程睡眠時間滿後,不必定會當即獲得執行,由於此時可能CPU正在執行其餘的任務。因此說調用sleep方法至關於讓線程進入阻塞狀態。this

3.yield()方法

調用yield方法會讓當前線程交出CPU權限,讓CPU去執行其餘的線程。它跟sleep方法相似,一樣不會釋放鎖。可是yield不能控制具體的交出CPU的時間,另外,yield方法只能讓擁有相同優先級的線程有獲取CPU執行時間的機會。spa

注意,調用yield方法並不會讓線程進入阻塞狀態,而是讓線程重回就緒狀態,它只須要等待從新獲取CPU執行時間,這一點是和sleep方法不同的。
代碼:操作系統

public class MyThread  extends Thread{
    @Override
    public void run() {
        long beginTime=System.currentTimeMillis();
        int count=0;
        for (int i=0;i<50000000;i++){
            count=count+(i+1);
            //Thread.yield();
        }
        long endTime=System.currentTimeMillis();
        System.out.println("用時:"+(endTime-beginTime)+" 毫秒!");
    }
}
 
public class Run {
    public static void main(String[] args) {
        MyThread t= new MyThread();
        t.start();
    }
}
//用時:3 毫秒!
//若是將 Thread.yield();的註釋去掉,執行結果以下:
用時:16080 毫秒!

二.對象方法

1.start()方法

start()用來啓動一個線程,當調用start方法後,系統纔會開啓一個新的線程來執行用戶定義的子任務,在這個過程當中,會爲相應的線程分配須要的資源。線程

2.run()方法

run()方法是不須要用戶來調用的,當經過start方法啓動一個線程以後,當線程得到了CPU執行時間,便進入run方法體去執行具體的任務。注意,繼承Thread類必須重寫run方法,在run方法中定義具體要執行的任務。

3.getId()

getId()的做用是取得線程的惟一標識
 

public class Test {
    public static void main(String[] args) {
        Thread t= Thread.currentThread();
        System.out.println(t.getName()+" "+t.getId());
    }
}
//main 1

4.isAlive()方法

方法isAlive()的功能是判斷當前線程是否處於活動狀態
 

public class MyThread  extends Thread{
    @Override
    public void run() {
        System.out.println("run="+this.isAlive());
    }
}
public class RunTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread=new MyThread();
        System.out.println("begin =="+myThread.isAlive());
        myThread.start();
        System.out.println("end =="+myThread.isAlive());
    }
}

//begin ==false
//run=true
//end ==false

方法isAlive()的做用是測試線程是否偶處於活動狀態。什麼是活動狀態呢?活動狀態就是線程已經啓動且還沒有終止。線程處於正在運行或準備開始運行的狀態,就認爲線程是「存活」的。
有個須要注意的地方

System.out.println("end =="+myThread.isAlive());

雖然上面的實例中打印的值是true,但此值是不肯定的。打印true值是由於myThread線程還未執行完畢,因此輸出true。若是代碼改爲下面這樣,加了個sleep休眠:

public static void main(String[] args) throws InterruptedException {
        MyThread myThread=new MyThread();
        System.out.println("begin =="+myThread.isAlive());
        myThread.start();
        Thread.sleep(1000);
        System.out.println("end =="+myThread.isAlive());
    }

則上述代碼運行的結果輸出爲false,由於mythread對象已經在1秒以內執行完畢。

5.join()方法

在不少狀況下,主線程建立並啓動了線程,若是子線程中藥進行大量耗時運算,主線程每每將早於子線程結束以前結束。這時,若是主線程想等待子線程執行完成以後再結束,好比子線程處理一個數據,主線程要取得這個數據中的值,就要用到join()方法了。方法join()的做用是等待線程對象銷燬。

public class Thread4 extends Thread{
    public Thread4(String name) {
        super(name);
    }
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(getName() + "  " + i);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        // 啓動子進程
        new Thread4("new thread").start();
        for (int i = 0; i < 10; i++) {
            if (i == 5) {
                Thread4 th = new Thread4("joined thread");
                th.start();
                th.join();
            }
            System.out.println(Thread.currentThread().getName() + "  " + i);
        }
    }
}
/*
main  0
main  1
main  2
main  3
main  4
new thread  0
new thread  1
new thread  2
new thread  3
new thread  4
joined thread  0
joined thread  1
joined thread  2
joined thread  3
joined thread  4
main  5
main  6
main  7
main  8
main  9
由上能夠看出main主線程等待joined thread線程先執行完了才結束的。若是把th.join()這行註釋掉,運行結果以下:
main  0
main  1
main  2
main  3
main  4
main  5
main  6
main  7
main  8
main  9
new thread  0
new thread  1
new thread  2
new thread  3
new thread  4
joined thread  0
joined thread  1
joined thread  2
joined thread  3
joined thread  4
*/

6.getName和setName

用來獲得或者設置線程名稱。

7.getPriority和setPriority

用來獲取和設置線程優先級。此方法不經常使用,這種方式不可靠

8.setDaemon和isDaemon

用來設置線程是否成爲守護線程和判斷線程是不是守護線程。

守護線程和用戶線程的區別在於:守護線程依賴於建立它的線程,而用戶線程則不依賴。舉個簡單的例子:若是在main線程中建立了一個守護線程,當main方法運行完畢以後,守護線程也會隨着消亡。而用戶線程則不會,用戶線程會一直運行直到其運行完畢。在JVM中,像垃圾收集器線程就是守護線程。

三.面試題

1.線程和進程有什麼區別?

    答:一個進程是一個獨立(self contained)的運行環境,它能夠被看做一個程序或者一個應用。而線程是在進程中執行的一個任務。線程是進程的子集,一個進程能夠有不少線程,每條線程並行執行不一樣的任務。不一樣的進程使用不一樣的內存空間,而全部的線程共享一片相同的內存空間。別把它和棧內存搞混,每一個線程都擁有單獨的棧內存用來存儲本地數據。

2.如何在Java中實現線程?

答:建立線程有兩種方式:
1、繼承 Thread 類,擴展線程。
2、實現 Runnable 接口。

3.啓動一個線程是調用run()仍是start()方法?

答:啓動一個線程是調用start()方法,使線程所表明的虛擬處理機處於可運行狀態,這意味着它能夠由JVM 調度並執行,這並不意味着線程就會當即運行。run()方法是線程啓動後要進行回調(callback)的方法

4.Thread類的sleep()方法和對象的wait()方法均可以讓線程暫停執行,它們有什麼區別?

答:sleep()方法(休眠)是線程類(Thread)的靜態方法,調用此方法會讓當前線程暫停執行指定的時間,將執行機會(CPU)讓給其餘線程,可是對象的鎖依然保持,所以休眠時間結束後會自動恢復。wait()是Object類的方法,調用對象的wait()方法致使當前線程放棄對象的鎖(線程暫停執行),進入對象的等待池(wait pool),只有調用對象的notify()方法(或notifyAll()方法)時才能喚醒等待池中的線程進入等鎖池(lock pool),若是線程從新得到對象的鎖就能夠進入就緒狀態。

5.線程的sleep()方法和yield()方法有什麼區別?

答:
① sleep()方法給其餘線程運行機會時不考慮線程的優先級,所以會給低優先級的線程以運行的機會;yield()方法只會給相同優先級或更高優先級的線程以運行的機會;
② 線程執行sleep()方法後轉入阻塞(blocked)狀態,而執行yield()方法後轉入就緒(ready)狀態;
③ sleep()方法聲明拋出InterruptedException,而yield()方法沒有聲明任何異常;
④ sleep()方法比yield()方法(跟操做系統CPU調度相關)具備更好的可移植性。

6.請說出與線程同步以及線程調度相關的方法。

  • wait():使一個線程處於等待(阻塞)狀態,而且釋放所持有的對象的鎖;
  • sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要處理InterruptedException異常;
  • notify():喚醒一個處於等待狀態的線程,固然在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM肯定喚醒哪一個線程,並且與優先級無關;
  • notityAll():喚醒全部處於等待狀態的線程,該方法並非將對象的鎖給全部線程,而是讓它們競爭,只有得到鎖的線程才能進入就緒狀態;

小夥伴一塊兒加油吧

相關文章
相關標籤/搜索