1. 程式人生 > >對多執行緒對訂單更新競爭的鎖機制的實現-初構

對多執行緒對訂單更新競爭的鎖機制的實現-初構

做支付寶支付接入時遇到一個問題,對同一個訂單,可能同時存在”支付寶伺服器非同步通知支付結果更新狀態“和“使用者主動要求發起查詢支付結果更新狀態"兩個過程。因為在查詢要使用者已支付訂單完成後要向其它系統發起交易請求,所以要求對同一訂單,兩個過程執行緒必須在更新時是同步執行的。但如果不區分的對整個更新方法加鎖,則併發量又上不去。綜合考慮,我決定自行實現一個鎖機制,要求加鎖後的更新方法做到以下要求:1、對不同的訂單,執行更新方法的執行緒是非同步執行的;2、對相同的訂單,執行更新方法的執行緒是同步執行的。

初步的程式碼實現如下:

import java.util.HashMap;

/**
 * 訂單鎖,當兩個執行緒同時操作同一個oderNo訂單時,進入臨界區,不同的oderNo不涉及臨界區操作.<br>
 * <p>Long order=1234l;<br>
 * try{<br>
 * 	OrderLock.lock(oderNo);<br>
 *  同步程式碼執行……<br>
 * }finally{<br>
 * 	OrderLock.unlock();<br>
 * }</p>
 * <br>
 *
 */
public class OrderLock {
	
	private final static HashMap<Long,ObjectLocker<String>> LockerStore=new HashMap<Long,ObjectLocker<String>>();
	
	private final static Object lock=new Object();
	
	/**
	 * 嘗試進入訂單號臨界區,其它執行緒若在其後呼叫此訂單號進入,則等待此執行緒釋放鎖後才能進入臨界區
	 * @param oderNo 訂單號,若已有其它執行緒使用相同的訂單號呼叫此方法,則需要等待其它執行緒呼叫過unlock方法後才能進入臨界區
	 */
	public static void lock(String oderNo){
		ObjectLocker<String> objLoc=null;
		synchronized (lock) {
			for(Long id : LockerStore.keySet()){
				if(LockerStore.get(id).equals(oderNo)){
					objLoc=LockerStore.get(id);
				}
			}
			if(objLoc==null){
				objLoc=new ObjectLocker<String>(oderNo);
			}
			LockerStore.put(Thread.currentThread().getId(), objLoc);
		}
		objLoc.lock();
	}
	
	/**
	 * 解除鎖,允許其它執行緒進入訂單臨界區
	 */
	public static void unlock(){
		ObjectLocker<String> objLoc=null;
		synchronized (lock) {
			objLoc=LockerStore.remove(Thread.currentThread().getId());
		}
		if(objLoc!=null){
			objLoc.unlock();
		}
	}
	
	/**
	 * 物件鎖
	 *
	 * @param <T>
	 */
	static class ObjectLocker<T> {

		private final Object locker = new Object();

		private final static long nullThread = -1;

		private long threadId = nullThread;
		
		private int requestCount=0;

		private T target;

		public ObjectLocker(T target) {
			super();
			this.target = target;
		}

		@Override
		public boolean equals(Object obj) {
			if(target==null){
				return super.equals(obj);
			}else{
				return target.equals(obj);
			}
			
		}
		public void lock() {
			synchronized (locker) {
				requestCount++;
				do {
					long currThread=Thread.currentThread().getId();
					if (currThread == threadId || threadId == nullThread) {
						threadId = currThread;
						break;
					}
					try {
						locker.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				} while (true);

			}
		}

		public void unlock() {
			synchronized (locker) {
				requestCount--;
				if (Thread.currentThread().getId() == threadId) {
					threadId = nullThread;
					locker.notifyAll();
				}
			}
		}
		
		public int getReqeustCount(){
			return requestCount;
		}

	}
}


初步的測試程式碼

import java.util.ArrayList;
import java.util.HashMap;
public class LockTest {
	
	static HashMap<Long,ArrayList<Long>> cache=new HashMap<Long,ArrayList<Long>>();

	static class TestThread extends Thread{
		
		Long orderNo;
		
		public TestThread(Long orderNo) {
			super();
			this.orderNo = orderNo;
		}

		@Override
		public void run() {
			try {
				sleep(2000l);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			for(int i=0;i<60;++i){
				try{
				//	System.out.println("執行緒"+getId()+"等待鎖"+orderNo+"……");
					OrderLock.lock(orderNo.toString());
					cache.get(orderNo).add(getId());
						try {
							sleep(20l);
						} catch (InterruptedException e) {}
				
			//		System.out.println("執行緒"+getId()+"獲得鎖"+orderNo+"……");		
				} finally{
					cache.get(orderNo).add(getId());
					OrderLock.unlock();
				}
		//		System.out.println("執行緒"+getId()+"釋放鎖"+orderNo+"……");
				try {
					sleep(20l);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	public static void main(String[] args) {
		long time0=System.currentTimeMillis();
		TestThread[] ts=new TestThread[24];
		cache.put(0l, new ArrayList<Long>());
		cache.put(1l, new ArrayList<Long>());
		cache.put(2l, new ArrayList<Long>());
		cache.put(3l, new ArrayList<Long>());
		for(int i=0;i<ts.length;++i){
			ts[i]=new TestThread(i%4l);
			ts[i].start();
		}
		for(int i=0;i<ts.length;++i){
			try {
				ts[i].join();
			} catch (InterruptedException e) {}
		}
		System.out.println(System.currentTimeMillis()-time0);
		for(Long key : cache.keySet()){
			ArrayList<Long> l=cache.get(key);
			int len=l.size()/2;
			for(int i=0;i<len;i++){
				if(!l.get(2*i).equals(l.get(2*i+1))){
					System.out.println(2*i);
					System.out.println(l.get(2*i));
					System.out.println(l.get(2*i+1));
					System.out.println(list2String(l));
					throw new RuntimeException("出現錯誤");
				}
			}
		//	System.out.println(list2String(l));
		}
	}
	static String list2String(ArrayList<Long> l){
		String tmp="";
		for(Long i : l){
			tmp+=i+",";
		}
		return tmp;
	}
}


相關推薦

執行訂單更新競爭機制實現-

做支付寶支付接入時遇到一個問題,對同一個訂單,可能同時存在”支付寶伺服器非同步通知支付結果更新狀態“和“使用者主動要求發起查詢支付結果更新狀態"兩個過程。因為在查詢要使用者已支付訂單完成後要向其它系統發起交易請求,所以要求對同一訂單,兩個過程執行緒必須在更新時是同步執行的。

執行(五): CAS無機制

java程式設計規範中long與double操作並不是原子的,在java的部分執行環境中,對於long以及double的操作並不是原子的。 例如有一個long型別的longfield欄位,某個執行緒正在執行:longfield = 123L ;這樣的指定操作,而同時有另一個執行緒正在

執行之使用讀寫ReentrantReadWriteLock實現快取系統

簡單地快取系統:當有執行緒來取資料時,如果該資料存在我的記憶體中,我就返回資料;如果不存在我的快取系統中,那麼就去查資料庫,返回資料的同時儲存在我的快取中。 其中涉及到讀寫問題:當多個執行緒執行讀操作時(都加讀鎖),如果有資料返回;如果沒有資料時,則讓第一個讀的執行緒,進行

用模板統一執行變數的使用

有時候需要定義全域性變數供多個執行緒使用,為了對變數進行保護,需要對其進行加鎖的操作 如果全域性變數較多,每個變數都進行加鎖的操作,會導致程式碼繁瑣混亂 使用類模板,可以將加鎖/解鎖的操作統一 #include <pthread.h> #include <s

Java--如何使用執行一個HashSet進行平行計算

這段時間工作比較忙。今天抽空整理了一個多執行緒使用場景。 當處理一個數據量比較大的集合時(每個元素的計算都耗時比較長)。由於只使用一個執行緒進行計算比較慢。所以想到多跑幾個執行緒進行處理。 1.每個執行緒可以自行計算要處理集合的開始和結束索引,確保每一個元素都被計算的到。

精選執行面試題目和答案,執行理解不是很到位的快來看了~

多執行緒,相信對於很多小白來說是噩夢一般的東西吧,別怕,接下來我們就來把多執行緒的一些面試題一一解讀,希望讀完這篇文章之後,能夠對多執行緒有一個更深入的瞭解。 1. 多執行緒使用的優缺點? 優點: (1)多執行緒技術使程式的響應速度更快 (2)當前沒有進行處理的任務可以將處理器時間讓

執行的理解和分類

一、多執行緒的定義和使用資訊: 多執行緒是一個比較輕量級的方法來實現單個應用程式內多個程式碼執行路徑 在系統級別內,程式並排執行,程式分配到每個程式的執行時間是基於該程式的所需時間和其他程式的所需時間來決定的。 然而,在每個程式內部,存在一個或者多個執行執行緒,它同

談談你執行的理解

執行緒是由一個主執行緒和很多個子執行緒組成的,主執行緒消失,子執行緒也會消失,但是子執行緒消失其中一個主執行緒不會消失執行緒的生命週期分為5個步驟像人的一生一樣,這5個步驟分別對應了5個方法新生-->啟動-->執行-->阻塞-->銷燬繼承Thread類

System.out,println執行的影響,以及主記憶體與工作記憶體的同步

先說一下之前對System.out.println的誤會先舉個例子package com.yigjn.Thread; public class MyThread extends Thread { private int count = 0; @Override p

筆記:執行技術GCD的簡單總結

提到 GCD 首先應該明白六個概念:序列佇列(DISPATCH_QUEUE_SERIAL)、並行佇列(DISPATCH_QUEUE_CONCURRENT)、同步執行(dispatch_sync)、非同步執行(dispatch_async)、 全域性佇列和主佇列,今天主要是先總結一下前四個概

java執行:3、Java執行的支援1

宣告:本教程不收取任何費用,歡迎轉載,尊重作者勞動成果,不得用於商業用途,侵權必究!!! 文章目錄 1、Java在語言級提供了對多執行緒程式設計的支援。 2、實現多執行緒程式的兩種方式 3、後臺執行緒 4、yield方法 5、執行緒優先順序 1、Java在語言級提

談談執行的看法——隨著瞭解的加深而不斷補充和修正

1、多執行緒的建立一共有三種方式,一是繼承 Thread 類,二是實現 Runnable 介面,然後藉助 Thread (Target) 構造方法進行多執行緒的例項化,都需要覆蓋重寫內部的 run() 方法,使用start()方法呼叫,這兩種方式都是沒有返回結果的。還有第三種

java執行:5、Java執行的支援(二)執行優先順序

一、執行緒優先順序 在java當中,每一個執行緒都有一個優先順序,我們可以通過Thread當中的getPriority()方法、setPriority方法去得到一個執行緒的優先順序和設定一個執行緒的優先順序。  設定執行緒優先順序,它的引數是一個整形。最小為1(Thread.M

java執行:4、Java執行的支援(二)後臺執行setDaemon、暫停執行yield

文章目錄 一、設定後臺執行緒 二、yield方法,暫停執行緒讓別的執行緒執行 上篇文章介紹了執行緒實現方式、執行緒的隨機性,大家如需瞭解可參考 java多執行緒:3、Java對多執行緒的支援(一)執行緒實現方式、執行緒的隨機性 這篇我們來看看後臺執行緒setD

.net 4.0 中執行新特性(一)

      在.net 40中對多執行緒的處理增加了很多新的類以方便多執行緒環境下的程式設計實現,首先需要了解的是兩個非常有用的類Lazy<T>和ThreadLazy<T>,通過這兩個類我們可以很方便實現一個單例模式而不用考慮太多的執行緒安全的問題。

【原】執行中Wait和Join的理解

對於,wait方法的解釋,有時候覺得很矛盾。呼叫某個物件的wait時,需要獲得該物件的鎖,在執行的時候又需要釋放該物件的所有鎖。這是問題一。 另外,在看Join的原始碼,會發現它利用了Wait來實現,但是它的實現原理是怎樣的呢? 這是問題二。 看下原始碼的英文描述:

面試系列 | 簡單談談你執行併發使用時的一些優化經驗?

首先加鎖會帶來效能上的損壞,但是加鎖本身不會帶來多少效能消耗,效能消耗主要是在獲取鎖的過程。如果只有一個執行緒競爭鎖,此時並不存在多執行緒競爭的情況,那麼 JVM 會進行優化,這時加鎖帶來的效能消耗基本可以忽略。因此,優化鎖的使用可以避免不必要的執行緒競爭,不僅

學習筆記——執行(持續更新中)

1、程序與執行緒的區別:程序是所有執行緒的集合,每一個執行緒是程序中的執行路徑。      根據我的理解,其實程序就可以看成是公共廁所,執行緒看做是廁所裡的隔斷間,一個廁所可以有很多個隔斷間,也可以有一個隔斷間。當人們上廁所的時候,如果廁所只有一個隔斷間上廁所的只有一個人,

RabbitMQ C++客戶端 RabbitMQ Client for Windows 超簡單接收發送介面(執行版)-最近更新2018-05-21

該庫特點:該介面本身不支援多執行緒,也就是說多個執行緒不能同時訪問一個介面物件。支援多執行緒:但是,由於該介面是值語義,所以在多執行緒場景中使用沒有任何問題。下面的例子就展示了多執行緒環境下程式的Demo和結果。要完成的功能:1 A執行緒傳送0-99這一百個整數到Rabbit

Android開發之執行環境下更新介面

Android應用程式的介面運行於獨立的執行緒裡。但有時候軟體需要單獨的執行緒來處理資料,然後再更新介面。這樣能夠保證介面執行的流暢又不至於影響使用者體驗。這裡的問題在於,UI只能被介面執行緒更新,在多執行緒環境下回出錯。本文會展示這種典型的錯誤,以及解決方案。 下面以計時