您的位置:首页 > 产品设计 > UI/UE

自己动手写带有事务支持的分布式Key-Value存储系统——锁管理器

2014-03-27 08:59 567 查看
对于锁事务的实现,里面需要使用大量的读写锁,大量线程同步地对数据加锁,难免会产生死锁,所以锁管理器不仅需要管理所有分配的锁,还要能够自动检测出死锁,并且主动解除死锁状态。

死锁检测的原理:

死锁检测方法中通常使用等待图 WFG(Wait-For Graph)作为表达事务间等待关系的数学模型。在 WFG 中,结点 ti

表示事务,边(ti,tj)表示事务 ti等待 tj,该边在 ti申请对 tj现持有的资源 sr 加锁时建立,在 tj释放其在资源 sr 上的锁后删除。

当 WFG 中出现环时,表明系统中存在死锁。对应于每个事务(因为本系统是基于线程的,每一个事务使用一个线程,所以这里的

事务可以和线程等价),如果事务想要加的读写锁和另一个事务中的所占用的锁互斥,则存在一条有向辺,遍历整个事务,就可以建立一个全局的WFG,然后通过检测

有向图中的环,就可以获得死锁的事务集合。

有向图的环的检测

递归删除入度为0的顶点,直到不存在入度为0的顶点,剩下的就是顶点就组成有向环。具体证明查见《图论》

具体实现:代码(点击打开链接)

有向图(不完整,只是用来实现环的检测,算法的复杂度n^2):Github中查看.寻找环的函数为findRings()

/**
* Alipay.com Inc.
* Copyright (c) 2004-2014 All Rights Reserved.
*/
package org.footoo.common.datastruct.graphic;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
* 有向图
* <ul>
*  <li>有向图的表示使用邻接表的方式</li>
*  <li>邻接表里存放有向边的起始节点列表</li>
*  <li>有向图节点存放的数据必须不能相等,因为数据相等的节点被当成一个节点</li>
* </ul>
*
* @author jeff
* @version $Id: DirectGraphic.java, v 0.1 2014年3月25日 下午3:30:11 jeff Exp $
*/
public class DirectGraphic<T> {
/** 邻接表方式表示的图 */
private Map<GraphicNode<T>, Set<GraphicNode<T>>> graphic = new HashMap<GraphicNode<T>, Set<GraphicNode<T>>>();

/**
* 默认的构造器
*/
public DirectGraphic() {

}

/**
* 拷贝构造函数
*
* @param other
*/
public DirectGraphic(DirectGraphic<T> other) {
//进行深拷贝
for (Map.Entry<GraphicNode<T>, Set<GraphicNode<T>>> entry : other.graphic.entrySet()) {
Set<GraphicNode<T>> set = new HashSet<>(entry.getValue());
this.graphic.put(entry.getKey(), set);
}
}

/**
* 获取图里面构成环的所有的节点的集合<br />
* 使用的去边法
*
*
* @return
*/
public Set<GraphicNode<T>> findRings() {
//拷贝一份
DirectGraphic<T> copy = new DirectGraphic<>(this);

boolean needAgain = true;
while (needAgain) {
needAgain = false;
Iterator<GraphicNode<T>> iterator = copy.graphic.keySet().iterator();
while (iterator.hasNext()) {
GraphicNode<T> node = iterator.next();
Set<GraphicNode<T>> set = copy.graphic.get(node);
//入度为0
if (set.isEmpty()) {
iterator.remove();
removeSetNode(node, copy.graphic);
needAgain = true;
}
}

}

return copy.graphic.keySet();
}

/**
* 移除邻接表中的node节点
*
* @param node
* @param map
*/
private void removeSetNode(GraphicNode<T> node, Map<GraphicNode<T>, Set<GraphicNode<T>>> map) {
//移除大的节点
//map.remove(node);
//移除邻接表集合中的节点
for (Map.Entry<GraphicNode<T>, Set<GraphicNode<T>>> entry : map.entrySet()) {
entry.getValue().remove(node);
}
}

/**
* 插入有向边
*
* @param from
* @param to
*/
public void insertEdge(T from, T to) {
insertEdge(createOrGetNode(from), createOrGetNode(to));
}

/**
* 插入有向边
*
* @param from
* @param to
*/
public void insertEdge(GraphicNode<T> from, GraphicNode<T> to) {
//获取起始节点的集合
Set<GraphicNode<T>> nodes = graphic.get(to);
if (nodes == null) {
nodes = new HashSet<>();
graphic.put(to, nodes);
}
nodes.add(from);
}

/**
* 创建(如果不存在)或者获取(存在)对应的节点
*
* @param value
* @return
*/
public GraphicNode<T> createOrGetNode(T value) {
GraphicNode<T> ret = new GraphicNode<T>(value);
if (!graphic.containsKey(ret)) {
graphic.put(ret, new HashSet<GraphicNode<T>>());
} else {
ret = getNode(value);
}

assert (ret != null);
return ret;
}

/**
* 获取node
*
* @param value
* @return
*/
private GraphicNode<T> getNode(T value) {
for (GraphicNode<T> node : graphic.keySet()) {
if (node.getValue().equals(value)) {
return node;
}
}
return null;
}
}


锁管理器,完成实际的锁的管理,其中的死锁检测由内部类DeadLockDetecter的定时服务完成,github中查看

/**
* Alipay.com Inc.
* Copyright (c) 2004-2014 All Rights Reserved.
*/
package org.footoo.storage.transaction.lock;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.footoo.common.datastruct.graphic.DirectGraphic;
import org.footoo.common.datastruct.graphic.GraphicNode;
import org.footoo.common.log.Logger;
import org.footoo.common.log.LoggerFactory;
import org.footoo.common.service.TaskService;

/**
* 锁管理器
*
* <ul>
*  <li>统计线程说占用的锁</li>
*  <li>死锁检测</li>
* </ul>
*
* @author jeff
* @version $Id: LockManager.java, v 0.1 2014年3月17日 下午11:13:34 jeff Exp $
*/
public class LockManager {
/** 所有的读写锁 */
private final Set<RWLock>                         rwLocks         = new HashSet<>();

/** 每个线程的锁使用情况 */
private ConcurrentHashMap<Thread, ThreadLockInfo> threadLockInfos = new ConcurrentHashMap<>();
/** 日志 */
private static final Logger                       logger          = LoggerFactory
.getLogger(LockManager.class);

public LockManager() {
//启动死锁检测服务
new DeadLockDetecter().start();
}

/**
* 创建一个读写锁
*
* @return
*/
public RWLock createRwLock() {
RWLock rwLock = new RandomSyncRWLock();
rwLocks.add(rwLock);

return rwLock;
}

/**
* 释放锁
*
* @param rwlock
*/
public void release(RWLock rwlock) {
rwlock.release();
rwLocks.remove(rwlock);
}

/**
* 释放当前线程的rwlock的锁信息
*
* @param rwLock
*/
public void releaseSelf(RWLock rwLock) {
rwLock.releaseSelf();
}

/**
* 释放当前线程所有读写锁
*/
public void releaseSelf() {
ThreadLockInfo threadLockInfo = threadLockInfos.remove(Thread.currentThread());
if (threadLockInfo != null) {
//
for (RWLock rwLock : threadLockInfo.getLockSet()) {
rwLock.releaseSelf();
}
}
}

/**
* 释放所有的锁,这个一般在系统终结的时候使用
*/
public void releaseAll() {
for (RWLock rwLock : rwLocks) {
rwLock.release();
}
}

/**
* 加读锁
*
* @param rwLock
*/
public void addReadLock(RWLock rwLock) throws DeadLockInterruptException {
//期待读锁,如果一直阻塞在加锁过程,死锁检测服务可以检测到
setWantedLock(rwLock, WantedLock.LOCK_TYPE_READ);
try {
rwLock.readLock().lock();
addReadCount(rwLock, 1);
} catch (DeadLockInterruptException e) {
//重新抛出异常
throw e;
} finally {
//释放想要加的锁
delWantedLock();
}

}

/**
* 读锁解锁
*
* @param rwLock
*/
public void readUnlock(RWLock rwLock) {
rwLock.readLock().unlock();
addReadCount(rwLock, -1);
}

/**
* 写锁加锁
*
* @param rwLock 读写锁
*/
public void addWriteLock(RWLock rwLock) throws DeadLockInterruptException {
setWantedLock(rwLock, WantedLock.LOCK_TYPE_WRITE);
try {
rwLock.writeLock().lock();
addWriteCount(rwLock, 1);
} catch (DeadLockInterruptException e) {
throw e;
} finally {
delWantedLock();
}
}

/**
* 写锁解锁
*
* @param rwLock
*/
public void writeUnlock(RWLock rwLock) {
rwLock.writeLock().unlock();
addWriteCount(rwLock, -1);
}

/**
* 死锁检测服务
* <ul>
*  <li>死锁检测使用的是有向图的方法,而且整个过程不加锁,检测出来的状态可能不会是死锁,
*      但死锁肯定会被检测出来</li>
* <li>被检测的死锁线程列表中会随机选取一个线程进行终结</li>
* </ul>
*
* @author jeff
* @version $Id: LockManager.java, v 0.1 2014年3月25日 下午9:16:18 jeff Exp $
*/
private class DeadLockDetecter extends TaskService {
public DeadLockDetecter() {
this.setDelay(1000);
//每2分钟启动一次
this.setPeriod(2 * 1000);
}

/**
* 创建死锁检测的有向图,
* 对于每一个线程所期望加的锁,如果和其他线程之间有互斥的依赖,就增加一条有向边
*
* @return
*/
private DirectGraphic<Thread> createDirectGraphic() {
DirectGraphic<Thread> result = new DirectGraphic<>();
Iterator<Thread> iterator = threadLockInfos.keySet().iterator();
//开始建立死锁检测的有向图
while (iterator.hasNext()) {
Thread thread = iterator.next();
ThreadLockInfo threadLockInfo = threadLockInfos.get(thread);
WantedLock wantedLock = threadLockInfo.getWantedLock();
if (wantedLock == null) {
continue;
}
//
for (Map.Entry<Thread, ThreadLockInfo> entry : threadLockInfos.entrySet()) {
//跳过当前线程
if (entry.getKey().equals(thread)) {
continue;
}
ThreadLockInfo threadLockInfo2 = entry.getValue();
//期望的是读锁
if (wantedLock.getLockType() == WantedLock.LOCK_TYPE_READ) {
if (threadLockInfo2.isInWriteLocks(wantedLock.getRwLock())) {
result.insertEdge(thread, entry.getKey());
}
} else {//期望的是写锁
if (threadLockInfo2.isInReadLocks(wantedLock.getRwLock())
|| threadLockInfo2.isInWriteLocks(wantedLock.getRwLock())) {
result.insertEdge(thread, entry.getKey());
}
}
}
}
return result;
}

/**
* @see java.util.TimerTask#run()
*/
@Override
public void run() {
//logger.debug("开始死锁检测");
DirectGraphic<Thread> directGraphic = createDirectGraphic();
//获取有向图的环
Set<GraphicNode<Thread>> threadSet = directGraphic.findRings();
//发生了死锁
if (!threadSet.isEmpty()) {
logger.warn("出现了死锁");
for (GraphicNode<Thread> node : threadSet) {
logger.warn("死锁线程" + node.getValue());
}

//唤醒第一个
Thread t1 = threadSet.iterator().next().getValue();
t1.interrupt();
}
}

}

/**
* 设置期望获取的锁,这个用于死锁检测
*
* @param rwLock
* @param type
*/
private void setWantedLock(RWLock rwLock, int type) {
ThreadLockInfo threadLockInfo = getAndSetThreadLockInfo();
threadLockInfo.setWantedLock(new WantedLock(rwLock, type));
}

/**
* 去掉想添加的锁
*/
private void delWantedLock() {
ThreadLockInfo threadLockInfo = getAndSetThreadLockInfo();
threadLockInfo.removeWantedLock();
}

/**
* 增加读锁的使用计数
* 这个函数是和本线程相关的,不会存在竞争
*
* @param rwLock
* @param count
*/
private void addReadCount(RWLock rwLock, int count) {
ThreadLockInfo threadLockInfo = getAndSetThreadLockInfo();

threadLockInfo.addReadUseCount(rwLock, count);
}

/**
* 增加写锁的使用计数
*
* @param rwLock
* @param count
*/
private void addWriteCount(RWLock rwLock, int count) {
ThreadLockInfo threadLockInfo = getAndSetThreadLockInfo();
threadLockInfo.addWriteUseCount(rwLock, count);
}

/**
* 获取并且设置ThreadLockInfo(如果不存在)
*
* @return
*/
private ThreadLockInfo getAndSetThreadLockInfo() {
ThreadLockInfo threadLockInfo = threadLockInfos.get(Thread.currentThread());
if (threadLockInfo == null) {
threadLockInfo = new ThreadLockInfo();
threadLockInfos.put(Thread.currentThread(), threadLockInfo);
}
return threadLockInfo;
}

/**
* 线程所持有锁的情况
*
* @author jeff
* @version $Id: LockManager.java, v 0.1 2014年3月25日 上午9:04:43 jeff Exp $
*/
public static class ThreadLockInfo {
/** 线程所占有的读锁 */
private final ConcurrentHashMap<RWLock, LockInfo> rLocks = new ConcurrentHashMap<>();
/** 线程所占用的写锁 */
private final ConcurrentHashMap<RWLock, LockInfo> wLocks = new ConcurrentHashMap<>();
/** 所期待的锁 */
private WantedLock                                wantedLock;

/**
* 移除期待的锁
*/
public final void removeWantedLock() {
wantedLock = null;
}

/**
* Getter method for property <tt>wantedLock</tt>.
*
* @return property value of wantedLock
*/
public final WantedLock getWantedLock() {
return wantedLock;
}

/**
* Setter method for property <tt>wantedLock</tt>.
*
* @param wantedLock value to be assigned to property wantedLock
*/
public final void setWantedLock(WantedLock wantedLock) {
this.wantedLock = wantedLock;
}

/**
* Getter method for property <tt>rLocks</tt>.
*
* @return property value of rLocks
*/
public final ConcurrentHashMap<RWLock, LockInfo> getrLocks() {
return rLocks;
}

/**
* Getter method for property <tt>wLocks</tt>.
*
* @return property value of wLocks
*/
public final ConcurrentHashMap<RWLock, LockInfo> getwLocks() {
return wLocks;
}

/**
* 测试rwLock是否在读锁集合
*
* @param rwLock
* @return
*/
public boolean isInReadLocks(RWLock rwLock) {
return rLocks.containsKey(rwLock);
}

/**
* 测试rwLock是否在写锁集合
*
* @param rwLock
* @return
*/
public boolean isInWriteLocks(RWLock rwLock) {
return wLocks.containsKey(rwLock);
}

/**
* 获取读锁集合
*
* @return
*/
public Set<RWLock> getReadSet() {
return rLocks.keySet();
}

/**
* 获取写锁集合
*
* @return
*/
public Set<RWLock> getWriteSet() {
return wLocks.keySet();
}

/**
* 获取线程锁占有的锁的集合
*
* @return
*/
public Set<RWLock> getLockSet() {
Set<RWLock> result = new HashSet<RWLock>(rLocks.keySet());
result.addAll(wLocks.keySet());
return result;
}

/**
* 给读锁增加使用计数
*
* @param rwLock
* @param count
*/
public void addReadUseCount(RWLock rwLock, int count) {
addUseCount(rwLock, count, rLocks);
}

/**
* 给写锁增加使用计数
*
* @param rwLock
* @param count
*/
public void addWriteUseCount(RWLock rwLock, int count) {
addUseCount(rwLock, count, wLocks);
}

/**
* 给rwLock增加number个使用计数
*
* @param rwLock
* @param number
* @param locks
*/
private void addUseCount(RWLock rwLock, int number,
ConcurrentHashMap<RWLock, LockInfo> locks) {
LockInfo lockInfo = locks.get(rwLock);
if (lockInfo == null) {
lockInfo = new LockInfo(rwLock, 0);
locks.put(rwLock, lockInfo);
}
lockInfo.setUseCount(lockInfo.getUseCount() + number);
//
if (lockInfo.getUseCount() <= 0) {
locks.remove(rwLock);
}
}

}

/**
* 线程所期望的锁信息
*
* @author jeff
* @version $Id: LockManager.java, v 0.1 2014年3月25日 上午9:09:31 jeff Exp $
*/
public static class WantedLock {
/** 读锁 */
public static final int LOCK_TYPE_READ  = 0;
/** 写锁 */
public static final int LOCK_TYPE_WRITE = 1;

/** 所期望的锁 */
private RWLock          rwLock;
/** 所期望的锁类型 */
private int             lockType;

/**
* 默认的构造器
*/
public WantedLock() {

}

/**
* 构造器
*
* @param rwLock
* @param lockType
*/
public WantedLock(RWLock rwLock, int lockType) {
this.rwLock = rwLock;
this.lockType = lockType;
}

/**
* Getter method for property <tt>rwLock</tt>.
*
* @return property value of rwLock
*/
public final RWLock getRwLock() {
return rwLock;
}

/**
* Setter method for property <tt>rwLock</tt>.
*
* @param rwLock value to be assigned to property rwLock
*/
public final void setRwLock(RWLock rwLock) {
this.rwLock = rwLock;
}

/**
* Getter method for property <tt>lockType</tt>.
*
* @return property value of lockType
*/
public final int getLockType() {
return lockType;
}

/**
* Setter method for property <tt>lockType</tt>.
*
* @param lockType value to be assigned to property lockType
*/
public final void setLockType(int lockType) {
this.lockType = lockType;
}

}

/**
* 锁使用信息
*
* @author jeff
* @version $Id: LockManager.java, v 0.1 2014年3月20日 下午5:48:35 jeff Exp $
*/
public static class LockInfo {
/** 锁 */
private RWLock rwLock;
/** 使用计数 */
private int    useCount;

public LockInfo() {

}

public LockInfo(RWLock rwLock, int useCount) {
this.rwLock = rwLock;
this.useCount = useCount;
}

public int hashCode() {
return rwLock.hashCode();
}

public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof LockInfo)) {
return false;
}
return this.rwLock.equals(((LockInfo) other).rwLock);
}

/**
* Getter method for property <tt>rwLock</tt>.
*
* @return property value of rwLock
*/
public RWLock getRwLock() {
return rwLock;
}

/**
* Setter method for property <tt>rwLock</tt>.
*
* @param rwLock value to be assigned to property rwLock
*/
public void setRwLock(RWLock rwLock) {
this.rwLock = rwLock;
}

/**
* Getter method for property <tt>useCount</tt>.
*
* @return property value of useCount
*/
public int getUseCount() {
return useCount;
}

/**
* Setter method for property <tt>useCount</tt>.
*
* @param useCount value to be assigned to property useCount
*/
public void setUseCount(int useCount) {
this.useCount = useCount;
}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐