併發同步知多少
找工作的時候是否經常看到要求有高併發,分散式系統的開發設計經驗,或者高併發,分散式系統的開發設計經驗者優先等字樣,這時候情不自禁的搜尋一下什麼是併發,多少算高併發,再思索一下自己的經歷哪些是符合這個要求的?那麼什麼是併發,開發中的併發是怎麼處理的,簡單瞭解一下吧。
在介紹併發之前我們先了解一下序列和並行:
熱鬧的景點,買票人很多,這時只有一個視窗售票,大家排隊依次買票就可以理解為序列。
排隊人太多了,旁邊又加開了幾個視窗,多人在不同的視窗同時買票可以理解為並行。
如果只能開一個視窗,這時好多著急的人圍上來,有問價格的,有掏錢的,又有取票的,在這個過程中售票員在同時應對多個買票人,可以理解為併發。
我們經常在計算機上一邊聽歌一邊寫文件(或者處理其他的事情),這就是一種併發行為,表面看兩個程式是同時進行,為什麼不是並行呢?計算機只有一個CPU所以只能支援一個執行緒執行。有人拍磚說:我家裡計算機是多核CPU可以同時支援多個執行緒執行,確實是這樣,為此我也特地去百度了一下有如下幾點認知:
1、雖然是多核CPU但是,系統匯流排,記憶體是共用的,在載入記憶體資料時仍然需要序列訪問。
2、目前的程式設計語言仍然是過程型開發,沒有和好的方法能自動的切割任務使平行計算。
3、作業系統線上程排程時隨著核心的增加複雜性遞增,目前最多支援8核
所以基於以上認知,我們在討論併發和同步這個問題時仍然按照CPU單核來討論。
那麼計算機是如何做到一邊播放歌曲一邊支援文件編輯呢?作業系統會把CPU的執行時間劃分微妙級別的時間片段,每一個時間片內去排程一個執行緒執行,多個執行緒不斷的切換執行,因此在人類可感知的時間段(秒級)內執行緒是同時執行的,所以多個執行緒在某個時間段內的同時執行就是併發。
序列、並行和併發如下圖所示:
網際網路應用基本上都是支援多使用者多請求同時訪問伺服器端的,所以網際網路應用都是支援併發的,那麼高併發的主要困難是什麼呢?作業系統會給每個執行緒分配獨立的記憶體空間和時間片,所以執行緒間是隔離的。但是如果執行緒訪問執行緒外的記憶體空間,檔案系統,輸入輸出裝置,資料庫或者其他儲存裝置時就會發生資源競爭,共享資源的訪問必須序列,保證序列訪問資源的機制就是同步,JAVA中經常使用的同步機制有synchronized關鍵字,java.util.concurrent.locks.Lock系列類。
同步的場景有以下幾種:
1、執行緒獲取同步鎖,獲取失敗則阻塞等待
適用場景:
a、同步獲取序列號生成器,當有其他執行緒獲取序列號時,其他執行緒等待
java程式碼例項:
public class SynchronizedProcessor implements Processor { /* (non-Javadoc) * @see com.sunhaojie.test.thread.Processor#process(java.lang.String) */ public void process(String name) { System.out.println(String.format("%s開始處理,當前時間是%d", name, System.currentTimeMillis())); synchronized (this) { System.out.println(String.format("%s獲得鎖%s", name, this.toString())); try { System.out.println(String.format("%s開始sleep", name)); Thread.sleep(1000); System.out.println(String.format("%s結束sleep", name)); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(String.format("%s釋放鎖%s", name, this.toString())); System.out.println(String.format("%s結束處理,當前時間是%d", name, System.currentTimeMillis())); } }
2、執行緒獲取同步鎖,獲取失敗結束
適用場景:
a、定時任務,前一個處理執行緒未完成時,新執行緒不能獲取鎖則直接結束
java程式碼示例:
public class LockFailCloseProcessor implements Processor { private static Lock lock = new ReentrantLock(); /* (non-Javadoc) * @see com.sunhaojie.test.thread.Processor#process(java.lang.String) */ public void process(String name) { System.out.println(String.format("%s開始處理,當前時間是%d", name, System.currentTimeMillis())); if (lock.tryLock()) { System.out.println(String.format("%s獲得鎖%s", name, this.toString())); try { System.out.println(String.format("%s開始sleep", name)); Thread.sleep(1000); System.out.println(String.format("%s結束sleep", name)); } catch (InterruptedException e) { e.printStackTrace(); } lock.unlock(); System.out.println(String.format("%s釋放鎖%s", name, this.toString())); } else { System.out.println(String.format("%s沒有獲得鎖直接退出", name)); } System.out.println(String.format("%s結束處理,當前時間是%d", name, System.currentTimeMillis())); } }
3、執行緒獲取同步鎖後,因為其他資源不滿足暫時釋放同步鎖,等待喚醒
適用場景:
a、即使通訊中,傳送者獲取同步鎖發現佇列寫滿時,釋放鎖等待接收者讀取資料
java程式碼示例:
public class SynchronizedWaitWriteProcessor implements Processor { /** * 是否可讀標記,false:不可讀,可寫 true:可讀,不可寫 */ public static int maxSize = 5; public static List<String> content = new ArrayList<String>(); /* (non-Javadoc) * @see com.sunhaojie.test.thread.Processor#process(java.lang.String) */ public void process(String name) { System.out.println(String.format("%s開始處理,當前時間是%d", name, System.currentTimeMillis())); synchronized (content) { System.out.println(String.format("%s獲得鎖%s", name, this.toString())); try { if (content.size() == maxSize) { System.out.println( String.format("%s臨時釋放鎖%s", name, this.toString())); content.wait(); } System.out.println( String.format("%s開始寫入資訊", name)); Random random = new Random(); for (int i = 0; i < maxSize; i++) { content.add( String.format("寫入資訊%d", random.nextInt(1000))); } System.out.println( String.format("%s結束寫入資訊", name)); } catch (InterruptedException e) { e.printStackTrace(); } content.notify(); } System.out.println(String.format("%s釋放鎖%s", name, this.toString())); System.out.println(String.format("%s結束處理,當前時間是%d", name, System.currentTimeMillis())); } }
4、執行緒獲取同步鎖後,因為其他資源不滿足結束執行緒
適用場景:
a、即使通訊中,接收者獲取同步鎖發現佇列無資料時,釋放鎖結束執行緒
java程式碼示例:
public class SynchronizedWaitReadProcessor implements Processor { /* (non-Javadoc) * @see com.sunhaojie.test.thread.Processor#process(java.lang.String) */ public void process(String name) { System.out.println(String.format("%s開始處理,當前時間是%d", name, System.currentTimeMillis())); synchronized (SynchronizedWaitWriteProcessor.content) { System.out.println(String.format("%s獲得鎖%s", name, this.toString())); if (SynchronizedWaitWriteProcessor.content.size() != 0) { System.out.println( String.format("%s開始讀出資訊", name)); for (int i = 0; i < SynchronizedWaitWriteProcessor.content.size(); i++) { System.out.println("讀出資訊:" + SynchronizedWaitWriteProcessor.content.get(i)); } System.out.println( String.format("%s結束讀出資訊", name)); } SynchronizedWaitWriteProcessor.content.notify(); } System.out.println(String.format("%s釋放鎖%s", name, this.toString())); System.out.println(String.format("%s結束處理,當前時間是%d", name, System.currentTimeMillis())); } }
最後送上執行以上程式的main方法和Processor 介面類:
public interface Processor { public void process(String name); } public class ThreadTest { public static void main(String[] args) throws InterruptedException { //測試SynchronizedProcessor // Processor processor = new SynchronizedProcessor(); // for (int i = 0; i < 10; i++) { // ProcessorThread threadProcessor = new ProcessorThread("name" + i, processor); // threadProcessor.start(); // } //測試LockProcessor // Processor processor = new LockProcessor(); // for (int i = 0; i < 10; i++) { // ProcessorThread threadProcessor = new ProcessorThread("name" + i, processor); // threadProcessor.start(); // } // Processor processor = new LockFailCloseProcessor(); // for (int i = 0; i < 10; i++) { // ProcessorThread threadProcessor = new ProcessorThread("name" + i, // processor); // threadProcessor.start(); // } Processor readProcessor = new SynchronizedWaitReadProcessor(); ProcessorThread readThreadProcessor = new ProcessorThread("read", readProcessor); readThreadProcessor.start(); Processor writeProcessor = new SynchronizedWaitWriteProcessor(); ProcessorThread writeThreadProcessor = new ProcessorThread("write", writeProcessor); writeThreadProcessor.start(); Thread.sleep(100); ProcessorThread read2ThreadProcessor = new ProcessorThread("read2", readProcessor); read2ThreadProcessor.start(); } }
多執行緒可以大大提高效能,但是多執行緒的同步又增加了應用的複雜性,是否能平衡多執行緒的效能和複雜性是是否有高併發經驗的要求。
相關文章
- 非同步知多少非同步
- Java併發(9)- 從同步容器到併發容器Java
- 併發技術中同步
- dbms_lock同步併發
- Java同步容器和併發容器Java
- Java 8 併發:同步和鎖Java
- Java併發——AbstractQueuedSynchronizer(AQS)同步器JavaAQS
- 併發技術4:同步排程
- 併發工具類(二)同步屏障CyclicBarrier
- 聊聊併發(三)——同步輔助類
- 3.1 JDK併發包之同步控制JDK
- java併發程式設計:同步容器Java程式設計
- ♻️同步和非同步;並行和併發;阻塞和非阻塞非同步並行
- Java併發之同步器設計Java
- 併發程式設計(一)——同步類容器程式設計
- 併發-7-同步容器和ConcurrentHashMapHashMap
- 多執行緒與併發----Semaphere同步執行緒
- Java併發之CyclicBarrier(在集合點同步)Java
- Python開源非同步併發框架Python非同步框架
- 程式執行緒、同步非同步、阻塞非阻塞、併發並行執行緒非同步並行
- 併發-0-同步/非同步/阻塞/非阻塞/程式/執行緒非同步執行緒
- java併發程式設計——執行緒同步Java程式設計執行緒
- Java併發之AQS同步器學習JavaAQS
- 計數器方式實現非同步併發非同步
- 執行緒,程式,協程, 併發,並行,同步,非同步概念解析執行緒並行非同步
- 【OS】同步非同步/阻塞非阻塞、併發並行序列的區分非同步並行
- 釋出-訂閱方式實現非同步併發非同步
- Golang 併發,有快取通道,通道同步案例演示Golang快取
- 說一說併發設計模式—Future(非同步)設計模式非同步
- 併發容器J.U.C -- AQS同步元件(二)AQS元件
- golang 高併發非同步tcp 框架求推薦?Golang非同步TCP框架
- 併發程式設計-11.取消非同步工作程式設計非同步
- 併發程式設計:自定義併發類:自定義非同步流(釋出與訂閱)程式設計非同步
- 非同步化,你的高併發大殺器非同步
- java多執行緒詳解(併發,並行,同步)Java執行緒並行
- Python 開源非同步併發框架的未來Python非同步框架
- JVM 併發性: 使用 Akka 執行非同步操作JVM非同步
- Go高階併發 09 | 同步原語:sync 包讓你對併發控制得心應手Go