您好,登錄后才能下訂單哦!
1.概述
Java 給多線程編程提供了內置的支持。 一條線程指的是進程中一個單一順序的控制流,一個進程中可以并發多個線程,每條線程并行執行不同的任務。
多線程是多任務的一種特別的形式,但多線程使用了更小的資源開銷。
這里定義和線程相關的另一個術語 - 進程:一個進程包括由操作系統分配的內存空間,包含一個或多個線程。一個線程不能獨立的存在,它必須是進程的一部分。一個進程一直運行,直到所有的非守護線程都結束運行后才能結束。
多線程能滿足程序員編寫高效率的程序來達到充分利用 CPU
的目的。
線程是一個動態執行的過程,它也有一個從產生到死亡的過程。
下圖顯示了一個線程完整的生命周期。
新建狀態:
使用 new
關鍵字和 Thread
類或其子類建立一個線程對象后,該線程對象就處于新建狀態。它保持這個狀態直到程序 start()
這個線程。
就緒狀態:
當線程對象調用了 start()
方法之后,該線程就進入就緒狀態。就緒狀態的線程處于就緒隊列中,要等待JVM里線程調度器的調度。
運行狀態:
如果就緒狀態的線程獲取 CPU
資源,就可以執行 run()
,此時線程便處于運行狀態。處于運行狀態的線程最為復雜,它可以變為阻塞狀態、就緒狀態和死亡狀態。
阻塞狀態:
如果一個線程執行了 sleep(睡眠)
、suspend(掛起)
等方法,失去所占用資源之后,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或獲得設備資源后可以重新進入就緒狀態。可以分為三種:
wait()
方法,使線程進入到等待阻塞狀態。synchronized
同步鎖失敗(因為同步鎖被其他線程占用)。sleep()
或 join()
發出了 I/O 請求時,線程就會進入到阻塞狀態。當 sleep()
狀態超時,join()
等待線程終止或超時,或者 I/O 處理完畢,線程重新轉入就緒狀態。死亡狀態:
一個運行狀態的線程完成任務或者其他終止條件發生時,該線程就切換到終止狀態。
每一個 Java 線程都有一個優先級,這樣有助于操作系統確定線程的調度順序。
Java 線程的優先級是一個整數,其取值范圍是 1 (Thread.MIN_PRIORITY )
- 10 (Thread.MAX_PRIORITY )
。
默認情況下,每一個線程都會分配一個優先級 NORM_PRIORITY(5)
。
具有較高優先級的線程對程序更重要,并且應該在低優先級的線程之前分配處理器資源。但是,線程優先級不能保證線程執行的順序,而且非常依賴于平臺。
Java 提供了三種創建線程的方法:
通過實現 Runnable
接口;
通過繼承 Thread
類本身;
通過 Callable
和 Future
創建線程。
創建一個線程,最簡單的方法是創建一個實現 Runnable
接口的類。
為了實現 Runnable
,一個類只需要執行一個方法調用 run()
,聲明如下:
public void run()
你可以重寫該方法,重要的是理解的 run()
可以調用其他方法,使用其他類,并聲明變量,就像主線程一樣。
在創建一個實現 Runnable
接口的類之后,你可以在類中實例化一個線程對象。
Thread
定義了幾個構造方法,下面的這個是我們經常使用的:
Thread(Runnable threadOb,String threadName);
這里,threadOb
是一個實現 Runnable
接口的類的實例,并且 threadName
指定新線程的名字。
新線程創建之后,你調用它的 start()
方法它才會運行。
void start();
下面是一個創建線程并開始讓它執行的實例:
class RunnableDemo implements Runnable {
private Thread t;
private String threadName;
RunnableDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// 讓線程睡眠一會
Thread.sleep(50);
}
}catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String[] args) {
RunnableDemo R1 = new RunnableDemo( "Thread-1");
R1.start();
RunnableDemo R2 = new RunnableDemo( "Thread-2");
R2.start();
}
}
編譯以上程序運行結果如下:
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
創建一個線程的第二種方法是創建一個新的類,該類繼承 Thread
類,然后創建一個該類的實例。
繼承類必須重寫 run()
方法,該方法是新線程的入口點。它也必須調用 start()
方法才能執行。
該方法盡管被列為一種多線程實現方式,但是本質上也是實現了 Runnable
接口的一個實例。
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
ThreadDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// 讓線程睡眠一會
Thread.sleep(50);
}
}catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String[] args) {
ThreadDemo T1 = new ThreadDemo( "Thread-1");
T1.start();
ThreadDemo T2 = new ThreadDemo( "Thread-2");
T2.start();
}
}
編譯以上程序運行結果如下:
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
下表列出了 Thread
類的一些重要方法:
序號 | 方法描述 |
---|---|
1 | public void start() <br>使該線程開始執行;Java 虛擬機調用該線程的 run 方法。 |
2 | public void run() <br>如果該線程是使用獨立的 Runnable 運行對象構造的,則調用該 Runnable 對象的 run 方法;否則,該方法不執行任何操作并返回。 |
3 | public final void setName(String name) <br>改變線程名稱,使之與參數 name 相同。 |
4 | public final void setPriority(int priority) <br> 更改線程的優先級。 |
5 | public final void setDaemon(boolean on) <br>將該線程標記為守護線程或用戶線程。 |
6 | public final void join(long millisec) <br>等待該線程終止的時間最長為 millis 毫秒。 |
7 | public void interrupt() <br>中斷線程。 |
8 | public final boolean isAlive() <br>測試線程是否處于活動狀態。 |
測試線程是否處于活動狀態。 上述方法是被 Thread
對象調用的。下面的方法是 Thread
類的靜態方法。
序號 | 方法描述 |
---|---|
1 | public static void yield() <br>暫停當前正在執行的線程對象,并執行其他線程。 |
2 | public static void sleep(long millisec) <br>在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行),此操作受到系統計時器和調度程序精度和準確性的影響。 |
3 | public static boolean holdsLock(Object x) <br>當且僅當當前線程在指定的對象上保持監視器鎖時,才返回 true 。 |
4 | public static Thread currentThread() <br>返回對當前正在執行的線程對象的引用。 |
5 | public static void dumpStack() <br>將當前線程的堆棧跟蹤打印至標準錯誤流。 |
如下的 ThreadClassDemo
程序演示了 Thread
類的一些方法:
DisplayMessage.java
文件代碼:
// 文件名 : DisplayMessage.java
// 通過實現 Runnable 接口創建線程
public class DisplayMessage implements Runnable {
private String message;
public DisplayMessage(String message) {
this.message = message;
}
public void run() {
while(true) {
System.out.println(message);
}
}
}
GuessANumber.java
文件代碼:
// 文件名 : GuessANumber.java
// 通過繼承 Thread 類創建線程
public class GuessANumber extends Thread {
private int number;
public GuessANumber(int number) {
this.number = number;
}
public void run() {
int counter = 0;
int guess = 0;
do {
guess = (int) (Math.random() * 100 + 1);
System.out.println(this.getName() + " guesses " + guess);
counter++;
} while(guess != number);
System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**");
}
}
ThreadClassDemo.java
文件代碼:
// 文件名 : ThreadClassDemo.java
public class ThreadClassDemo {
public static void main(String [] args) {
Runnable hello = new DisplayMessage("Hello");
Thread thread1 = new Thread(hello);
thread1.setDaemon(true);
thread1.setName("hello");
System.out.println("Starting hello thread...");
thread1.start();
Runnable bye = new DisplayMessage("Goodbye");
Thread thread2 = new Thread(bye);
thread2.setPriority(Thread.MIN_PRIORITY);
thread2.setDaemon(true);
System.out.println("Starting goodbye thread...");
thread2.start();
System.out.println("Starting thread3...");
Thread thread3 = new GuessANumber(27);
thread3.start();
try {
thread3.join();
}catch(InterruptedException e) {
System.out.println("Thread interrupted.");
}
System.out.println("Starting thread4...");
Thread thread4 = new GuessANumber(75);
thread4.start();
System.out.println("main() is ending...");
}
}
運行結果如下,每一次運行的結果都不一樣。
Starting hello thread...
Starting goodbye thread...
Hello
Hello
Hello
Hello
Hello
Hello
Goodbye
Goodbye
Goodbye
Goodbye
Goodbye
.......
Callable
接口的實現類,并實現 call()
方法,該 call()
方法將作為線程執行體,并且有返回值。Callable
實現類的實例,使用 FutureTask
類來包裝 Callable
對象,該 FutureTask
對象封裝了該 Callable
對象的 call()
方法的返回值。FutureTask
對象作為 Thread
對象的 target
創建并啟動新線程。FutureTask
對象的 get()
方法來獲得子線程執行結束后的返回值。public class CallableThreadTest implements Callable<Integer> {
public static void main(String[] args)
{
CallableThreadTest ctt = new CallableThreadTest();
FutureTask<Integer> ft = new FutureTask<>(ctt);
for(int i = 0;i < 100;i++)
{
System.out.println(Thread.currentThread().getName()+" 的循環變量i的值"+i);
if(i==20)
{
new Thread(ft,"有返回值的線程").start();
}
}
try
{
System.out.println("子線程的返回值:"+ft.get());
} catch (InterruptedException e)
{
e.printStackTrace();
} catch (ExecutionException e)
{
e.printStackTrace();
}
}
@Override
public Integer call() throws Exception
{
int i = 0;
for(;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
}
return i;
}
}
Runnable
、Callable
接口的方式創建多線程時,線程類只是實現了 Runnable
接口或 Callable
接口,還可以繼承其他類。Thread
類的方式創建多線程時,編寫簡單,如果需要訪問當前線程,則無需使用 Thread.currentThread()
方法,直接使用 this
即可獲得當前線程。在多線程編程時,你需要了解以下幾個概念:
有效利用多線程的關鍵是理解程序是并發執行而不是串行執行的。例如:程序中有兩個子系統需要并發執行,這時候就需要利用多線程編程。
通過對多線程的使用,可以編寫出非常高效的程序。不過請注意,如果你創建太多的線程,程序執行的效率實際上是降低了,而不是提升了。
請記住,上下文的切換開銷也很重要,如果你創建了太多的線程,CPU
花費在上下文的切換的時間將多于執行程序的時間!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。