Java 多執行緒
java多執行緒
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 介面的類,
為了實作 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來創建執行緒
創建一個執行緒的第二種方法是創建一個新的類,該類繼承 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 方法
下表列出了Thread類的一些重要方法:
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()
測驗執行緒是否處于活動狀態,
測驗執行緒是否處于活動狀態, 上述方法是被Thread物件呼叫的,下面的方法是Thread類的靜態方法,
1 public static void yield()
暫停當前正在執行的執行緒物件,并執行其他執行緒,
2 public static void sleep(long millisec)
在指定的毫秒數內讓當前正在執行的執行緒休眠(暫停執行),此操作受到系統計時器和調度程式精度和準確性的影響,
3 public static boolean holdsLock(Object x)
當且僅當當前執行緒在指定的物件上保持監視器鎖時,才回傳 true,
4 public static Thread currentThread()
回傳對當前正在執行的執行緒物件的參考,
5 public static void dumpStack()
將當前執行緒的堆疊跟蹤列印至標準錯誤流,
實體
如下的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 和 Future 創建執行緒
-
創建 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 花費在背景關系的切換的時間將多于執行程式的時間!
Python3 多執行緒
Python3 多執行緒
多執行緒類似于同時執行多個不同程式,多執行緒運行有如下優點:
使用執行緒可以把占據長時間的程式中的任務放到后臺去處理,
用戶界面可以更加吸引人,比如用戶點擊了一個按鈕去觸發某些事件的處理,可以彈出一個進度條來顯示處理的進度,
程式的運行速度可能加快,
在一些等待的任務實作上如用戶輸入、檔案讀寫和網路收發資料等,執行緒就比較有用了,在這種情況下我們可以釋放一些珍貴的資源如記憶體占用等等,
每個獨立的執行緒有一個程式運行的入口、順序執行序列和程式的出口,但是執行緒不能夠獨立執行,必須依存在應用程式中,由應用程式提供多個執行緒執行控制,
每個執行緒都有他自己的一組CPU暫存器,稱為執行緒的背景關系,該背景關系反映了執行緒上次運行該執行緒的CPU暫存器的狀態,
指令指標和堆疊指標暫存器是執行緒背景關系中兩個最重要的暫存器,執行緒總是在行程得到背景關系中運行的,這些地址都用于標志擁有執行緒的行程地址空間中的記憶體,
執行緒可以被搶占(中斷),
在其他執行緒正在運行時,執行緒可以暫時擱置(也稱為睡眠) – 這就是執行緒的退讓,
執行緒可以分為:
內核執行緒:由作業系統內核創建和撤銷,
用戶執行緒:不需要內核支持而在用戶程式中實作的執行緒,
Python3 執行緒中常用的兩個模塊為:
_thread
threading(推薦使用)
thread 模塊已被廢棄,用戶可以使用 threading 模塊代替,所以,在 Python3 中不能再使用"thread" 模塊,為了兼容性,Python3 將 thread 重命名為 “_thread”,
開始學習Python執行緒
Python中使用執行緒有兩種方式:函式或者用類來包裝執行緒物件,
函式式:呼叫 _thread 模塊中的start_new_thread()函式來產生新執行緒,語法如下:
_thread.start_new_thread ( function, args[, kwargs] )
引數說明:
function - 執行緒函式,
args - 傳遞給執行緒函式的引數,他必須是個tuple型別,
kwargs - 可選引數,
實體
#!/usr/bin/python3
import _thread
import time
為執行緒定義一個函式
def print_time( threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print ("%s: %s" % ( threadName, time.ctime(time.time()) ))
創建兩個執行緒
try:
_thread.start_new_thread( print_time, ("Thread-1", 2, ) )
_thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
print ("Error: 無法啟動執行緒")
while 1:
pass
執行以上程式輸出結果如下:
Thread-1: Wed Apr 6 11:36:31 2016
Thread-1: Wed Apr 6 11:36:33 2016
Thread-2: Wed Apr 6 11:36:33 2016
Thread-1: Wed Apr 6 11:36:35 2016
Thread-1: Wed Apr 6 11:36:37 2016
Thread-2: Wed Apr 6 11:36:37 2016
Thread-1: Wed Apr 6 11:36:39 2016
Thread-2: Wed Apr 6 11:36:41 2016
Thread-2: Wed Apr 6 11:36:45 2016
Thread-2: Wed Apr 6 11:36:49 2016
執行以上程后可以按下 ctrl-c 退出,
執行緒模塊
Python3 通過兩個標準庫 _thread 和 threading 提供對執行緒的支持,
_thread 提供了低級別的、原始的執行緒以及一個簡單的鎖,它相比于 threading 模塊的功能還是比較有限的,
threading 模塊除了包含 _thread 模塊中的所有方法外,還提供的其他方法:
threading.currentThread(): 回傳當前的執行緒變數,
threading.enumerate(): 回傳一個包含正在運行的執行緒的list,正在運行指執行緒啟動后、結束前,不包括啟動前和終止后的執行緒,
threading.activeCount(): 回傳正在運行的執行緒數量,與len(threading.enumerate())有相同的結果,
除了使用方法外,執行緒模塊同樣提供了Thread類來處理執行緒,Thread類提供了以下方法:
run(): 用以表示執行緒活動的方法,
start():啟動執行緒活動,
join([time]): 等待至執行緒中止,這阻塞呼叫執行緒直至執行緒的join() 方法被呼叫中止-正常退出或者拋出未處理的例外-或者是可選的超時發生,
isAlive(): 回傳執行緒是否活動的,
getName(): 回傳執行緒名,
setName(): 設定執行緒名,
使用 threading 模塊創建執行緒
我們可以通過直接從 threading.Thread 繼承創建一個新的子類,并實體化后呼叫 start() 方法啟動新執行緒,即它呼叫了執行緒的 run() 方法:
實體
#!/usr/bin/python3
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print ("開始執行緒:" + self.name)
print_time(self.name, self.counter, 5)
print ("退出執行緒:" + self.name)
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
創建新執行緒
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
開啟新執行緒
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("退出主執行緒")
以上程式執行結果如下;
開始執行緒:Thread-1
開始執行緒:Thread-2
Thread-1: Wed Apr 6 11:46:46 2016
Thread-1: Wed Apr 6 11:46:47 2016
Thread-2: Wed Apr 6 11:46:47 2016
Thread-1: Wed Apr 6 11:46:48 2016
Thread-1: Wed Apr 6 11:46:49 2016
Thread-2: Wed Apr 6 11:46:49 2016
Thread-1: Wed Apr 6 11:46:50 2016
退出執行緒:Thread-1
Thread-2: Wed Apr 6 11:46:51 2016
Thread-2: Wed Apr 6 11:46:53 2016
Thread-2: Wed Apr 6 11:46:55 2016
退出執行緒:Thread-2
退出主執行緒
執行緒同步
如果多個執行緒共同對某個資料修改,則可能出現不可預料的結果,為了保證資料的正確性,需要對多個執行緒進行同步,
使用 Thread 物件的 Lock 和 Rlock 可以實作簡單的執行緒同步,這兩個物件都有 acquire 方法和 release 方法,對于那些需要每次只允許一個執行緒操作的資料,可以將其操作放到 acquire 和 release 方法之間,如下:
多執行緒的優勢在于可以同時運行多個任務(至少感覺起來是這樣),但是當執行緒需要共享資料時,可能存在資料不同步的問題,
考慮這樣一種情況:一個串列里所有元素都是0,執行緒"set"從后向前把所有元素改成1,而執行緒"print"負責從前往后讀取串列并列印,
那么,可能執行緒"set"開始改的時候,執行緒"print"便來列印串列了,輸出就成了一半0一半1,這就是資料的不同步,為了避免這種情況,引入了鎖的概念,
鎖有兩種狀態——鎖定和未鎖定,每當一個執行緒比如"set"要訪問共享資料時,必須先獲得鎖定;如果已經有別的執行緒比如"print"獲得鎖定了,那么就讓執行緒"set"暫停,也就是同步阻塞;等到執行緒"print"訪問完畢,釋放鎖以后,再讓執行緒"set"繼續,
經過這樣的處理,列印串列時要么全部輸出0,要么全部輸出1,不會再出現一半0一半1的尷尬場面,
實體
#!/usr/bin/python3
import threading
import time
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print ("開啟執行緒: " + self.name)
# 獲取鎖,用于執行緒同步
threadLock.acquire()
print_time(self.name, self.counter, 3)
# 釋放鎖,開啟下一個執行緒
threadLock.release()
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
threadLock = threading.Lock()
threads = []
創建新執行緒
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
開啟新執行緒
thread1.start()
thread2.start()
添加執行緒到執行緒串列
threads.append(thread1)
threads.append(thread2)
等待所有執行緒完成
for t in threads:
t.join()
print ("退出主執行緒")
執行以上程式,輸出結果為:
開啟執行緒: Thread-1
開啟執行緒: Thread-2
Thread-1: Wed Apr 6 11:52:57 2016
Thread-1: Wed Apr 6 11:52:58 2016
Thread-1: Wed Apr 6 11:52:59 2016
Thread-2: Wed Apr 6 11:53:01 2016
Thread-2: Wed Apr 6 11:53:03 2016
Thread-2: Wed Apr 6 11:53:05 2016
退出主執行緒
執行緒優先級佇列( Queue)
Python 的 Queue 模塊中提供了同步的、執行緒安全的佇列類,包括FIFO(先入先出)佇列Queue,LIFO(后入先出)佇列LifoQueue,和優先級佇列 PriorityQueue,
這些佇列都實作了鎖原語,能夠在多執行緒中直接使用,可以使用佇列來實作執行緒間的同步,
Queue 模塊中的常用方法:
Queue.qsize() 回傳佇列的大小
Queue.empty() 如果佇列為空,回傳True,反之False
Queue.full() 如果佇列滿了,回傳True,反之False
Queue.full 與 maxsize 大小對應
Queue.get([block[, timeout]])獲取佇列,timeout等待時間
Queue.get_nowait() 相當Queue.get(False)
Queue.put(item) 寫入佇列,timeout等待時間
Queue.put_nowait(item) 相當Queue.put(item, False)
Queue.task_done() 在完成一項作業之后,Queue.task_done()函式向任務已經完成的佇列發送一個信號
Queue.join() 實際上意味著等到佇列為空,再執行別的操作
實體
#!/usr/bin/python3
import queue
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
def __init__(self, threadID, name, q):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
print ("開啟執行緒:" + self.name)
process_data(self.name, self.q)
print ("退出執行緒:" + self.name)
def process_data(threadName, q):
while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
queueLock.release()
print ("%s processing %s" % (threadName, data))
else:
queueLock.release()
time.sleep(1)
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = queue.Queue(10)
threads = []
threadID = 1
創建新執行緒
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
填充佇列
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
等待佇列清空
while not workQueue.empty():
pass
通知執行緒是時候退出
exitFlag = 1
等待所有執行緒完成
for t in threads:
t.join()
print ("退出主執行緒")
以上程式執行結果:
開啟執行緒:Thread-1
開啟執行緒:Thread-2
開啟執行緒:Thread-3
Thread-3 processing One
Thread-1 processing Two
Thread-2 processing Three
Thread-3 processing Four
Thread-1 processing Five
退出執行緒:Thread-3
退出執行緒:Thread-2
退出執行緒:Thread-1
退出主執行緒
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/229448.html
標籤:java
上一篇:一分鐘看懂位元組流
