开始的时候项目没有添加互斥锁,用的依然是老的思路,在并发量增加的情况下,遇到了很多的问题,包括数据库重复读等,想了下考虑增加 互斥锁来排序对单个资源的操作。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @Order(1) public @interface DistributeLock {LockType type() default LockType.Id;enum LockType {Id,Function;} }
实现了两个方式,用注解+Aspect的方式 和 try(Closeable){} 的方式确保锁的释放。
实现依托于redis的
String result = conn.set(key, value, "NX", "PX", expire);
方法,该方法在单节点和集群下都可用。我测试用的单节点生产cluster集群都测试通过。
public class MutexLock implements Closeable {RedisCache redisCache;// private boolean isWorkFine = false;private String key;private final String DEFAULT_PREFIX = "DEFAULT";public boolean isWorkFine() {return isWorkFine;}public MutexLock(String prefix, Long id) throws EirException{redisCache = ApplicationContextHelper.getBean(RedisCache.class);if (redisCache == null) {System.out.println("枷锁 redis 未开启");return;}this.key = (StringUtil.isNullOrEmpty(prefix) ? DEFAULT_PREFIX : prefix) + ":" + id;Boolean tryGetLock = new RedisUtils(redisCache).setnx(key);if (tryGetLock != null && tryGetLock) {isWorkFine = true;}if (tryGetLock != null && !tryGetLock) {throw new EirException("当前资源正忙");}}/*** Closes this stream and releases any system resources associated* with it. If the stream is already closed then invoking this* method has no effect.* <p>* <p> As noted in {@link AutoCloseable#close()}, cases where the* close may fail require careful attention. It is strongly advised* to relinquish the underlying resources and to internally* <em>mark</em> the {@code Closeable} as closed, prior to throwing* the {@code IOException}.** @throws IOException if an I/O error occurs*/@Overridepublic void close() {try {if (isWorkFine) {new RedisUtils(redisCache).releaseLock(this.key);}} catch (Exception e) {e.printStackTrace();}}
redisUtils 中在getLock的时候 设置了等待时间,5秒钟,超过之后会返回“资源正忙”, 锁也加了超时时间, 避免死锁问题。
测试代码就不贴了。
总结下来这个应该是最廉价实惠的互斥锁方案了。