1. 程式人生 > >Java 7之多執行緒第1篇

Java 7之多執行緒第1篇

         一個程式只有一個程序,而一個程序可以包含多個執行緒,所以程序只能是併發,而執行緒可以並行。程序是作業系統中資源分配的基本單位,同一程序的執行緒間可以共享所屬程序的資源,在執行期間,執行緒才是作業系統的排程和分派的基本單位。同時,作業系統在建立、撤銷及切換執行緒的時候,開銷會比程序小。執行緒在狀態轉換過程中,可以呼叫Java API提供的某些方法來改變執行緒執行的狀態。如下圖。


下面來介紹一下影響執行緒執行狀態的相關方法。

1、建立及啟動Java執行緒

      使用java.lang.Thread類或者java.lang.Runnable介面編寫程式碼來定義、例項化和啟動一個Java執行緒。Java中,每個執行緒都有一個呼叫棧,即使不在程式中建立任何新的執行緒,執行緒也在後臺執行著。一個Java應用總是從main()方法開始執行,mian()方法執行在一個執行緒內,它被稱為主執行緒。

(1)第一種方法:直接繼承Thread類,重寫父類的run()方法

public class MyThread extends Thread {
    //重寫父類的run()方法
    public void run() {
        this.setName("run Thread");
        for (int i = 0; i < 7; i++) {
            System.out.println(this + " " + i);
        }
    }
    public static void main(String[] args) {
        //注意,執行緒開始執行的方法是start()而不是run()方法
        new MyThread().start();    
        for (int i = 0; i < 5; i++) {
            System.out.println("Main Thread : " + i);
        }
    }

}
執行的一種結果如下:
Main Thread : 0
Thread[run Thread,5,main] 0
Main Thread : 1
Thread[run Thread,5,main] 1
Main Thread : 2
Thread[run Thread,5,main] 2
Main Thread : 3
Thread[run Thread,5,main] 3
Thread[run Thread,5,main] 4
Main Thread : 4
Thread[run Thread,5,main] 5
Thread[run Thread,5,main] 6

    可以看出:一系列執行緒以某種順序啟動並不意味著將按該順序執行。對於任何一組啟動的執行緒來說,排程程式不能保證其執行次序,持續時間也無法保證。

(2)定義執行緒類並實現Runnable介面。因為Java是單繼承了,如果繼承了Thread類就不能繼承其他的類了,而介面卻是可以實現多個的,同時這種方式還能夠為多個執行緒提供共享的資料

class  MThread implements Runnable {
    public void run() {
        //獲取當前執行緒並且設定執行緒的名字
        Thread.currentThread().setName(("The other Thread"));    
        for (int i = 0; i <10; i++) {
            System.out.println(Thread.currentThread() + " " + i);
        }
    }
}
public class MyRunnable {
    public static void main(String[] args) {
        //注意將建立自定義的執行緒類物件並傳進Thread類的構造方法中    
        new Thread(new MThread()).start();
        for (int i = 0; i < 10; i++) {
            System.out.println("Main Thread : " + i);
        }        
    }
}

執行的一種結果如下:

Main Thread : 0
Main Thread : 1
Thread[The other Thread,5,main] 0
Main Thread : 2
Thread[The other Thread,5,main] 1
Main Thread : 3
Thread[The other Thread,5,main] 2
Main Thread : 4
Thread[The other Thread,5,main] 3
Main Thread : 5
Main Thread : 6
Thread[The other Thread,5,main] 4
Thread[The other Thread,5,main] 5
Thread[The other Thread,5,main] 6
Thread[The other Thread,5,main] 7
Thread[The other Thread,5,main] 8
Thread[The other Thread,5,main] 9
Main Thread : 7
Main Thread : 8
Main Thread : 9

2、設定執行緒的優先順序getPriority()和setPriority()

 通過該方法可以設定執行緒的優先順序,一般情況下,優先順序越高的執行緒獲得CPU排程的時間片將會越長。java執行緒的優先順序值預設為5,設定範圍為1-10.

  因為Java的執行緒是被對映到系統的原執行緒上來實現的,所以執行緒排程最終還是由作業系統說了算的,雖然很多執行緒都提供執行緒優先順序的概念,但是並不剪得能與java執行緒的優先順序一一對應,如Solaries中有231種優先順序,Windows中就只有7中,並不一定能與java執行緒的優先順序設定一一對應,因此,建議還是直接使用java本身自帶的三個MIN_PRIORITY(1)、NORM_PRIORITY(5)、MAX_PRIORITY(10)來設定優先順序比較好

public class TestThread extends Thread{
    public void run() {
        Thread currentThread = Thread.currentThread();
        //輸出當前執行緒的名字、優先等級
        for (int i = 0; i < 50; i++) {
            System.out.println("name : " + currentThread.getName()
                    + "\t priopity : " + currentThread.getPriority() + " " + i);
        }
    }
    
    public static void main(String[] args) {
        //建立優先順序為5一個執行緒物件
        TestThread ttNorm = new TestThread();
        ttNorm.setPriority(NORM_PRIORITY);
        ttNorm.start();
        
        //建立優先順序為1一個執行緒物件
        TestThread ttMin = new TestThread();
        ttMin.setPriority(MIN_PRIORITY);
        ttMin.start();
        
        //建立優先順序為10一個執行緒物件
        TestThread ttMax = new TestThread();
        ttMax.setPriority(MAX_PRIORITY);
        ttMax.start();        
    }
}

執行多次後發現,優先順序高的一般會最先執行完迴圈,而最低優先順序的一般會最後執行完迴圈。

3、讓出CPU,當前執行緒進入就緒佇列等待排程yield()

public class Testyield {
    public static void main(String[] args) {
        MyThread3 t1 = new MyThread3("t1");
        MyThread3 t2 = new MyThread3("t2");
        t1.start();
        t2.start();
    }
}

class MyThread3 extends Thread {
    MyThread3(String s) {
        super(s);
    }
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(getName() + ": " + i);
            if (i % 10 == 0) {
                yield();
            }
        }
    }

}

  t1執行緒和t2執行緒輸出10個數字後就會進入等待佇列把CPU讓給了對方,所以當某個執行緒是10的倍數時,下一個執行的執行緒一定會是另外一個執行緒。所以可以從列印的結果看到,t1和t2執行緒肯定是輪流執行的。

如果註釋掉t2執行緒後,則只有t1執行緒在執行了。

4、執行緒睡眠sleep()

指定執行緒睡眠時間

 public static void main(String[] args) {
    	 Thread currentThread = Thread.currentThread();
         try {
             Thread.sleep(5000);    //讓執行緒休眠5秒鐘
         } catch (InterruptedException e) {
             e.printStackTrace();
         }   
         System.out.println("hello world");
        
    }

等待一會兒的時間,可以看到打印出hello world。為什麼說一會兒呢,可能還有其他執行緒也在執行,除非你確保自己的電腦上只有一個執行緒在執行。 sleep是執行緒類(Thread)的方法,導致此執行緒暫停執行指定時間,給執行機會給其他執行緒,但是監控狀態依然保持,到時後會自動恢復。呼叫sleep不會釋放物件鎖。即使當前執行緒使用sleep方法讓出了cpu,但其他被同步鎖擋住了的執行緒也無法得到執行,所以在同步塊中一般不使用sleep()方法。

5、判斷執行緒生死isAlive()

public class TestThread extends Thread{
    public void run() {
        //輸出當前執行緒的名字
        System.out.println("in run menthod : " + Thread.currentThread().getName());
    }
    public static void main(String[] args) {
        TestThread tt = new TestThread(); //建立一個執行緒物件

        System.out.println("執行緒還或者嗎? " + tt.isAlive()); //執行緒還沒有開啟,下面語句將會輸出false
        tt.start();        
        System.out.println("執行緒還或者嗎? " + tt.isAlive());//執行緒已經開啟,下面語句將會輸出true

    }

}

對執行緒的操作,Java是不可能直接做到的,他也是通過JNI本地呼叫進行操作

其實執行緒還可以相互影響,下面來介紹一些執行緒相互作用時用到的方法。

6、執行緒的暫停與喚醒wait()、notify()和notifyAll()

當前執行緒進入等待池(wait pool),wait方法通過引數可以指定等待的時長,如果沒有指定引數,預設一直等待直到使用notify()方法通知該執行緒或者notifyAll()方法通知才會繼續執行下去。需要注意的是,必須從同步環境內呼叫wait()、notify()、notifyAll()方法。執行緒不能呼叫物件上等待或通知的方法,除非它擁有那個物件的鎖。,否則會報以下錯誤:

Exception in thread "main" java.lang.IllegalMonitorStateException
    at java.lang.Object.wait(Native Method)
    at com.thread.second.TestWait.main(TestWait.java:23)

wait()、notify()、notifyAll()都是Object的例項方法。與每個物件具有鎖一樣,每個物件可以有一個執行緒列表,他們等待來自該訊號(通知)。執行緒通過執行物件上的wait()方法獲得這個等待列表。從那時候起,它不再執行任何其他指令,直到呼叫物件的notify()方法為止。如果多個執行緒在同一個物件上等待,則將只選擇一個執行緒(不保證以何種順序)繼續執行。如果沒有執行緒等待,則不採取任何特殊操作。

public class test2 {
	public static void main(String[] args) {
		new Thread(new Thread1()).start();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		new Thread(new Thread2()).start();
	}

	private static class Thread1 implements Runnable {
		public void run() {
			/* 
			 * 由於這裡的Thread1和下面的Thread2內部run方法要用同一物件作為監視器,這裡不能用this,
			 * 因為在Thread2裡面的this和這個Thread1的this不是同一個物件。我們用test.class這
			 * 個位元組碼物件,當前虛擬機器裡引用這個變數時,指向的都是同一個物件。
			 */
			synchronized (test2.class) {
				System.out.println("enter thread1...");
				System.out.println("thread1 is waiting");
				try {
					/*
					 *  釋放鎖有兩種方式:
					 *  1、程式自然離開監視器的範圍,也就是離開了synchronized關鍵字管轄的程式碼範圍
					 *  2、在synchronized關鍵字管轄的程式碼內部呼叫監視器物件的wait方法。這裡,使用wait方法釋放鎖
					 */
					test2.class.wait();   // 會釋放鎖,使這個執行緒處於等待狀態且以下方法不會執行
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("thread1 is going on...");
				System.out.println("thread1 is being over!");
			}
		}//end run
	}

	private static class Thread2 implements Runnable {
		public void run() {
			synchronized (test2.class) {
				System.out.println("enter thread2...");
				System.out.println("thread2 notify other thread can release wait status..");
				// 由於notify方法並不釋放鎖,
				// 即使thread2呼叫下面的sleep方法休息了10毫秒,但thread1仍然不會執行,因為thread2沒有釋放鎖,所以Thread1無法得不到鎖。
				test2.class.notify(); // 如果沒有這個,thread1剩下的不會執行
				System.out.println("thread2 is sleeping ten millisecond...");
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("thread2 is going on...");
				System.out.println("thread2 is being over!");
			}
		}
	}
}


wait是Object類的方法,對此物件呼叫wait方法導致本執行緒放棄物件鎖,進入等待此物件的等待鎖定池,只有針對此物件發出notify方法(或notifyAll)後本執行緒才進入物件鎖定池準備獲得物件鎖進入執行狀態。

在使用時注意notify()和notifyAll()方法:

    notify():喚醒一個處於等待狀態的執行緒,不包括當前執行notify()方法的執行緒。由於是執行注意的是在呼叫此方法的時候,並不能確切的喚醒某一個等待狀態的執行緒,而是由JVM確定喚醒哪個執行緒,而且不是按優先順序。 
    Allnotity():喚醒所有處入等待狀態的執行緒,注意並不是給所有喚醒執行緒一個物件的鎖,而是讓它們競爭。

最後執行的結果如下:

enter thread1...
thread1 is waiting
enter thread2...
thread2 notify other thread can release wait status..
thread2 is sleeping ten millisecond...
thread2 is going on...
thread2 is being over!
thread1 is going on...
thread1 is being over!

7、執行緒合併的join()方法

呼叫某執行緒A的這個方法,將該執行緒與當前執行緒B合併,即等待該執行緒A執行完畢後再繼續執行執行緒B,如下圖。

   

   當執行緒B呼叫執行緒A的join方法時,執行緒B必須等待執行緒A執行完畢,執行緒B才能繼續往下執行。join方法主要用來將大問題分解成小問題,當小問題計算完成時,大問題才能繼續往下執行,這時候我們就可以利用join方法了(這種設計思想在開發複雜的程式時一定要掌握)。

public class Testjoin {
	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + "  " + i);
			if (i == 5) {
				JoinThread jt = new JoinThread();
				jt.start();
				// 主執行緒呼叫jt的join方法,主執行緒必須等待jt執行緒執行完才能繼續執行
				try {
					jt.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

	}

}

class JoinThread extends Thread {
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(this.getName() + "  " + i);
		}
	}

}
執行結果如下:
main  0
main  1
main  2
main  3
main  4
main  5
Thread-0  0
Thread-0  1
Thread-0  2
Thread-0  3
Thread-0  4
Thread-0  5
Thread-0  6
Thread-0  7
Thread-0  8
Thread-0  9
main  6
main  7
main  8
main  9

8、殺死執行緒stop()

    一般不使用stop()方法,是因為它不安全。它會解除由執行緒獲取的所有鎖定,而且如果物件處於一種不連貫狀態,那麼其他執行緒能在那種狀態下檢查和修改它們。結果很難檢查出真正的問題所在。suspend()方法容易發生死鎖。呼叫suspend()的時候,目標執行緒會停下來,但卻仍然持有在這之前獲得的鎖定。此時,其他任何執行緒都不能訪問鎖定的資源,除非被"掛起"的執行緒恢復執行。對任何執行緒來說,如果它們想恢復目標執行緒,同時又試圖使用任何一個鎖定的資源,就會造成死鎖。所以不應該使用suspend(),而應在自己的Thread類中置入一個標誌,指出執行緒應該活動還是掛起。若標誌指出執行緒應該掛起,便用wait()命其進入等待狀態。若標誌指出執行緒應當恢復,則用一個notify()重新啟動執行緒。

相關推薦

Java 7執行1

         一個程式只有一個程序,而一個程序可以包含多個執行緒,所以程序只能是併發,而執行緒可以並行。程序是作業系統中資源分配的基本單位,同一程序的執行緒間可以共享所屬程序的資源,在執行期間,執行緒才是作業系統的排程和分派的基本單位。同時,作業系統在建立、撤銷及切換執行

Java 7執行5

一道面試題:假如有一個檔案可以允許多個人同時編輯,如果一個人在編輯完成後進行提交時,另外一個人已經對這個文件進行了修改,這時候就需要提醒下要提交的人,“文件已經修改,是否檢視?”最為簡單的辦法就是:其實原子類大體也是用到這樣的思想。在java.util.concurrent包

Java 7執行執行

執行緒池能夠複用執行緒,減少執行緒建立,銷燬,恢復等狀態切換的開銷,提高程式的效能。一個執行緒池管理了一組工作執行緒,同時它還包括了一個用於放置等待執行的任務的佇列。 ThreadPoolExecutor類中定義了一些與執行緒狀態與活動執行緒數相關的一些變數,如下:

Java 7執行

Java沒有一種安全的搶佔式方法來停止執行緒,只有一些協作式機制。其中一種協作機制能設定某個“已請求取消”標誌,而任務將定期檢視該標誌。如果設定了這個標誌,那麼任務將提前結束。舉例如下: public class PrimeGenerator implements Run

JAVA複習執行

java中多執行緒同步是什麼? 在多執行緒程式下,同步能控制對共享資源的訪問。如果沒有同步,當一個java縣城在修改一個共享變數時,另外一個執行緒正在使用或者更新同一個變數,這樣容易導致程式出現錯誤。 解釋實現多執行緒的幾種方法?區別是什麼? Java執行緒可以實現Runnable介面或

Java基礎執行及併發庫

實際上關於多執行緒的基礎知識,前面自己已經總結過一部分,但是每一個階段對於同樣知識點的學習側重點是不一樣的,前面的Java基礎總結八之多執行緒(一)和 Java基礎總結九之多執行緒(二)是對JDK5以前多執行緒相關基礎知識的一個簡單總結,今天本文將偏重於JDK5提供的併發庫進行學習總結。 首先,

黑馬程式設計師----Java基礎執行

------- <a href="http://www.itheima.com" target="blank">android培訓</a>、<a href="http://www.itheima.com" target="blank">java培訓</a&g

Java基礎執行-生產消費

使用場景請看上一篇部落格Java基礎之多執行緒-多生產多消費 話不多說,直接上程式碼: 顧客: package cn.itcast.day07.demo02; public class MultiConsumer implements Runnable { priva

Java基礎執行案例-單生產單消費

在講單生產單消費之前,我們先來說一下執行緒間通訊的問題 一、 執行緒間通訊 概念:多個執行緒在處理同一個資源,但是處理的動作(執行緒的任務)卻不相同。 比如:執行緒A用來生成包子的,執行緒B用來吃包子的,包子可以理解為同一資源,執行緒A與執行緒B處理的動作,一個是生產,一個是消費,

Java基礎執行原理、實現方式及匿名內部類建立執行方法

一、概念 程序:作業系統當中正在執行的一個程式。例如正在執行一個QQ。 執行緒:程序之內多工的執行單位。例如迅雷當中正在下載的多個電影。 JVM當中:棧(Stack)記憶體是執行緒獨立的,堆(Heap)記憶體是執行緒共享的。 (1)Java程式執行的時候至少有兩個執行緒: 1)主

sincerit java基礎執行

執行緒狀態 Java執行緒具有五中基本狀態 新建狀態(New):當執行緒物件對建立後,即進入了新建狀態,如:Thread t = new MyThread(); 就緒狀態(Runnable):當呼叫執行緒物件的start()方法(t.start();),執行緒即進入就緒狀態。處於就

Java基礎執行

以下是我們Java基礎多執行緒的一些知識點總結: 執行緒中run()和start()的區別:   對於Thread物件來說,當你呼叫的是start(),執行緒會被放到等待佇列,等待CPU排程,不一定馬上執行;無需等待run()方法執行完畢,可以直接執行下面的程式碼; 而呼叫的是run()的話,就是當做普通的方

java面試執行(未完待續)

1.什麼是多執行緒? 在一個應用程式中,同時,有多個不同的執行路徑。 2.執行緒和程序有什麼區別? 執行緒是程序的一條執行路徑,而程序是執行緒的集合。 3.什麼是執行緒同步、非同步? 執行緒同

Java學習執行、內部類

內部類:    定義在類中的類,成為內部類。    好處是可以直接訪問外部類中的成員(包括私有)。    外部類想訪問內部類需要建立內部類物件。    匿名內部類:就是內部類的簡化格式。        內部類必須繼承一個類或者實現介面,這種情況下的內部類可以簡化成匿名內部類。

java基礎——執行

程式碼體現: public class MyThread extends Thread { public void run() { for(int x=0; x<100; x++) { System.out.println(getName()+"--

java基礎執行的練習題

題目如下: 某公司組織年會,會議入場時有兩個入口,在入場時每位員工都能獲取一張雙色球彩票,假設公司有100個員工,利用多執行緒模擬年會入場過程,並分別統計每個入口入場的人數,以及每個員工拿到的彩票的號碼。執行緒執行後列印格式如下:編號為: 2 的員工 從後門 入場! 拿到的

Java基礎執行

        /* 這是flag=1這個執行緒 */         if (flag == 1) {             synchronized (o1) {                 /* 使用synchronized關鍵字把物件01鎖定了 */                 try {

黑馬程式設計師--Java基礎執行

------- <a href="http://www.itheima.com" target="blank">android培訓</a>、<a href="http://www.itheima.com" target="blank">j

java程式設計執行計算

/* *編寫兩個執行緒: *第一個執行緒計算2-1000000之間的質數及個數 *第二個執行緒計算1000000-2000000之間的質數 及個數 */ class myThread extends Thread{ private int a,b; //

黑馬程式設計師——java基礎執行

---------------------- ASP.Net+Android+IOS開發、.Net培訓、期待與您交流! ---------------------- 1. Java 多執行緒程式設計     Java 語言的優勢之一就是執行緒處理較為簡單。     一般