Java并发编程(02):线程核心机制,基础概念扩展
本文源碼: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;}@Overridepublic 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 {@Overridepublic 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 {@Overridepublic void run() {for (int i = 0 ; i < 100 ; i++){System.out.println(Thread.currentThread().getName()+";i="+i);}} } class Priority02 extends Thread {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 ;}@Overridepublic 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 {@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e){e.printStackTrace();}// 如何處理這里異常?Integer.parseInt("cicada") ;} } class MyExe implements Thread.UncaughtExceptionHandler {@Overridepublic 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總結
以上是生活随笔為你收集整理的Java并发编程(02):线程核心机制,基础概念扩展的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux看3D实景
- 下一篇: 爱由心生