在單進程的系統中,當存在多個線程能夠同時改變某個變量(可變共享變量)時,就須要對變量或代碼塊作同步,使其在修改這種變量時可以線性執行消除併發修改變量。java
而同步的本質是經過鎖來實現的。爲了實現多個線程在一個時刻同一個代碼塊只能有一個線程可執行,那麼須要在某個地方作個標記,這個標記必須每一個線程都能看到,當標記不存在時能夠設置該標記,其他後續線程發現已經有標記了則等待擁有標記的線程結束同步代碼塊取消標記後再去嘗試設置標記。這個標記能夠理解爲鎖。linux
不一樣地方實現鎖的方式也不同,只要能知足全部線程都能看獲得標記便可。如java中synchronize是在對象頭設置標記,Lock接口的實現類基本上都只是某一個volitile修飾的int型變量其保證滅個線程都能擁有對該int的可見性和原子修改,linux內核中也是利用互斥量或信號量等內存數據作標記。數據庫
除了利用內存數據作鎖其實任何互斥的都能作鎖(只考慮互斥狀況),如流水錶中流水號與時間結合作冪等校驗能夠看做是一個不會釋放的鎖,或者使用某個文件是否存在做爲鎖等。只須要知足在對標記進行修改能保證原子性和內存可見性便可。安全
分佈式鎖 當在分佈式模型下,數據只有一份(或有限制),此時須要利用鎖的技術控制某一時刻修改數據的進程數。 與單機模式下的鎖不只須要保證進程可見,還須要考慮進程與鎖之間的網絡問題。(我以爲分佈式狀況下之因此問題變得複雜,主要就是須要考慮到網絡的延時和不可靠。。。一個大坑) 分佈式鎖仍是能夠將標記存在內存,只是該內存不是某個進程分配的內存而是公共內存如Redis、Memcache。至於利用數據庫、文件等作鎖與單機的實現是同樣的,只要保證標記能互斥就行。
通常狀況下直接用setnx加expire就夠了,但從安全性的角度看仍是存在一下幾個問題:網絡
Redlock是Redis的做者antirez給出的集羣模式的Redis分佈式鎖,它基於N個徹底獨立的Redis節點(一般狀況下N能夠設置成5)。併發
當有5個節點,某次上鎖對a,b,c三個節點上鎖成功,然後c立刻down了,此時還沒經過AOF或RDB寫入磁盤。然後c又立刻恢復,此時c沒有上鎖數據,所以此時可能出現c,d,e三個節點被別的進程上鎖。因此在節點恢復時應該延時起碼一個鎖的過時時間。分佈式