Java併發程式設計中的設計模式解析(一)
Java併發程式設計,除了被用於各種Web應用、分散式系統和大資料系統,構成高併發系統的核心基礎外,其本身也蘊含著大量的設計模式思想在裡面。這一系列文章主要是結合Java原始碼,對併發程式設計中使用到的、實現的各類設計模式做歸納總結,以便進一步沉澱對Java併發設計的理解。
模板設計模式
Thread類中run和start方法,就是一個典型的模板設計模式的實現,即:父類定義演算法邏輯程式碼,子類實現其細節。
1 public synchronized void start() { 2 /** 3 * 執行緒物件新建後的New狀態,其內部thereadStatus屬性為0 4 */ 5 if (threadStatus != 0) 6 throw new IllegalThreadStateException(); 7 8 /* 同時會被新增到一個ThreadGroup */ 9 group.add(this); 10 11 boolean started = false; 12 //呼叫JNI方法start0()來啟動執行緒 13 try { 14 start0(); 15 started = true; 16 } finally { 17 //執行緒結束之後,再次啟動將丟擲異常 18 try { 19 if (!started) { 20 group.threadStartFailed(this); 21 } 22 } catch (Throwable ignore) { 23 /* do nothing. If start0 threw a Throwable then 24 it will be passed up the call stack */ 25 } 26 } 27 }
下面以一個例子演示模板模式:
public class TemplateMethod { //相當於Thread類的start方法, 用final修飾避免被更改 public final void print(String message) { System.out.println("-------------"); wrapPrint(message); System.out.println("-------------"); } //相當於Thread的run方法, 用protected修飾限於子類重寫 protected void wrapPrint(String message) { } public static void main(String[] args) { //通過匿名內部子類, 重寫父類的wrapPrint方法, 從而實現不同的輸出模板 TemplateMethod t1 = new TemplateMethod() { @Override protected void wrapPrint(String message) { System.out.println("111" + message + "111"); } }; t1.print("Hello World!"); TemplateMethod t2 = new TemplateMethod() { @Override protected void wrapPrint(String message) { System.out.println("222" + message + "222"); } }; t2.print("Hello World!"); } }
策略模式
建立Java多執行緒中,實現Runnable介面作為Target並傳入Thread類的構造方法來生成執行緒物件的過程,就體現了GoF中策略模式的設計思想。下面是一個簡單的示例:
首先,仿照Runnable介面的思想,定義一個用於處理資料庫行的介面
1 /* 2 * RowHandler定義了對資料庫查詢返回結果操作的方法, 具體實現需要 3 * 實現類完成, 類似於Runnable介面 4 */ 5 public interface RowHandler<T> { 6 T handle(ResultSet rs); 7 }
然後,仿照Thread方法,定義資料庫查詢的工作類
1 public class RecordQuery { 2 3 private final Connection connection; 4 5 public RecordQuery(Connection connection) { 6 this.connection = connection; 7 } 8 //方法中傳入RowHandler的實現類 9 public <T> T query(RowHandler<T> handler, String sql, Object... params) throws SQLException { 10 PreparedStatement stmt; 11 ResultSet resultSet; 12 stmt = connection.prepareStatement(sql); 13 int index = 1; 14 for (Object param : params) { 15 stmt.setObject(index++, param); 16 } 17 resultSet = stmt.executeQuery(); 18 //呼叫實現類的handle方法來處理資料 19 return handler.handle(resultSet); 20 } 21 }
生產者-消費者模式
生產者-消費者模式是使用Java併發程式設計通訊所實現的經典模式之一。該模式是通過佇列這一資料結構來儲存物件元素,由多執行緒分別充當生產者和消費者,生產者不斷生成元素、消費者不斷消費元素的過程。下面通過程式碼來演示:
實現一個帶有入隊和出隊的佇列
1 /* 2 * 通過一個生產者-消費者佇列來說明執行緒通訊的基本使用方法 3 */ 4 public class EventQueue { 5 //定義一個佇列元素數量, 一旦賦值則不可更改 6 private final int max; 7 //定義一個空的內部類, 代表儲存元素 8 static class Event{ } 9 //定義一個不可改的連結串列集合, 作為佇列載體 10 private final LinkedList<Event> eventQueue = new LinkedList<>(); 11 //如果不指定初始容量, 則容量預設為10 12 private final static int DEFAULT_MAX_EVENT = 10; 13 //使用自定義容量初始化佇列 14 public EventQueue(int max) { 15 this.max = max; 16 } 17 //如果不指定初始容量, 則容量預設為10 18 public EventQueue() { 19 this(DEFAULT_MAX_EVENT); 20 } 21 //封裝一個輸出到控制檯的方法 22 private void console(String message) { 23 System.out.printf("%s:%s ",Thread.currentThread().getName(), message); 24 } 25 //定義入隊方法 26 public void offer(Event event) { 27 //使用連結串列物件作為鎖, 通過synchronized程式碼塊實現同步 28 synchronized(eventQueue) { 29 //在迴圈中判斷如果佇列已滿, 則呼叫鎖的wait方法, 使生產者執行緒阻塞 30 while(eventQueue.size() >= max) { 31 try { 32 console(" the queue is full"); 33 eventQueue.wait(); 34 } catch (InterruptedException e) { 35 e.printStackTrace(); 36 } 37 } 38 console(" the new event is submitted"); 39 eventQueue.addLast(event); 40 //喚醒所有等待中的消費者;注意如果此處使用notify(),可能導致執行緒不安全 41 this.eventQueue.notifyAll(); 42 } 43 } 44 //定義出隊方法 45 public Event take() { 46 //使用連結串列物件作為鎖 47 synchronized(eventQueue) { 48 //在迴圈中判斷如果佇列已空, 則呼叫鎖的wait方法, 使消費者執行緒阻塞 49 while(eventQueue.isEmpty()) { 50 try { 51 console(" the queue is empty."); 52 eventQueue.wait(); 53 } catch (InterruptedException e) { 54 e.printStackTrace(); 55 } 56 } 57 Event event = eventQueue.removeFirst(); 58 //喚醒所有等待中的生產者;注意如果此處使用notify(),可能導致執行緒不安全 59 this.eventQueue.notifyAll(); 60 console(" the event " + event + " is handled/taked."); 61 return event; 62 } 63 } 64 }
驗證該佇列的類
1 /* 2 * producer/client pattern 3 */ 4 public class EventClient { 5 6 public static void main(String[] args) { 7 //定義不可變佇列例項 8 final EventQueue eventQueue = new EventQueue(); 9 //新建生產者執行緒, 可以設定多個 10 new Thread(()->{ 11 while(true) { 12 eventQueue.offer(new EventQueue.Event()); 13 } 14 }, "producer").start(); 15 //新建消費者執行緒, 可以設定多個 16 new Thread(()->{ 17 while(true) { 18 eventQueue.take(); 19 try { 20 TimeUnit.MILLISECONDS.sleep(10); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 } 25 }, "consumer").start(); 26 } 27 }
相關文章
- Java 併發程式設計解析Java程式設計
- Java併發程式設計中的設計模式解析(二)一個單例的七種寫法Java程式設計設計模式單例
- Java併發程式設計---java規範與模式下的併發程式設計1.1Java程式設計模式
- java併發程式設計系列:java併發程式設計背景知識Java程式設計
- 併發程式設計-ExecutorCompletionService解析程式設計
- java 併發程式設計Java程式設計
- Java併發程式設計Java程式設計
- 【Java併發程式設計】一、為什麼需要學習併發程式設計?Java程式設計
- Java併發程式設計:volatile關鍵字解析Java程式設計
- Java併發程式設計 - 第十一章 Java併發程式設計實踐Java程式設計
- 併發程式設計之死鎖解析程式設計
- Java 併發程式設計(十) -- ReentrantLock中的SyncJava程式設計ReentrantLock
- Java 併發程式設計(六) -- ThreadPoolExecutor中的WorkerJava程式設計thread
- Java 併發程式設計(八) -- AbstractQueuedSynchronizer中的NodeJava程式設計
- Java併發程式設計—ThreadLocalJava程式設計thread
- Java併發程式設計:synchronizedJava程式設計synchronized
- Java併發程式設計 -- ThreadLocalJava程式設計thread
- Java併發程式設計 -- ConditionJava程式設計
- Java併發程式設計——ThreadLocalJava程式設計thread
- java-併發程式設計Java程式設計
- Java併發程式設計-CASJava程式設計
- Java併發程式設計:LockJava程式設計
- Java併發設計模式--不可變模式(immutable)Java設計模式
- Java之併發程式設計:volatile關鍵字解析Java程式設計
- 實戰Java高併發程式設計模式視訊Java程式設計設計模式
- Java併發程式設計(一)Thread詳解Java程式設計thread
- 一個故事搞懂Java併發程式設計Java程式設計
- Java 併發程式設計(一) → LockSupport 詳解Java程式設計
- Java併發程式設計的藝術,解讀併發程式設計的優缺點Java程式設計
- .NET 中的併發程式設計程式設計
- Java併發程式設計ForkJoin的DemoJava程式設計
- Java併發程式設計的藝術(五)——中斷Java程式設計
- 高併發程式設計-AQS深入解析程式設計AQS
- java面試-Java併發程式設計(五)——中斷Java面試程式設計
- 【併發程式設計】Future模式及JDK中的實現程式設計模式JDK
- Java併發程式設計之synchronizedJava程式設計synchronized
- Java併發程式設計藝術Java程式設計
- Java併發程式設計實踐Java程式設計