本文簡要介紹在 Java 世界中, 線程相關知識。主要包含 線程的創建與銷毀;線程安全與同步;線程通訊;註意本文沒有什麼高深新知識,只緣起前段時間在翻看項目代碼的時候,發現有些同學對此有諸多誤解,故在此稍微整理一下,以幫助類似同學,同時警醒一下自己。 ...
本文簡要介紹在 Java 世界中, 線程相關知識。主要包含 線程的創建與銷毀;線程安全與同步;線程通訊;註意本文沒有什麼高深新知識,只緣起前段時間在翻看項目代碼的時候,發現有些同學對此有諸多誤解,故在此稍微整理一下,以幫助類似同學,同時警醒一下自己。
1. 線程的創建和銷毀;
a) .創建線程可以通過繼承 Thread 類 或 實現 Runnable 介面, 並重寫 run() 方法, 其中的run() 方法即是本線程需要執行的內容.
b). 相比於單獨繼承 Thread ,Runnable介面配合 Thread 實現會更靈活,並可以通過共用一個Runnable介面實例,在Thread中共用資源.
c). 至於線程銷毀,不推薦使用 Thread.Stop()方法, 此方法在使用不當情況下會出現死鎖,更多的時候推薦在run()方法中使用額外變數(或條件)結束此方法即可.
2. 線程安全與同步;
a). 對於需要遵循ACID原子一致性的代碼段, 可以通過 synchronized(lockKey){} 代碼塊鎖定;
b). 同時 synchronized 關鍵字可以用來修飾一個方法,表示整個方法都需要遵循ACID原子一致性,值得註意的是,此時其實的lockKey等效於this關鍵字;
b). 在鎖定的代碼塊中推薦再進行一次必要的條件判斷。
3. 線程通訊,在java的世界中可以藉助 wait() notify() notifyAll() 這三個方法來完成,這三個方法定義在Object類中,因此所有的對象都可以使用.
4.下麵通過簡單的幾個代碼片段來加以說明
a). 演示線程創建與銷毀,及線程安全與同步

1 public class ThreadTest implements Runnable { 2 private boolean stop; //是否需要停止運行 3 private int tiketCount = 100000; //總票數 4 private boolean lockTypeIsMethod = true; //是否是提供方法鎖定還是代碼塊 5 6 public boolean isStop() { 7 return stop; 8 } 9 public void setStop(boolean stop) { 10 this.stop = stop; 11 } 12 13 public boolean isLockTypeMethod() { 14 return lockTypeIsMethod; 15 } 16 public void setLockTypeIsMethod(boolean lockTypeIsMethod) { 17 this.lockTypeIsMethod = lockTypeIsMethod; 18 } 19 20 @Override 21 public void run() { 22 while (tiketCount > 0 && !stop) { 23 try { 24 Thread.sleep(50); //延時,方便演示 25 } catch (InterruptedException e) { 26 e.printStackTrace(); 27 } 28 29 //如果是通過鎖定方法 30 if (lockTypeIsMethod) { 31 sale(); 32 } else { 33 synchronized (this) { 34 if (tiketCount > 0 && !stop) { 35 System.out.println("使用代碼塊鎖定:threadId=" 36 + Thread.currentThread().getName() + ",ticketNO:" + tiketCount--); 37 } 38 } 39 } 40 } 41 } 42 43 public synchronized void sale() { 44 if (tiketCount > 0 && !stop) { 45 System.out.println("使用方法鎖定:threadId=" 46 + Thread.currentThread().getName() + ",ticketNO:" + tiketCount--); 47 } 48 } 49 }線程定義類

1 public static void main(String[] args) throws InterruptedException { 2 ThreadTest threadTest = new ThreadTest(); //共用變數ThreadTest 3 //啟用四個線程 4 new Thread(threadTest).start(); 5 new Thread(threadTest).start(); 6 new Thread(threadTest).start(); 7 new Thread(threadTest).start(); 8 //模擬設置共用變數, 9 // 1.交替使用方法體和代碼塊來進行線程同步實驗 10 // 2.模擬線程停止 11 for (int i = 0; i < 100; i++) { 12 Thread.sleep(1000); 13 threadTest.setLockTypeIsMethod(i % 2 == 0); 14 if (i == 50) { 15 threadTest.setStop(true); 16 } 17 } 18 }調用端
b). 演示線程通訊,本處模擬兩個線程以生產和消費者角色讀寫一個集合的示例,其中當集合中有數據的時候通知消費者處理數據,處理完後通知生產者往集合中放入數據

1 //數據倉庫 2 public class DataRepository { 3 private List<String> data = new ArrayList<>(); 4 private boolean hasData; 5 6 public boolean HasData() { 7 return hasData; 8 } 9 10 public void setHasData(boolean hasData) { 11 this.hasData = hasData; 12 } 13 14 //放入數據 15 public synchronized void put(List<String> data) throws InterruptedException { 16 //生產者放入數據的時候,如果還有數據則等待. 17 if (hasData) { 18 wait(); 19 } 20 this.data = data; 21 hasData = true; 22 //放入完畢後通知消費者 23 notify(); 24 } 25 26 //讀取數據 27 public synchronized List<String> get() throws InterruptedException { 28 //沒有數據則等待 29 if (!hasData) { 30 wait(); 31 } 32 //獲取數據副本返回 33 List<String> rs = new ArrayList<>(data); 34 data.clear(); 35 hasData = false; 36 notify(); 37 return rs; 38 } 39 }數據倉庫

1 public class Producer implements Runnable { 2 private DataRepository dataRepository; //數據倉庫 3 public Producer(DataRepository dataRepository) { 4 this.dataRepository = dataRepository; 5 } 6 7 public void run() { 8 while (true) { 9 try { 10 Thread.sleep(1000); 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 List<String> temp = new ArrayList<>(); 15 temp.add("------------"); 16 temp.add("第一個數據"); 17 temp.add("第二個數據"); 18 temp.add("第三個數據"); 19 temp.add("第四個數據"); 20 temp.add("------------"); 21 try { 22 dataRepository.put(temp); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 } 27 } 28 }生產者

1 public class Consumer implements Runnable { 2 private DataRepository dataRepository; 3 4 public Consumer(DataRepository dataRepository) { 5 this.dataRepository = dataRepository; 6 } 7 8 public void run() { 9 while (true) { 10 try { 11 Thread.sleep(1000); 12 } catch (InterruptedException e) { 13 e.printStackTrace(); 14 } 15 try { 16 List<String> data=dataRepository.get(); 17 if(data!=null&&!data.isEmpty()){ 18 for (String temp :data){ 19 System.out.println(temp); 20 } 21 } 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 } 26 } 27 }消費者

1 public class Client { 2 public static void main(String[] args) throws InterruptedException { 3 4 DataRepository dr=new DataRepository(); 5 new Thread(new Producer(dr)).start(); //啟動生產者線程 6 new Thread(new Consumer(dr)).start(); //啟動消費者線程 7 } 8 }調用端
後記:
a). 多線程屬於較基礎的知識,我們首先需要瞭解其最基本的概念,才能在項目中游刃有餘的應用;
b).不管是什麼語言,其所需要的理論支持均大同小異;
c).回到最初的那個概念,在多線程中,能不需要線程互相通訊就儘量不要用,能不同步就儘量不要使用線程同步,能不使用多線程就儘量不要使用多線程,說得有些含糊,各位自己去參悟吧.