Java並發編程(02):執行緒核心機制,基礎概念擴展
- 2020 年 3 月 15 日
- 筆記
本文源碼:GitHub·點這裡 || GitEE·點這裡
一、執行緒基本機制
1、概念描述
並發編程的特點是:可以將程式劃分為多個分離且獨立運行的任務,通過執行緒來驅動這些獨立的任務執行,從而提升整體的效率。下面提供一個基礎的演示案例。
2、應用案例
場景:假設有一個容器集合,需要拿出容器中的每個元素,進行加工處理,一般情況下直接遍歷就好,如果數據偏大,可以根據執行緒數量對集合切割,每個執行緒處理一部分數據,這樣處理時間就會減少很多。
public class ExtendThread01 { public static void main(String[] args) { List<Object> dataList = new ArrayList<>() ; dataList.add("A"); dataList.add("B"); dataList.add("C"); // 把一個大的集合按照每個子集合的2個元素切割 List<List<Object>> splitList = splitList(dataList,2); for (List<Object> list:splitList){ System.out.println(list); } // 多執行緒處理 for (List<Object> childList:splitList){ ListTask listTask = new ListTask(childList) ; Thread runThread = new Thread(listTask); runThread.start(); } } /** * List 集合切割 */ private static List<List<Object>> splitList (List<Object> list, int childSize) { if (list == null || list.size() == 0 || childSize < 1) { return null; } List<List<Object>> result = new ArrayList<>(); int size = list.size(); int count = (size + childSize - 1) / childSize ; for (int i = 0; i < count; i++) { List<Object> subList = list.subList(i * childSize, ((i + 1) * childSize > size ? size : childSize * (i + 1))); result.add(subList); } return result; } } class ListTask implements Runnable { private List<Object> list ; public ListTask (List<Object> list){this.list=list;} @Override public void run() { for (Object object:list){ System.out.println(Thread.currentThread().getName()+"=="+object); } } }
注意:這裡案例只是對場景原理的實現,在開發中,是不允許這種操作的,需要使用執行緒池處理,後續會說。如果集合沒有控制好,導致大量創建Thread執行緒,導致記憶體溢出。
二、執行緒停止啟動
1、基礎流程
執行緒啟動後執行任務方法,在執行過程中可以被阻塞,休眠,喚醒,停止等一系列狀態操作。
執行緒休眠作用:當執行緒一部分任務執行完畢後進入休眠(阻塞)狀態,執行緒調度器可以切換到另外執行緒,這對分布任務的執行相對公平。
2、使用案例
public class ExtendThread02 { public static void main(String[] args) { StopThread stopThread = new StopThread() ; stopThread.start(); // 標記當前執行緒停止訊號,且拋出中斷異常,但沒有停止 stopThread.interrupt(); // 判斷當前執行緒是否已經是終止狀態 System.out.println("1=="+stopThread.isInterrupted()); // 清除當前執行緒的終止訊號 System.out.println("2=="+stopThread.interrupted()); // 再次判斷當前執行緒狀態 System.out.println("3=="+stopThread.isInterrupted()); System.out.println("main end ..."); } } class StopThread extends Thread { @Override public void run() { for (int i = 0 ; i < 10 ; i++){ try { System.out.println(Thread.currentThread().getId()+"="+i); // 執行緒阻塞1秒 Thread.sleep(1000); } catch (InterruptedException e){ e.printStackTrace(); } } } }
3、核心方法
sleep(long millis):執行緒休眠指定的時間,進入阻塞狀態;
interrupt():切換執行緒為中斷狀態,拋出中斷異常,不會停止執行緒,可以監視執行緒的中斷狀態並定義執行策略。
interrupted():清除調用該方法執行緒的中斷狀態,也不會影響執行緒的執行,且返回當前執行『stopThread.interrupted()』的執行緒是否中斷,這裡就是指main執行緒是否中斷。
isInterrupted():判斷調用該方法的執行緒是否已經是中斷狀態。
補刀一句:執行緒的這幾個方法極其容易混淆,需要斷點源碼追蹤一下看看,進入源碼方法,調用相關API查看一下狀態。(附斷點圖一張:)

三、執行緒優先順序
1、基礎概念
CPU執行和處理執行緒的順序是不確定的,但是執行緒調度器傾向執行執行緒優先順序高的執行緒,執行緒優先順序高說明獲取CPU資源的概率高,或者獲取的執行時間分片多,但不代表優先順序低的一定最後執行。
2、使用案例
public class ExtendThread03 { public static void main(String[] args) { Priority01 priority01 = new Priority01(); priority01.start(); System.out.println("priority01="+priority01.getPriority()); Priority02 priority02 = new Priority02(); priority02.start(); System.out.println("priority02="+priority02.getPriority()); priority01.setPriority(10); priority02.setPriority(1); } } class Priority01 extends Thread { @Override public void run() { for (int i = 0 ; i < 100 ; i++){ System.out.println(Thread.currentThread().getName()+";i="+i); } } } class Priority02 extends Thread { @Override public void run() { for (int a = 0 ; a < 100 ; a++){ System.out.println(Thread.currentThread().getName()+";a="+a); } } }
注意:優先順序範圍[MAX_PRIORITY=10,MIN_PRIORITY=1],如果超出範圍會拋出IllegalArgumentException異常。
建議:通常實際開發中,是不允許輕易修改執行緒運行的參數,容易引發認知之外的異常。
四、執行緒加入
1、基本概念
如果在執行緒A中,執行執行緒B的加入方法,那麼A執行緒就會等待執行緒B執行完畢再返回繼續執行。
2、使用案例
public class ExtendThread04 { public static void main(String[] args) { JoinThreadA joinThreadA = new JoinThreadA() ; joinThreadA.start(); } } class JoinThreadA extends Thread { @Override public void run() { System.out.println("缺水中..."); JoinThreadB joinThreadB = new JoinThreadB() ; joinThreadB.start(); try{ joinThreadB.join(); } catch (Exception e){ e.printStackTrace(); } System.out.println("喝水中..."); } } class JoinThreadB extends Thread { @Override public void run() { System.out.println("買水中..."); try{ TimeUnit.SECONDS.sleep(2); } catch (Exception e){ e.printStackTrace(); } System.out.println("買到水..."); } }
注意:可以設置執行緒的加入時間join(long),畢竟等不到雪月風花,人生都是有時差,只能後會無期了。
五、本地執行緒
1、基本概念
本地的執行緒變數,底層維護ThreadLocalMap存儲值:
static class Entry extends WeakReference<ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
即以Key-Value鍵值對的方式存儲數據。如果對集合容器的源碼熟悉的話,這個Entry就是似曾相識感覺。
2、使用案例
public class ExtendThread05 { private static final ThreadLocal<Long> threadLocal = new ThreadLocal<>() ; private static void initBegin (){ threadLocal.set(System.currentTimeMillis()); } private static Long overTime (){ return System.currentTimeMillis()-threadLocal.get(); } public static void main(String[] args) throws Exception { ExtendThread05.initBegin(); TimeUnit.SECONDS.sleep(3); System.out.println(ExtendThread05.overTime()); } }
ThreadLocal提供執行緒記憶體儲變數的能力,並且綁定到當前執行緒,通過get和set方法就可以得到和設置當前執行緒對應的值。這個在web開發中是常見的應用。
六、守護執行緒
1、基本概念
守護執行緒是支援輔助型執行緒,主要在程式中起到調度和支援性作用,當Jvm中非守護執行緒全部結束,守護執行緒也就會結束。
2、使用案例
public class ExtendThread06 { public static void main(String[] args) throws Exception { InputStreamReader is = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(is); String value = br.readLine(); CheckThread checkThread = new CheckThread(value) ; checkThread.setDaemon(true); checkThread.start(); System.out.println("Main End ..."); } } class CheckThread extends Thread { private String spell ; public CheckThread (String spell){ this.spell = spell ; } @Override public void run() { if (spell.startsWith("cs")){ System.out.println(spell+":輸入正確"); } else { System.out.println(spell+":輸入錯誤"); } try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e){ e.printStackTrace(); } } }
注意:守護執行緒需要顯式調用setDaemon(true);方法,這裡可以看到main執行緒結束,整合程式就結束了,絲毫理會休眠中的守護執行緒。如果打斷非守護執行緒的休眠,試試會不會拋你一臉異常?
七、執行緒異常處理
1、機制描述
按照Java中異常處理機制,拋異常逐級降低,執行緒的任務方法run沒有拋異常,那重寫或者實現的方法自然不能直接throws異常出去。多執行緒中推薦捕獲異常,可以針對性處理機制。
2、使用案例
public class ExtendThread07 { public static void main(String[] args) { TryThread tryThread = new TryThread(); tryThread.setName("try-name"); // 定義運行中異常處理策略 MyExe myExe = new MyExe() ; tryThread.setUncaughtExceptionHandler(myExe); tryThread.start(); } } class TryThread extends Thread { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e){ e.printStackTrace(); } // 如何處理這裡異常? Integer.parseInt("cicada") ; } } class MyExe implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println(t.getName()+";異常:"+e.getMessage()); } }
通過實現UncaughtExceptionHandler介面,並且執行緒要指定自定義異常處理對象,也可以處理未檢查的異常。
八、源程式碼地址
GitHub·地址 https://github.com/cicadasmile/java-base-parent GitEE·地址 https://gitee.com/cicadasmile/java-base-parent


