個人小結:簡單來說,CAS(Check and Set)是一個確保並發一致性的機制,屬於“樂觀鎖”範疇;原理很簡單:拿版本號碼,操作,對比版本號碼,如果一致就操作,不一致就放棄任何操作,操作後版本號碼加1。
聯想另外一個機制,與這個有點類似:防重複提交
1.makeToken:server產生一個token,並傳到頁面中,也就是版本號碼(只要不重新整理頁面,這個值都不變);
2.checkValid:提交時對比頁面傳來的token和server中的token,如果相同則提交,不同則放棄;
3.makeToken:更新server中的token。
Struts1與Struts2的原理類似,只不過前者需要開發人員手動調用相應函數,後者可以使用攔截器更簡潔的完成。
----------參考文章------------------
引用:http://langyu.iteye.com/blog/680052
什麼是CAS協議
Memcached於1.2.4版本新增CAS(Check and Set)協議類同於Java並發的CAS(Compare and Swap)原子操作,處理同一item被多個線程更改過程的並發問題。
在Memcached中,每個key關聯有一個64-bit長度的long型惟一數值,表示該key對應value的版本號碼。這個數值由Memcached server產生,從1開始,且同一Memcached server不會重複。在兩種情況下這個版本數值會加1:1、新增一個key-value對;2、對某已有key對應的value值更新成功。刪除item版本值不會減小。
例如
Java代碼
MemcachedClient client = new MemcachedClient();
client.set("fKey", "fValue");
//第一次set, 在Memcached server中會維護fKey對應的value的版本號碼,假設是548;
client.set("fKey", "sValue");
//再次set,則這個fKey對應的value的版本號碼變為549;
CASValue casValue = client.gets("fKey");
//這樣就可以得到對應key的cas版本號碼和實際value(各個Memcached client都有類似的對象表示,名字可能不一樣,但效果類同),如 casValue.getValue = "sValue",casValue.getCas=549;
CAS協議解決的問題
類比多個Memcached client並發set同一個key的情境。如clientA想把當前key的value set為"x",且操作成功;clientB卻把當前key的value值由"x"覆蓋set為"y",這時clientA再根據key去取value時得到"y"而不是期望的"x",它使用這個值,但不知道這個值已經被其它線程修改過,就可能會出現問題。
CAS協議解決這種並發修改問題。有線程試圖修改當前key-value對的value時,先由gets方法得到item的版本號碼,操作完成提交資料時,使用cas方法謹慎變更,如果在本地對item操作過程中這個key-value對在Memcached server端被其它線程更改過,就放棄此次修改(樂觀鎖概念)。
Java代碼
CASValue casValue = client.gets(key);
//*****
//本地的各種處理
//*****
CASResponse response = client.cas(key, newValue, casValue);
//在我取資料時item的版本號碼是casValue.getCas(),所以提交時我期望item的版本號碼是沒有改變過的。如果被修改過,不是我取資料時的版本號碼,那麼Memcached server對這次提交什麼也不做,返回true或false由使用者自己來提出解決方案(什麼也不做或是重新擷取版本號碼,再次重試提交等)
並發環境下的正確性驗證
用多個Memcached client並發更改同一個key值,將value遞增,如果 操作次數-CAS失敗次數 = value增加的值,表示並發環境下CAS處理沒有問題。
Java代碼
import java.io.IOException;
import java.net.InetSocketAddress;
import net.spy.memcached.CASResponse;
import net.spy.memcached.CASValue;
import net.spy.memcached.MemcachedClient;
public class CASTest {
private static MemcachedClient client = null;
static {
try {
client = new MemcachedClient(
new InetSocketAddress("localhost", 11211));
} catch (IOException o) {
o.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
//Firstly, the key should exist.
//key is "number", value is Integer 1, 7845 is expire time
client.set("number", 7845, 1);
CASTest testObj = new CASTest();
//start the multithread environment
for (int i = 0; i < 10; i++) {
testObj.new ThreadTest("Thread-" + (i + 1)).start();
}
}
/**
* Each thread runs many times
*/
private class ThreadTest extends Thread {
private MemcachedClient client = null;
ThreadTest(String name) throws IOException {
super(name);
client = new MemcachedClient(
new InetSocketAddress("localhost", 11211));
}
public void run() {
int i = 0;
int success = 0;
while (i < 10) {
i++;
CASValue<Object> uniqueValue =client.gets("number");
CASResponse response = client.cas("number",
uniqueValue.getCas(), (Integer)uniqueValue.getValue() + 1);
if (response.toString().equals("OK")) {
success++;
}
System.out.println(Thread.currentThread().getName() + " " + i
+ " time " + " update oldValue : " + uniqueValue
+ " , result : " + response);
}
if (success < 10) {
System.out.println(Thread.currentThread().getName()
+ " unsuccessful times : " + (10 - success ));
}
}
}
}
每次執行的結果都會不一樣,如其中某次的執行結果為: 總共操作100次,衝突47次,且最後value由1漲到53,那麼表示驗證成功。
不足之處,誠請提出!