java多線程機制
⑴ 在java 中多線程的實現方法有哪些,如何使用
Java多線程的創建及啟動
Java中線程的創建常見有如三種基本形式
1.繼承Thread類,重寫該類的run()方法。
復制代碼
1 class MyThread extends Thread {
2
3 private int i = 0;
4
5 @Override
6 public void run() {
7 for (i = 0; i < 100; i++) {
8 System.out.println(Thread.currentThread().getName() + " " + i);
9 }
10 }
11 }
復制代碼
復制代碼
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4 for (int i = 0; i < 100; i++) {
5 System.out.println(Thread.currentThread().getName() + " " + i);
6 if (i == 30) {
7 Thread myThread1 = new MyThread(); // 創建一個新的線程 myThread1 此線程進入新建狀態
8 Thread myThread2 = new MyThread(); // 創建一個新的線程 myThread2 此線程進入新建狀態
9 myThread1.start(); // 調用start()方法使得線程進入就緒狀態
10 myThread2.start(); // 調用start()方法使得線程進入就緒狀態
11 }
12 }
13 }
14 }
復制代碼
如上所示,繼承Thread類,通過重寫run()方法定義了一個新的線程類MyThread,其中run()方法的方法體代表了線程需要完成的任務,稱之為線程執行體。當創建此線程類對象時一個新的線程得以創建,並進入到線程新建狀態。通過調用線程對象引用的start()方法,使得該線程進入到就緒狀態,此時此線程並不一定會馬上得以執行,這取決於CPU調度時機。
2.實現Runnable介面,並重寫該介面的run()方法,該run()方法同樣是線程執行體,創建Runnable實現類的實例,並以此實例作為Thread類的target來創建Thread對象,該Thread對象才是真正的線程對象。
復制代碼
1 class MyRunnable implements Runnable {
2 private int i = 0;
3
4 @Override
5 public void run() {
6 for (i = 0; i < 100; i++) {
7 System.out.println(Thread.currentThread().getName() + " " + i);
8 }
9 }
10 }
復制代碼
復制代碼
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4 for (int i = 0; i < 100; i++) {
5 System.out.println(Thread.currentThread().getName() + " " + i);
6 if (i == 30) {
7 Runnable myRunnable = new MyRunnable(); // 創建一個Runnable實現類的對象
8 Thread thread1 = new Thread(myRunnable); // 將myRunnable作為Thread target創建新的線程
9 Thread thread2 = new Thread(myRunnable);
10 thread1.start(); // 調用start()方法使得線程進入就緒狀態
11 thread2.start();
12 }
13 }
14 }
15 }
復制代碼
相信以上兩種創建新線程的方式大家都很熟悉了,那麼Thread和Runnable之間到底是什麼關系呢?我們首先來看一下下面這個例子。
復制代碼
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4 for (int i = 0; i < 100; i++) {
5 System.out.println(Thread.currentThread().getName() + " " + i);
6 if (i == 30) {
7 Runnable myRunnable = new MyRunnable();
8 Thread thread = new MyThread(myRunnable);
9 thread.start();
10 }
11 }
12 }
13 }
14
15 class MyRunnable implements Runnable {
16 private int i = 0;
17
18 @Override
19 public void run() {
20 System.out.println("in MyRunnable run");
21 for (i = 0; i < 100; i++) {
22 System.out.println(Thread.currentThread().getName() + " " + i);
23 }
24 }
25 }
26
27 class MyThread extends Thread {
28
29 private int i = 0;
30
31 public MyThread(Runnable runnable){
32 super(runnable);
33 }
34
35 @Override
36 public void run() {
37 System.out.println("in MyThread run");
38 for (i = 0; i < 100; i++) {
39 System.out.println(Thread.currentThread().getName() + " " + i);
40 }
41 }
42 }
復制代碼
同樣的,與實現Runnable介面創建線程方式相似,不同的地方在於
1 Thread thread = new MyThread(myRunnable);
那麼這種方式可以順利創建出一個新的線程么?答案是肯定的。至於此時的線程執行體到底是MyRunnable介面中的run()方法還是MyThread類中的run()方法呢?通過輸出我們知道線程執行體是MyThread類中的run()方法。其實原因很簡單,因為Thread類本身也是實現了Runnable介面,而run()方法最先是在Runnable介面中定義的方法。
1 public interface Runnable {
2
3 public abstract void run();
4
5 }
我們看一下Thread類中對Runnable介面中run()方法的實現:
復制代碼
@Override
public void run() {
if (target != null) {
target.run();
}
}
復制代碼
也就是說,當執行到Thread類中的run()方法時,會首先判斷target是否存在,存在則執行target中的run()方法,也就是實現了Runnable介面並重寫了run()方法的類中的run()方法。但是上述給到的列子中,由於多態的存在,根本就沒有執行到Thread類中的run()方法,而是直接先執行了運行時類型即MyThread類中的run()方法。
3.使用Callable和Future介面創建線程。具體是創建Callable介面的實現類,並實現clall()方法。並使用FutureTask類來包裝Callable實現類的對象,且以此FutureTask對象作為Thread對象的target來創建線程。
看著好像有點復雜,直接來看一個例子就清晰了。
復制代碼
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4
5 Callable<Integer> myCallable = new MyCallable(); // 創建MyCallable對象
6 FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask來包裝MyCallable對象
7
8 for (int i = 0; i < 100; i++) {
9 System.out.println(Thread.currentThread().getName() + " " + i);
10 if (i == 30) {
11 Thread thread = new Thread(ft); //FutureTask對象作為Thread對象的target創建新的線程
12 thread.start(); //線程進入到就緒狀態
13 }
14 }
15
16 System.out.println("主線程for循環執行完畢..");
17
18 try {
19 int sum = ft.get(); //取得新創建的新線程中的call()方法返回的結果
20 System.out.println("sum = " + sum);
21 } catch (InterruptedException e) {
22 e.printStackTrace();
23 } catch (ExecutionException e) {
24 e.printStackTrace();
25 }
26
27 }
28 }
29
30
31 class MyCallable implements Callable<Integer> {
32 private int i = 0;
33
34 // 與run()方法不同的是,call()方法具有返回值
35 @Override
36 public Integer call() {
37 int sum = 0;
38 for (; i < 100; i++) {
39 System.out.println(Thread.currentThread().getName() + " " + i);
40 sum += i;
41 }
42 return sum;
43 }
44
45 }
復制代碼
首先,我們發現,在實現Callable介面中,此時不再是run()方法了,而是call()方法,此call()方法作為線程執行體,同時還具有返回值!在創建新的線程時,是通過FutureTask來包裝MyCallable對象,同時作為了Thread對象的target。那麼看下FutureTask類的定義:
1 public class FutureTask<V> implements RunnableFuture<V> {
2
3 //....
4
5 }
1 public interface RunnableFuture<V> extends Runnable, Future<V> {
2
3 void run();
4
5 }
於是,我們發現FutureTask類實際上是同時實現了Runnable和Future介面,由此才使得其具有Future和Runnable雙重特性。通過Runnable特性,可以作為Thread對象的target,而Future特性,使得其可以取得新創建線程中的call()方法的返回值。
執行下此程序,我們發現sum = 4950永遠都是最後輸出的。而「主線程for循環執行完畢..」則很可能是在子線程循環中間輸出。由CPU的線程調度機制,我們知道,「主線程for循環執行完畢..」的輸出時機是沒有任何問題的,那麼為什麼sum =4950會永遠最後輸出呢?
原因在於通過ft.get()方法獲取子線程call()方法的返回值時,當子線程此方法還未執行完畢,ft.get()方法會一直阻塞,直到call()方法執行完畢才能取到返回值。
上述主要講解了三種常見的線程創建方式,對於線程的啟動而言,都是調用線程對象的start()方法,需要特別注意的是:不能對同一線程對象兩次調用start()方法。
你好,本題已解答,如果滿意
請點右下角「採納答案」。
⑵ Java支持多線程機制 對嗎
對的,沒有任何問題,java中實現多線程可以繼承thread類和實現runnable方法。
⑶ java 多線程是什麼
進程是程序在處理機中的一次運行。一個進程既包括其所要執行的指令,也包括了執行指令所需的系統資源,不同進程所佔用的系統資源相對獨立。所以進程是重量級的任務,它們之間的通信和轉換都需要操作系統付出較大的開銷。
線程是進程中的一個實體,是被系統獨立調度和分派的基本單位。線程自己基本上不擁有系統資源,但它可以與同屬一個進程的其他線程共享進程所擁有的全部資源。所以線程是輕量級的任務,它們之間的通信和轉換只需要較小的系統開銷。
Java支持多線程編程,因此用Java編寫的應用程序可以同時執行多個任務。Java的多線程機制使用起來非常方便,用戶只需關注程序細節的實現,而不用擔心後台的多任務系統。
Java語言里,線程表現為線程類。Thread線程類封裝了所有需要的線程操作控制。在設計程序時,必須很清晰地區分開線程對象和運行線程,可以將線程對象看作是運行線程的控制面板。在線程對象里有很多方法來控制一個線程是否運行,睡眠,掛起或停止。線程類是控制線程行為的唯一的手段。一旦一個Java程序啟動後,就已經有一個線程在運行。可通過調用Thread.currentThread方法來查看當前運行的是哪一個線程。
⑷ 什麼是Java多線程編程
一、 什麼是多線程:
我們現在所使用操作系統都是多任務操作系統(早期使用的DOS操作系統為單任務操作系統),多任務操作指在同一時刻可以同時做多件事(可以同時執行多個程序)。
多進程:每個程序都是一個進程,在操作系統中可以同時執行多個程序,多進程的目的是為了有效的使用CPU資源,每開一個進程系統要為該進程分配相關的系統資源(內存資源)
多線程:線程是進程內部比進程更小的執行單元(執行流|程序片段),每個線程完成一個任務,每個進程內部包含了多個線程每個線程做自己的事情,在進程中的所有線程共享該進程的資源;
主線程:在進程中至少存在一個主線程,其他子線程都由主線程開啟,主線程不一定在其他線程結束後結束,有可能在其他線程結束前結束。Java中的主線程是main線程,是Java的main函數;
繼承Thread類來實現多線程:
二、 Java中實現多線程的方式:
當我們自定義的類繼承Thread類後,該類就為一個線程類,該類為一個獨立的執行單元,線程代碼必須編寫在run()方法中,run方法是由Thread類定義,我們自己寫的線程類必須重寫run方法。
run方法中定義的代碼為線程代碼,但run方法不能直接調用,如果直接調用並沒有開啟新的線程而是將run方法交給調用的線程執行
要開啟新的線程需要調用Thread類的start()方法,該方法自動開啟一個新的線程並自動執行run方法中的內容
java多線程的啟動順序不一定是線程執行的順序,各個線程之間是搶佔CPU資源執行的,所有有可能出現與啟動順序不一致的情況。
CPU的調用策略:
如何使用CPU資源是由操作系統來決定的,但操作系統只能決定CPU的使用策略不能控制實際獲得CPU執行權的程序。
線程執行有兩種方式:
1.搶占式:
目前PC機中使用最多的一種方式,線程搶佔CPU的執行權,當一個線程搶到CPU的資源後並不是一直執行到此線程執行結束,而是執行一個時間片後讓出CPU資源,此時同其他線程再次搶佔CPU資源獲得執行權。
2.輪循式;
每個線程執行固定的時間片後讓出CPU資源,以此循環執行每個線程執行相同的時間片後讓出CPU資源交給下一個線程執行。
希望對您有所幫助!~
⑸ java多線程開發的同步機制有哪些
一段synchronized的代碼被一個線程執行之前,他要先拿到執行這段代碼的許可權,在 java里邊就是拿到某個同步對象的鎖(一個對象只有一把鎖); 如果這個時候同步對象的鎖被其他線程拿走了,他(這個線程)就只能等了(線程阻塞在鎖池 等待隊列中)。 取到鎖後,他就開始執行同步代碼(被synchronized修飾的代碼);線程執行完同步代碼後馬上就把鎖還給同步對象,其他在鎖池中 等待的某個線程就可以拿到鎖執行同步代碼了。這樣就保證了同步代碼在統一時刻只有一個線程在執行。
眾所周知,在Java多線程編程中,一個非常重要的方面就是線程的同步問題。
關於線程的同步,一般有以下解決方法:
1. 在需要同步的方法的方法簽名中加入synchronized關鍵字。
2. 使用synchronized塊對需要進行同步的代碼段進行同步。
3. 使用JDK 5中提供的java.util.concurrent.lock包中的Lock對象。
另外,為了解決多個線程對同一變數進行訪問時可能發生的安全性問題,我們不僅可以採用同步機制,更可以通過JDK 1.2中加入的ThreadLocal來保證更好的並發性。
⑹ java多線程運行機制
首先子線程不是獨立的空間啊!要不也有同步鎖了!
java中運行多線程時是一會運行這個線程,一會運行那個線程……隨機運行的。因為運行的速度快時間短,所以就給人感覺線程是同時運行的。
只要出錯了,java程序立馬停止運行!
⑺ Java多線程編程
作者 natrium 一 理解多線程多線程是這樣一種機制 它允許在程序中並發執行多個指令流 每個指令流都稱為一個線程 彼此間互相獨立 線程又稱為輕量級進程 它和進程一樣擁有獨立的執行控制 由操作系統負責調度 區別在於線程沒有獨立的存儲空間 而是和所屬進程中的其它線程共享一個存儲空間 這使得線程間的通信遠較進程簡單 多個線程的執行是並發的 也就是在邏輯上 同時 而不管是否是物理上的 同時 如果系統只有一個CPU 那麼真正的 同時 是不可能的 但是由於CPU的速度非常快 用戶感覺不到其中的區別 因此我們也不用關心它 只需要設想各個線程是同時執行即可 多線程和傳統的單線程在程序設計上最大的區別在於 由於各個線程的控制流彼此獨立 使得各個線程之間的代碼是亂序執行的 由此帶來的線程調度 同步等問題 將在以後探討 二 在Java中實現多線程我們不妨設想 為了創建一個新的線程 我們需要做些什麼?很顯然 我們必須指明這個線程所要執行的代碼 而這就是在Java中實現多線程我們所需要做的一切!真是神奇!Java是如何做到這一點的?通過類!作為一個完全面向對象的語言 Java提供了類 java lang Thread 來方便多線程編程 這個類提供了大量的方法來方便我們控制自己的各個線程 我們以後的討論都將圍繞這個類進行 那麼如何提供給 Java 我們要線程執行的代碼呢?讓我們來看一看 Thread 類 Thread 類最重要的方法是 run() 它為Thread 類的方法 start() 所調用 提供我們的線程所要執行的代碼 為了指定我們自己的代碼 只需要覆蓋它!方法一 繼承 Thread 類 覆蓋方法 run() 我們在創建的 Thread 類的子類中重寫 run() 加入線程所要執行的代碼即可 下面是一個例子 public class MyThread extends Thread {int count= number;public MyThread(int num) {number = num;System out println( 創建線程 + number);}public void run() {while(true) {System out println( 線程 + number + :計數 + count);if(++count== ) return;}}public static void main(String args[]) {for(int i = ; i < 5; i++) new MyThread(i+1).start();}}這種方法簡單明了,符合大家的習慣,但是,它也有一個很大的缺點,那就是如果我們的類已經從一個類繼承(如小程序必須繼承自 Applet 類),則無法再繼承 Thread 類,這時如果我們又不想建立一個新的類,應該怎麼辦呢?我們不妨來探索一種新的方法:我們不創建 Thread 類的子類,而是直接使用它,那麼我們只能將我們的方法作為參數傳遞給 Thread 類的實例,有點類似回調函數。.WINgWIT.但是 Java 沒有指針,我們只能傳遞一個包含這個方法的類的實例。那麼如何限制這個類必須包含這一方法呢?當然是使用介面!(雖然抽象類也可滿足,但是需要繼承,而我們之所以要採用這種新方法,不就是為了避免繼承帶來的限制嗎?)Java 提供了介面 java.lang.Runnable 來支持這種方法。方法二:實現 Runnable 介面Runnable 介面只有一個方法 run(),我們聲明自己的類實現 Runnable 介面並提供這一方法,將我們的線程代碼寫入其中,就完成了這一部分的任務。但是 Runnable 介面並沒有任何對線程的支持,我們還必須創建 Thread 類的實例,這一點通過 Thread 類的構造函數public Thread(Runnable target);來實現。下面是一個例子:public class MyThread implements Runnable {int count= 1, number;public MyThread(int num) {number = num;System.out.println("創建線程 " + number);}public void run() {while(true) {System.out.println("線程 " + number + ":計數 " + count);if(++count== 6) return;} }public static void main(String args[]) {for(int i = 0; i < 5; i++) new Thread(new MyThread(i+1)).start();}}嚴格地說,創建 Thread 子類的實例也是可行的,但是必須注意的是,該子類必須沒有覆蓋 Thread 類的 run 方法,否則該線程執行的將是子類的 run 方法,而不是我們用以實現Runnable 介面的類的 run 方法,對此大家不妨試驗一下。使用 Runnable 介面來實現多線程使得我們能夠在一個類中包容所有的代碼,有利於封裝,它的缺點在於,我們只能使用一套代碼,若想創建多個線程並使各個線程執行不同的代碼,則仍必須額外創建類,如果這樣的話,在大多數情況下也許還不如直接用多個類分別繼承 Thread 來得緊湊。綜上所述,兩種方法各有千秋,大家可以靈活運用。下面讓我們一起來研究一下多線程使用中的一些問題。三:線程的四種狀態1. 新狀態:線程已被創建但尚未執行(start() 尚未被調用)。2. 可執行狀態:線程可以執行,雖然不一定正在執行。CPU 時間隨時可能被分配給該線程,從而使得它執行。3. 死亡狀態:正常情況下 run() 返回使得線程死亡。調用 stop()或 destroy() 亦有同樣效果,但是不被推薦,前者會產生異常,後者是強制終止,不會釋放鎖。4. 阻塞狀態:線程不會被分配 CPU 時間,無法執行。四:線程的優先順序 線程的優先順序代表該線程的重要程度,當有多個線程同時處於可執行狀態並等待獲得 CPU 時間時,線程調度系統根據各個線程的優先順序來決定給誰分配 CPU 時間,優先順序高的線程有更大的機會獲得 CPU 時間,優先順序低的線程也不是沒有機會,只是機會要小一些罷了。你可以調用 Thread 類的方法 getPriority() 和 setPriority()來存取線程的優先順序,線程的優先順序界於1(MIN_PRIORITY)和10(MAX_PRIORITY)之間,預設是5(NORM_PRIORITY)。五:線程的同步由於同一進程的多個線程共享同一片存儲空間,在帶來方便的同時,也帶來了訪問沖突這個嚴重的問題。Java語言提供了專門機制以解決這種沖突,有效避免了同一個數據對象被多個線程同時訪問。由於我們可以通過 private 關鍵字來保證數據對象只能被方法訪問,所以我們只需針對方法提出一套機制,這套機制就是 synchronized 關鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊。1. synchronized 方法:通過在方法聲明中加入 synchronized關鍵字來聲明 synchronized 方法。如:public synchronized void accessVal(int newVal);synchronized 方法控制對類成員變數的訪問:每個類實例對應一把鎖,每個 synchronized 方法都必須獲得調用該方法的類實例的鎖方能執行,否則所屬線程阻塞,方法一旦執行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此後被阻塞的線程方能獲得該鎖,重新進入可執行狀態。這種機制確保了同一時刻對於每一個類實例,其所有聲明為 synchronized 的成員函數中至多隻有一個處於可執行狀態(因為至多隻有一個能夠獲得該類實例對應的鎖),從而有效避免了類成員變數的訪問沖突(只要所有可能訪問類成員變數的方法均被聲明為 synchronized)。在 Java 中,不光是類實例,每一個類也對應一把鎖,這樣我們也可將類的靜態成員函數聲明為 synchronized ,以控制其對類的靜態成員變數的訪問。synchronized 方法的缺陷:若將一個大的方法聲明為synchronized 將會大大影響效率,典型地,若將線程類的方法 run() 聲明為 synchronized ,由於在線程的整個生命期內它一直在運行,因此將導致它對本類任何 synchronized 方法的調用都永遠不會成功。當然我們可以通過將訪問類成員變數的代碼放到專門的方法中,將其聲明為 synchronized ,並在主方法中調用來解決這一問題,但是 Java 為我們提供了更好的解決辦法,那就是 synchronized 塊。2. synchronized 塊:通過 synchronized關鍵字來聲明synchronized 塊。語法如下: synchronized(syncObject) {//允許訪問控制的代碼}synchronized 塊是這樣一個代碼塊,其中的代碼必須獲得對象 syncObject (如前所述,可以是類實例或類)的鎖方能執行,具體機制同前所述。由於可以針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。六:線程的阻塞為了解決對共享存儲區的訪問沖突,Java 引入了同步機制,現在讓我們來考察多個線程對共享資源的訪問,顯然同步機制已經不夠了,因為在任意時刻所要求的資源不一定已經准備好了被訪問,反過來,同一時刻准備好了的資源也可能不止一個。為了解決這種情況下的訪問控制問題,Java 引入了對阻塞機制的支持。阻塞指的是暫停一個線程的執行以等待某個條件發生(如某資源就緒),學過操作系統的同學對它一定已經很熟悉了。Java 提供了大量方法來支持阻塞,下面讓我們逐一分析。1. sleep() 方法:sleep() 允許 指定以毫秒為單位的一段時間作為參數,它使得線程在指定的時間內進入阻塞狀態,不能得到CPU 時間,指定的時間一過,線程重新進入可執行狀態。典型地,sleep() 被用在等待某個資源就緒的情形:測試發現條件不滿足後,讓線程阻塞一段時間後重新測試,直到條件滿足為止。2. suspend() 和 resume() 方法:兩個方法配套使用,suspend()使得線程進入阻塞狀態,並且不會自動恢復,必須其對應的resume() 被調用,才能使得線程重新進入可執行狀態。典型地,suspend() 和 resume() 被用在等待另一個線程產生的結果的情形:測試發現結果還沒有產生後,讓線程阻塞,另一個線程產生了結果後 lishixin/Article/program/Java/gj/201311/27622
⑻ java多線程機制中線程間可以共享相同的內存單元對還是錯
java多線程機制中線程間可以共享相同的內存單元是對的。根據查詢相關公開信息顯示,同一進程的多個線程間可以共享相同的內存單元,並可利用這些共享單元來實現數據交換、實時通信和必要的同步操作。
⑼ java 多線程是什麼一個處理器怎麼同時處理多個程序
進程是程序在處理機中的一次運行。一個進程既包括其所要執行的指令,也包括了執行指令所需的系統資源,不同進程所佔用的系統資源相對獨立。所以進程是重量級的任務,它們之間的通信和轉換都需要操作系統付出較大的開銷。
線程是進程中的一個實體,是被系統獨立調度和分派的基本單位。線程自己基本上不擁有系統資源,但它可以與同屬一個進程的其他線程共享進程所擁有的全部資源。所以線程是輕量級的任務,它們之間的通信和轉換只需要較小的系統開銷。
Java支持多線程編程,因此用Java編寫的應用程序可以同時執行多個任務。Java的多線程機制使用起來非常方便,用戶只需關注程序細節的實現,而不用擔心後台的多任務系統。
Java語言里,線程表現為線程類。Thread線程類封裝了所有需要的線程操作控制。在設計程序時,必須很清晰地區分開線程對象和運行線程,可以將線程對象看作是運行線程的控制面板。在線程對象里有很多方法來控制一個線程是否運行,睡眠,掛起或停止。線程類是控制線程行為的唯一的手段。一旦一個Java程序啟動後,就已經有一個線程在運行。可通過調用Thread.currentThread方法來查看當前運行的是哪一個線程。
class ThreadTest{
public static void main(String args[]){
Thread t = Thread.currentThread();
t.setName("單線程"); //對線程取名為"單線程"
t.setPriority(8);
//設置線程優先順序為8,最高為10,最低為1,默認為5
System.out.println("The running thread: " + t);
// 顯示線程信息
try{
for(int i=0;i<3;i++){
System.out.println("Sleep time " + i);
Thread.sleep(100); // 睡眠100毫秒
}
}catch(InterruptedException e){// 捕獲異常
System.out.println("thread has wrong");
}
}
}
多線程的實現方法
繼承Thread類
可通過繼承Thread類並重寫其中的run()方法來定義線程體以實現線程的具體行為,然後創建該子類的對象以創建線程。
在繼承Thread類的子類ThreadSubclassName中重寫run()方法來定義線程體的一般格式為:
public class ThreadSubclassName extends Thread{
public ThreadSubclassName(){
..... // 編寫子類的構造方法,可預設
}
public void run(){
..... // 編寫自己的線程代碼
}
}
用定義的線程子類ThreadSubclassName創建線程對象的一般格式為:
ThreadSubclassName ThreadObject =
new ThreadSubclassName();
然後,就可啟動該線程對象表示的線程:
ThreadObject.start(); //啟動線程
應用繼承類Thread的方法實現多線程的程序。本程序創建了三個單獨的線程,它們分別列印自己的「Hello World!」。
class ThreadDemo extends Thread{
private String whoami;
private int delay;
public ThreadDemo(String s,int d){
whoami=s;
delay=d;
}
public void run(){
try{
sleep(delay);
}catch(InterruptedException e){ }
System.out.println("Hello World!" + whoami
+ " " + delay);
}
}
public class MultiThread{
public static void main(String args[]){
ThreadDemo t1,t2,t3;
t1 = new ThreadDemo("Thread1",
(int)(Math.random()*2000));
t2 = new ThreadDemo("Thread2",
(int)(Math.random()*2000));
t3 = new ThreadDemo("Thread3",
(int)(Math.random()*2000));
t1.start();
t2.start();
t3.start();
}
}
實現Runnable介面
編寫多線程程序的另一種的方法是實現Runnable介面。在一個類中實現Runnable介面(以後稱實現Runnable介面的類為Runnable類),並在該類中定義run()方法,然後用帶有Runnable參數的Thread類構造方法創建線程。
創建線程對象可用下面的兩個步驟來完成:
(1)生成Runnable類ClassName的對象
ClassName RunnableObject = new ClassName();
(2)用帶有Runnable參數的Thread類構造方法創建線程對象。新創建的線程的指針將指向Runnable類的實例。用該Runnable類的實例為線程提供 run()方法---線程體。
Thread ThreadObject = new Thread(RunnableObject);
然後,就可啟動線程對象ThreadObject表示的線程:
ThreadObject.start();
在Thread類中帶有Runnable介面的構造方法有:
public Thread(Runnable target);
public Thread(Runnable target, String name);
public Thread(String name);
public Thread(ThreadGroup group,Runnable target);
public Thread(ThreadGroup group,Runnable target,
String name);
其中,參數Runnable target表示該線程執行時運行target的run()方法,String name以指定名字構造線程,ThreadGroup group表示創建線程組。
用Runnable介面實現的多線程。
class TwoThread implements Runnable{
TwoThread(){
Thread t1 = Thread.currentThread();
t1.setName("第一主線程");
System.out.println("正在運行的線程: " + t1);
Thread t2 = new Thread(this,"第二線程");
System.out.println("創建第二線程");
t2.start();
try{
System.out.println("第一線程休眠");
Thread.sleep(3000);
}catch(InterruptedException e){
System.out.println("第一線程有錯");
}
System.out.println("第一線程退出");
}
public void run(){
try{
for(int i = 0;i < 5;i++){
System.out.println(「第二線程的休眠時間:」
+ i);
Thread.sleep(1000);
}
}catch(InterruptedException e){
System.out.println("線程有錯");
}
System.out.println("第二線程退出");
}
public static void main(String args[]){
new TwoThread();
}
}
程序運行結果如下:
正在運行的線程: Thread[第一主線程,5,main
創建第二線程
第一線程休眠
第二線程的休眠時間:0
第二線程的休眠時間:1
第二線程的休眠時間:2
第一線程退出
第二線程的休眠時間:3
第二線程的休眠時間:4
第二線程退出
至於一個處理器同時處理多個程序,其實不是同時運行多個程序的,簡單的說,如果是單核的CPU,在運行多個程序的時候其實是每個程序輪流佔用CPU的,只是每個程序佔用的時間很短,所以我們人為的感覺是「同時」運行多個程序。
⑽ JAVA線程的機制有哪些
Java的線程機制 摘 要: 多線程機制是Java的重要技術,闡述了線程和進程的差別;Java中線程4個狀態之間的轉換;並結合例子說明了兩種創建線程的方法。 線程是指程序中能順序執行的一個序列。一個線程只有一個入口點� 但可能有幾個出口點� 不過,每個時刻的執行點總是只有一個。線程是不能獨立運行的程序,而只是某個整體程序內部的一個順序執行流。 多線程是Java的一個重要特點。如果一個程序是單線程的,那麼,任何時刻都只有一個執行點。這種單線程執行方法使系統運行效率低,而且,由於必須依靠中斷來處理輸入/輸出。所以,當出現頻繁輸入/輸出或者有優先順序較低的中斷請求時,實時性就變得很差。多線程系統可以避免這個缺點。所謂多線程,就是通過系統的調度使幾個具有不同功能的程序流即線程同時並行地運行。 在單處理器計算機系統中,實際上是不可能使多個線程真正並行運行的,而要通過系統用極短的時間、極快的速度對多個線程進行切換,宏觀上形成多個線程並發執行的效果。 1 線程和進程機制上的差別 線程和進程很相象,它們都是程序的一個順序執行序列,但兩者又有區別。進程是一個實體,每個進程有自己獨立的狀態,並有自己的專用數據段,創建進程時, 必須建立和復制其專用數據段,線程則互相共享數據段。同一個程序中的所有線程只有一個數據段, 所以, 創建線程時不必重新建立和復制數據段。由於數據段建立和復制這方面的差異,使線程的建立和線程間的切換速度大大優於進程,另一方面,線程又具備進程的大多數優點。 假設銀行系統辦理存款和取款手續,將帳本看成數據段。如果按進程這種機制,那麼,當儲戶去存/取款時,銀行應先把帳本復制一遍,為儲戶建立一個獨立的帳本再結算。如果按線程機制, 那麼,銀行里所有的出納員都用同一個帳本,儲戶來辦存/取款時,也從這個帳本直接結算。用線程機制省去了數據段復制這一步顯然是線程獨具的特點。 由於多個線程共享一個數據段,所以,也出現了數據訪問過程的互斥和同步問題,這使系統管理功能變得相對復雜。 總的來說,一個多線程系統在提高系統的輸入/輸出速度、有效利用系統資源、改善計算機通信功能以及發揮多處理器硬體功能方面顯示了很大優勢。因此,一些最新的操作系統如Windows95、Windows98、Windows NT等都提供了對多線程的支持。但是,在多線程操作系統下設計多線程的程序仍然是一個比較復雜和困難的工作。由於需要解決對數據段的共享,所以,原則上應該從程序設計角度採用加鎖和釋放措施,稍有不慎,便會使系統產生管理上的混亂。 而Java從語言一級提供對多線程的支持,這樣,可由語言和運行系統聯合提供對共享數據段的管理功能和同步機制,使得多線程並行程序設計相對比較容易。 2 Java線程的生命周期 每個線程都是和生命周期相聯系的,一個生命周期含有多個狀態,這些狀態間可以互相轉化。 Java的線程的生命周期可以分為4個狀態;創建(new)狀態;可運行(runnable)狀態;不執行(notrunnable)狀態;消亡(dead)狀態。 創建狀態是指創建一個線程對應的對象的過程,Java系統中,些對象都是從Java.lang包內一個稱為Thread的類用關鍵字new創建的。剛創建的線程不能執行,必須向系統進行注冊、分配必要的資源後才能進入可運行狀態,這個步驟是由start操作完成的,而處於可運行狀態的線程也未必一定處於運行中,它有可能由於外部的I/O請求而處於不運行狀態。進入消亡狀態後,此線程就不再存在了。 一個線程創建之後,總是處於其生命周期的4個狀態之一中,線程的狀態表明此線程當前正在進行的活動,而線程的狀態是可以通過程序來進行控制的,就是說,可以對線程進行操作來改變狀態。 這些操作包括啟動(start)、終止(stop)、睡眠(sleep)、掛起(suspend)、恢復(resume)、等待(wait)和通知(notify)。每一個操作都對應了一個方法� 這些方法是由軟體包Java.lang提供的。通過各種操作,線程的4個狀態之間可按圖1所示進行轉換。 2.1 創建(new)狀態 如果創建了一個線程而沒有啟動它,那麼,此線程就處於創建狀態。比如,下述語句執行以後,使系統有了一個處於創建狀態的線程myThread:� Thread myThread=new MyThreadClass(); 其中,MyThreadClass()是Thread的子類,而Thread是由Java系統的Java.lang軟體包提供的。處於創建狀態的線程還沒有獲得應有的資源,所以,這是一個空的線程,線程只有通過啟動後,系統才會為它分配資源。對處於創建狀態的線程可以進行兩種操作: 一是啟動(start)操作,使其進入可運行狀態;二是終止(stop)操作,使其進入消亡狀態。如果進入到消亡狀態,那麼,此後這個線程就不能進入其它狀態,也就是說,它不復存在了。 start方法是對應啟動操作的方法,其具體功能是為線程分配必要的系統資源,將線程設置為可運行狀態,從而可以使系統調度這個線程。 2.2 可運行(runnable)狀態 如果對一個處於創建狀態的線程進行啟動操作,則此線程便進入可運行狀態。比如,用下列語句� myThread.start();� � 則使線程myThread進入可運行狀態。上述語句實質上是調用了線程體即run()方法,注意,run()方法包含在myThread線程中,也就是先由java.lang包的Thread類將run()方法傳遞給子類MyThreadClass(),再通過創建線程由子類MyThreadClass,傳遞給線程myThread。 線程處於可運行狀態只說明它具備了運行條件,但可運行狀態並不一定是運行狀態,因為在單處理器系統中運行多線程程序,實際上在一個時間點只有一個線程在運行,而系統中往往有多個線程同時處於可運行狀態,系統通過快速切換和調度使所有可運行線程共享處理器,造成宏觀上的多線程並發運行。可見,一個線程是否處於運行狀, 除了必須處於可運行狀態外,還取決於系統的調度。 在可運行狀態可以進行多種操作,最通常的是從run()方法正常退出而使線程結束,進入消亡狀態。 此, 還可以有如下操作� 掛起操作,通過調用suspend方法來實現; 睡眠操作,通過調用sleep方法來實現; 等待操作,通過調用wait方法來實現; 退讓操作,通過調用yield方法來實現; 終止操作,通過調用stop方法來實現。 前面三種操作都會使一個處於可運行狀態的線程進入不可運行狀態。比如,仍以myThread線程為例,當其處於可運行狀態後,再用如下語句� myThread.sleep (5000); 則調用sleep方法使myThread線程睡眠5s(5000ms)。這5s內,此線程不能被系統調度運行,只有過5s後,myThread線程才會醒來並自動回到可運行狀態。 如果一個線程被執行掛起操作而轉到不可運行狀態,則必須通過調用恢復(resume)操作,才能使這個線程再回到可運行狀態。 退讓操作是使某個線程把CPU控制權提前轉交給同級優先權的其他線程。 對可運行狀態的線程也可以通過調用stop方法使其進入消亡狀態。 2.3 不可運行(not runnable)狀態 不可運行狀態都是由可運行狀態轉變來的。一個處於可運行狀態的線程,如果遇到掛起(suspend)操作、睡眠(sleep)操作或者等待(wait)操作,就會進入不可運行狀態。 另外,如果一個線程是和I/O操作有關的,那麼,在執行I/O指令時,由於外設速度遠遠低於處理器速度而使線程受到阻, 從而進入不可運行狀態,只有外設完成輸入/輸出之後,才會自動回到可運行狀態。線程進入不可運行狀態後,還可以再回到可運行狀態,通常有三種途徑使其恢復到可運行狀態。 一是自動恢復。通過睡眠(sleep)操作進入不可運行狀態的線程會在過了指定睡眠時間以後自動恢復到可運行狀態,由於I/O阻塞而進入不可運行狀態的線程在外設完成I/O操作後,自動恢復到可運行狀態。 二是用恢復(resume)方法使其恢復。如果一個線程由於掛起(suspend)操作而從可運行狀態進入不可運行狀態,那麼,必須用恢復(resume)操作使其再恢復到可運行狀態。 三是用通知(notify或notifyAll)方法使其恢復。如果一個處於可運行狀態的線程由於等待(wait)操作而轉入不可運行狀態,那麼,必須通過調用notify方法或notifyAll方法才能使其恢復到可運行狀態,採用等待操作往往是由於線程需要等待某個條件變數,當獲得此條件變數後,便可由notify或ontifyAll方法使線程恢復到可運行狀態。 恢復到可運行狀態的每一種途徑都是有針對性的,不能交叉。比如,對由於阻塞而進入不可運行狀態的線程採用恢復操作將是無效的。 在不可運行狀態,也可由終止(stop)操作使其進入消亡狀態。 2.4 消亡(dead)狀態 一個線程可以由其他任何一個狀態通過終止(stop)操作而進入消亡狀態。 線程一旦進入消亡狀態,那它就不再存在了,所以也不可能再轉到其它狀態。 通常,在一個應用程序運行時,如果通過其它外部命令終止當前應用程序,那麼就會調用(stop)方法終止線程。但是,最正常、最常見的途徑是由於線程在可運行狀態正常完成自身的任務而″壽終正寢″,從而進入消亡狀態,這個完成任務的動作是由run方法實現的。 3 Java線程的兩種創建途徑 一種途徑是通過對Thread的繼承來派生一個子類,再由此子類生成一個對象來實現線程的創建,這是比較簡單直接的辦法。Thread類包含在系統API提供的8個軟體包之一Java.lang中,Thread類中包含了很多與線程有關的方, 其中,一個名為run的方法就是用來實現線程行為的。比如:� 1 import java.lang.*� //引用lang包 2 class Mango exteds Thread { 3 public void run() { 4 ...... 5 �} 6 �} 上述程序段中,第1行語句引用軟體包lang,這樣做是為了給編譯器一個信息,從而使後面程序中有關lang包中的方法可直接用方法名,而不必帶前綴「Java.lang」。第2行語句是從lang包Thread派生一個子類Mango, 而這個子類中提供了run方法的實現,這樣,運行時,將由子類Mango 的 run方法置換父類Thread的run方法。 不過這一步還沒有創建線, 必須由子類生成一個對象,並且進行啟動操作,這樣才能得到一個處於可運行狀態的線程。生成對象其實就是完成線程的創建,而啟動是對已創建的線程進行操作。具體語句如下:� Mango t=new Mango(); � t.start(); � 上面先用關鍵字new使線程進入創建狀態,又調用start()方法使線程進入可運行狀態。注意,start()方法是由Thread繼承給子類Mango、然後又在生成對象時由對象t從類Mango得到的。 另一種途徑是通過一個類去繼承介面runnable來實現線程的創建� 而這個類必須提供runnable介面中定義的方法run()的實現。runnable是Java.lang包中的一個介面,在runnable介面中,只定義了一個抽象方法run()。所以,如用這種途徑來創建線程,則應先由一個類連接介面runnable,並且提供run()方法的實現。比如,下面的程序段實現了與介面的連接。 1 public class xyz implements Runnable{ 2 int i; � 3 public voed run(){ 4 while (true){ � 5 System.out.println("Hello"+i++); 6 � } 7 � } 8 � } 然後再創建一個線程� runnable r=new xyz(); � Thread t=new Thread(r); 這種途徑創建線程比第一種途徑靈活。當一個類既需要繼承一個父類又要由此創建一個線程時,由於Java不支持多重繼承,這樣,用第一種途徑將行不通,因為,按此思路創建線程也是以繼承的方法實現的。 於是,就需要一個類既繼承Thread類,又繼承另一個父類。但用介面方法卻能實現這個目標。 4 線程的啟動和終止 Thread的start()方法對應於啟動操作,它完成兩方面的功能:一方面是為線程分配必要的資源,使線程處於可運行狀態,另一方面是調用線程的run()方法置換Thread的中run()方法或者置換runnable中的run()方法來運行線程。 使用start()方法的語句很簡單,即: ThreadName.start(); 下面的程序段先創建並啟動線程myThread, 然後使用sleep()方法讓其睡眠20000ms即20s,使其處於不可運行狀態,過20s後,線程又自動恢復到可運行狀態。 Thread MyThread=new MyThreadClass(); MyThread.start();� � try{ � MyThread.sleep(20000); �} catch(InterrujptedException e){ }