根據不同的場景來選擇不同的方式,一下案例只保證數據一致性.....具體的要根據具體的應用場景來分析
1.分佈式鎖常用的幾種實現
在分析這幾種實現方案之前我們先來想一下,我們需要的分佈式鎖應該是怎麼樣的?(這裏以方法鎖爲例,資源鎖同理)
可以保證在分佈式部署的應用集羣中,同一個方法在同一時間只能被一臺機器上的一個線程執行。
這把鎖要是一把可重入鎖(避免死鎖)
這把鎖最好是一把阻塞鎖(根據業務需求考慮要不要這條)
有高可用的獲取鎖和釋放鎖功能
獲取鎖和釋放鎖的性能要好
什麼是可重入鎖和不可重入鎖?
https://www.cnblogs.com/dj3839/p/6580765.html 懶得寫了...這篇寫的很清晰
2.分佈式鎖的具體實現
2.1基於數據庫的分佈式鎖
在數據庫里加一個唯一約束字段,每次新增的時候去驗證是否是否成功,失敗在做異常處理。
可以在數據庫裏新建一個字段存放當前節點的名稱和線程名用來保證可重入性。
2.2 基於zookeeper的分佈式鎖實現
可以使用JavaApi實現,可以使用zkClient實現,可以使用curator實現,curator的功能十分強大,封裝了很多應用場景中常見的例子,例如master選舉和分佈式鎖。
2.21 基於zkClient的實現
ZkClient工具類
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
public class ZookeeperClient {
private final static String CONNECTSTRING="192.168.154.130:2181,192.168.154.130:2182,192.168.154.130:2183" ;
private static int sessionTimeout=5000;
//獲取連接
public static ZooKeeper getInstance() throws IOException, InterruptedException {
final CountDownLatch conectStatus=new CountDownLatch(1);
ZooKeeper zooKeeper=new ZooKeeper(CONNECTSTRING, sessionTimeout, new Watcher() {
public void process(WatchedEvent event) {
if(event.getState()== Event.KeeperState.SyncConnected){
conectStatus.countDown();
}
}
});
conectStatus.await();
return zooKeeper;
}
public static int getSessionTimeout() {
return sessionTimeout;
}
}
zkClient分佈式鎖的實現
import org.apache.zookeeper.*;
import java.io.IOException;
import java.util.List;
import java.util.Random;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
public class DistributeLock {
//根節點
private static final String ROOT_LOCK="/Locks2";
private ZooKeeper zooKeeper;
private int SessionTimeOut;
private static final byte[] data={1,2};
private String LockId;//記錄鎖的Id
private CountDownLatch countDownLatch = new CountDownLatch(1);
public DistributeLock(){
try {
this.zooKeeper = ZookeeperClient.getInstance();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
this.SessionTimeOut = ZookeeperClient.getSessionTimeOut();
}
//獲取鎖的方法
public boolean lock(){
try {
//創建一個臨時有序節點
LockId = zooKeeper.create(ROOT_LOCK+"/",data,ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(Thread.currentThread().getName()+"成功的創建了lockId:"+LockId);
//獲取根節點所有子節點
List<String> rootNodes = zooKeeper.getChildren(ROOT_LOCK,true);
//排序
SortedSet<String> sortedSet = new TreeSet();
rootNodes.forEach(s -> sortedSet.add(ROOT_LOCK+"/"+s));
String first = sortedSet.first();
//成功獲取鎖
if(LockId.equals(first)){
System.out.println(Thread.currentThread().getName()+"->我拿到了鎖->lock:"+LockId);
return true;
}
//獲取比它小的節點
SortedSet<String> lessSortedSet = sortedSet.headSet(LockId);
CountDownLatch countDownLatch1 = new CountDownLatch(1);
if(!lessSortedSet.isEmpty()){
String prevLockId = lessSortedSet.last();//拿到比當前LOCKID這個節點更小的上一個節點
zooKeeper.exists(prevLockId, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if(watchedEvent.getType()==Event.EventType.NodeDeleted){
countDownLatch1.countDown();
}
}
});
countDownLatch1.await(SessionTimeOut,TimeUnit.MILLISECONDS);
//上面節點刪除或者釋放
System.out.println(Thread.currentThread().getName()+"成功獲取鎖:"+LockId);
}
return true;
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
public boolean unlock(){
System.out.println(Thread.currentThread().getName()+"-->開始釋放鎖:"+LockId);
try {
zooKeeper.delete(LockId,-1);
System.out.println("節點:"+LockId+"成功刪除");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
return false;
}
}
測試,測試是測的多線程環境,實際應該在多進程環境 ,可以啓動多次來進行模擬。
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Unit test for simple App.
*/
public class AppTest
{
public static int count = 0;
public static CountDownLatch countDownLatch = new CountDownLatch(100);
public static void main(String[] args) throws InterruptedException {
for (int i=0;i<100;i++){
DistributeLock distributeLock = new DistributeLock();
Thread thread = new Thread(){
@Override
public void run() {
try {
// distributeLock.lock();
TimeUnit.SECONDS.sleep(1);
for(int y=0;y<200;y++){
count++;
}
/* System.out.println(Thread.currentThread().toString());*/
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
// distributeLock.unlock();
}
countDownLatch.countDown();
}
};
thread.start();
}
countDownLatch.await();
System.out.println("結束了-->"+count);
}
}
2.22基於curator的實現
基於curator的就更簡單了....推薦看這裏---->https://www.jianshu.com/p/31335efec309
代碼寫的非常詳細,對源碼也進行了剖析,以下是我的測試.
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
public class CuratorLocks {
private static CuratorFramework curatorFramework ;
private static int sessionTimeout=5000;
private static final String CONNECTSTRING = "192.168.216.128:2180,192.168.216.128:2181,192.168.216.128:2182";
public static InterProcessMutex getInstance(){
CuratorFramework curatorFramework = CuratorFrameworkFactory.
newClient(CONNECTSTRING,5000,5000,new ExponentialBackoffRetry(1000,3));
curatorFramework.start();
return new InterProcessMutex(curatorFramework,"/locksCurator");
}
}
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMultiLock;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* 〈一句話功能簡述〉<br>
* 〈〉
*
* @author Administrator
* @create 2019/2/22
* @since 1.0.0
*/
public class CuratorTest {
public static int count = 0;
public static CountDownLatch countDownLatch = new CountDownLatch(100);
public static void main(String[] args) throws InterruptedException {
for (int i=0;i<100;i++){
CuratorLocks curatorLocks = new CuratorLocks();
final InterProcessLock interProcessLock = CuratorLocks.getInstance();
Thread thread = new Thread(){
@Override
public void run() {
// distributeLock.lock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
interProcessLock.acquire();
for(int y=0;y<2000;y++){
count++;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
interProcessLock.release();
} catch (Exception e) {
e.printStackTrace();
}
}
countDownLatch.countDown();
}
};
thread.start();
}
countDownLatch.await();
System.out.println("結束了-->"+count);
}
}
2.3基於redis的實現
慢慢在更.....