Java Concurrency #0: 預先知識

Charlie Lee
Bucketing
Published in
7 min readJul 19, 2020
Photo by Héctor J. Rivas on Unsplash

文章架構

  • 什麼是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,

  1. 記憶體的劃分: 在Java中會是透過JVM來限制Process的記憶體使用大小,超過會丟出OOM例外,
  2. 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執行要平行化的處理。

  1. 繼承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,或是每次要執行新的功能時都需要繼承一次,沒有保護邏輯)。

  1. 將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方法後,開發會變得多麼直觀簡單。

  1. 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有興趣的話歡迎閱讀,如果有其他想法也歡迎留言攻擊我

--

--