Skip to main content

带读写锁的对象:ObjWithLock

网络编程中会伴随大量并发操作,大家对ConcurrentModificationException一定不会陌生,这个是典型的并发操作集合引发的异常。为了更好的处理并发,tio自创了一个ObjWithLock对象,这个对象很简单,但给并发编程带来了极大的方便,如果您阅读过tio源代码,相信已经体会到这个对象在tio中是无处不在的。ObjWithLock顾名思义,它就是一个自带了一把(读写)锁的普通对象(一般是集合对象),每当要对这个对象进行同步安全操作(并发下对集合进行遍历或对集合对象进行元素修改删除增加)时,就得用这个锁。

ObjWithLock.java源码

package org.tio.utils.lock;

import java.io.Serializable;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* 自带读写锁的对象.
*
* @author tanyaowu
*/
public class ObjWithLock<T> implements Serializable {

private static final long serialVersionUID = -3048283373239453901L;

private static Logger log = LoggerFactory.getLogger(ObjWithLock.class);

/**
*
*/
private T obj = null;

/**
*
*/
private ReentrantReadWriteLock lock = null;

/**
*
* @param obj
* @author tanyaowu
*/
public ObjWithLock(T obj) {
this(obj, new ReentrantReadWriteLock());
}

/**
*
* @param obj
* @param lock
* @author tanyaowu
*/
public ObjWithLock(T obj, ReentrantReadWriteLock lock) {
super();
this.obj = obj;
this.lock = lock;
}

/**
*
* @return
* @author tanyaowu
*/
public ReentrantReadWriteLock getLock() {
return lock;
}

/**
* 获取写锁
* @return
*/
public WriteLock writeLock() {
return lock.writeLock();
}

/**
* 获取读锁
* @return
*/
public ReadLock readLock() {
return lock.readLock();
}

/**
*
* @return
* @author tanyaowu
*/
public T getObj() {
return obj;
}

/**
*
* @param obj
* @author tanyaowu
*/
public void setObj(T obj) {
this.obj = obj;
}

/**
* 操作obj时,带上读锁
* @param readLockHandler
*/
public void handle(ReadLockHandler<T> readLockHandler) {
ReadLock readLock = lock.readLock();
readLock.lock();
try {
readLockHandler.handler(obj);
} catch (Throwable e) {
log.error(e.getMessage(), e);
} finally {
readLock.unlock();
}
}

/**
* 操作obj时,带上写锁
* @param writeLockHandler
*/
public void handle(WriteLockHandler<T> writeLockHandler) {
WriteLock writeLock = lock.writeLock();
writeLock.lock();
try {
writeLockHandler.handler(obj);
} catch (Throwable e) {
log.error(e.getMessage(), e);
} finally {
writeLock.unlock();
}
}

}

三个ObjWithLock子类

  • ListWithLock<T>
  • MapWithLock<K, V>
  • SetWithLock<T>

ListWithLock里面有Obj就是List对象,MapWithLock里面有Obj就是Map对象,SetWithLock里面有Obj就是Set对象

掌握这些对象,我觉得最好的方法是看个例子,SetWithLock就是个活生生的例子,它的源代码如下

package org.tio.utils.lock;

import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author tanyaowu
* 2017年5月14日 上午9:55:37
*/
public class SetWithLock<T> extends ObjWithLock<Set<T>> {
private static final long serialVersionUID = -2305909960649321346L;
private static final Logger log = LoggerFactory.getLogger(SetWithLock.class);

/**
* @param set
* @author tanyaowu
*/
public SetWithLock(Set<T> set) {
super(set);
}

/**
* @param set
* @param lock
* @author tanyaowu
*/
public SetWithLock(Set<T> set, ReentrantReadWriteLock lock) {
super(set, lock);
}

/**
*
* @param t
* @return
* @author tanyaowu
*/
public boolean add(T t) {
WriteLock writeLock = this.writeLock();
writeLock.lock();
try {
Set<T> set = this.getObj();
return set.add(t);
} catch (Throwable e) {
log.error(e.getMessage(), e);
} finally {
writeLock.unlock();
}
return false;
}

/**
*
*
* @author tanyaowu
*/
public void clear() {
WriteLock writeLock = this.writeLock();
writeLock.lock();
try {
Set<T> set = this.getObj();
set.clear();
} catch (Throwable e) {
log.error(e.getMessage(), e);
} finally {
writeLock.unlock();
}
}

/**
*
* @param t
* @return
* @author tanyaowu
*/
public boolean remove(T t) {
WriteLock writeLock = this.writeLock();
writeLock.lock();
try {
Set<T> set = this.getObj();
return set.remove(t);
} catch (Throwable e) {
log.error(e.getMessage(), e);
} finally {
writeLock.unlock();
}
return false;
}

/**
*
* @return
* @author tanyaowu
*/
public int size() {
ReadLock readLock = this.readLock();
readLock.lock();
try {
Set<T> set = this.getObj();
return set.size();
} finally {
readLock.unlock();
}
}
}

操作步骤

先拿到相应的锁(根据业务需要获取读锁或写锁,如果只是读取数据,则获取读锁,如果需要对集合进行修改,则获取写锁),然后【 lock()-->业务处理-->unlock() 】,注意一定要在try前面进行lock(),在finally块中进行unlock()操作,这样可以保证一个获取锁到释放锁形成一个原子操作。图解如下

ObjWithLock操作4步曲

简化操作

上面的操作步骤还是复杂,所以t-io提供了极其简化的API,让工程师们根本不用碰锁就能用上锁

  • 读锁操作简化:org.tio.utils.lock.ObjWithLock.handle(ReadLockHandler<T>)

    示例

/**
* 关闭某群所有连接
* @param tioConfig
* @param group
* @param remark
* @return
*/
public static void closeGroup(TioConfig tioConfig, String group, String remark) {
SetWithLock<ChannelContext> setWithLock = Tio.getChannelContextsByGroup(tioConfig, group);
setWithLock.handle(new ReadLockHandler<Set<ChannelContext>>() {
@Override
public void handler(Set<ChannelContext> set) {
for (ChannelContext channelContext : set) {
Tio.close(channelContext, remark);
}
}
});
}
  • 写锁操作简化:org.tio.utils.lock.ObjWithLock.handle(WriteLockHandler <T>)

    示例

    public static void writeLockDemo(TioConfig tioConfig, String group, String remark) {
SetWithLock<ChannelContext> setWithLock = Tio.getChannelContextsByGroup(tioConfig, group);
setWithLock.handle(new WriteLockHandler<Set<ChannelContext>>() {
@Override
public void handler(Set<ChannelContext> set) {
for (ChannelContext channelContext : set) {
if ("44".equals(channelContext.getId())) {
set.remove(channelContext);
}
}
}
});
}