Java Concurrency #0: 預先知識
文章架構
- 什麼是Thread?
- 如何在Java使用Thread
什麼是Thread?
在開始Java Concurrecny系列文章前,最必要的知識就是Thread是甚麼,而聊到Thread就需要帶到Program與Procss的OS概念,直接從一個情境瞭解Program/Process/Thread。
A開發者寫好了交易系統,打包成可運行的程式(jar/exe…),點擊兩下系統並且等待系統初始化後開始執行,打開畫面可以看到每分鐘更新的商品價格與交易的相關操作。
- A開發者寫好了交易系統,打包成可運行的程式(jar/exe…)
開發者寫好的交易系統就是所謂的Code,而打包成可以運行的程式就是Program
- 點擊兩下系統並且等待系統初始化後開始執行
點擊兩下告訴作業系統要運行這個Program,作業系統會劃分一個區塊並且區別此Program可以使用的資源(記憶體/CPU時間),這個劃分的區塊就是Process,
- 記憶體的劃分: 在Java中會是透過JVM來限制Process的記憶體使用大小,超過會丟出OOM例外,
- CPU時間的劃分: 每個單元就是Thread,當一個Process創造出多個Thread也代表CPU會跑到此Process的功能機率會比較高(當CPU調用策略是公平的時候),
在每個Process初始化的時候會創造出Main Thread,負責控制Process的主流程(Java中的Main),開發者通常會圍繞著Main Thread開發與Debug。
- 打開畫面可以看到每分鐘更新的商品價格與交易的相關操作
這一段可以看出,此Process *背後可能包含了三個不同的Thread,Main Thread負責控制畫面,其它兩個Sub Thread一個負責每分鐘拿取價格資料並且刷新,而另一個等待使用著使用交易操作並且採取相關的邏輯
FunctionalInterface
在未來的系列文範例中,會使用Lambda的方式開發,Lambda的知識點只要知道當Interface上頭包含了@FunctionalInterface與只有一個method,就代表此介面的子孫(實作繼承)都可以透過Lambda方式開發() -> do something
如何在Java使用Thread
在Java使用Thread有三種方法,而每個方法的關鍵點都是Override run方法,再透過Thread的start執行要平行化的處理。
- 繼承Thread
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("Hi my name is Monster Lee");
} public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
}
}
第一種方法是最簡單使用Thread的方法,但是不建議使用,因為是直接改寫Java提供的Thread,算是比較重量級的方法(可能會改到原生Thread的method,或是每次要執行新的功能時都需要繼承一次,沒有保護邏輯)。
- 將Runnable物件帶入到Thread建構者中
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Hi my name is Monster Lee");
} public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
Thread thread1 = new Thread(() -> {
System.out.println("Hi I am lambda runnable");
});
thread1.start();
}
}
時做Runnable相較於繼承Thread,更加輕量(只牽涉到run方法),在main前面的Thread是用最基本的方式將Runnable帶入Thread的建構者中,而第二個Thread是使用lambda語法直接使用,可以看到當Java8讓開發者可以使用Fundamental方法後,開發會變得多麼直觀簡單。
- FututreTask與Callback等待非同步的資料傳回(程式會block在等待資料的程式碼)
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(4000);
return "Hi my name is Monster Lee";
} public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable mc = new MyCallable();
FutureTask<String> ftData = new FutureTask<>(mc);
Thread thread = new Thread(ftData);
thread.start();
System.out.println(ftData.get()); FutureTask<String> ftData2 = new FutureTask<String>(()-> "in lambda callable");
Thread thread1 = new Thread(ftData2);
thread1.start();
while (!ftData2.isDone()) ;
System.out.println(ftData2.get());
}
}
Callback與FutureTask的搭配,若是有上面MyCallback的情形,有消耗時間較長的邏輯(DB的搜尋 I/O)則Main Thread在拿回回傳值的時候會Block住,解決方法可以參考ftData2的方法,先詢問isDone再取回。
在一般情形不常使用Callback與FutureTask,但是當Concurrency程式變的龐大與複雜的時候,Callback與FutureTask可以協助開發者進行比較複雜的Multi Thread設計,在後續的系列文也會提到。
總結
本篇文章介紹了最簡單的Thread與OS Program -> Process -> Thrad的概念,在後半段實作了三種Java使用Thread的方法,這些都只是系列文的開端先有個基本知識,後續的文章會有更進階的使用。
Murmur
在現在分散式系統概念人人朗朗上口的環境下,到底有多少工程師已經把單台計算機的效能用到極致,才將系統分散化。或是其實每台計算機都只用到百分之10的效能,只是為了追求分散式系統的潮流而分散化,值得深思。
如果讀者們對Java Concurrency有興趣的話歡迎閱讀,如果有其他想法也歡迎留言攻擊我