package org.hsweb.concurrent.lock.support.redis; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.util.Assert; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; /** * Created by zhouhao on 16-5-27. */ public class RedisLock implements Lock { static final String PREFIX = "lock:"; static final long DEFAULT_EXPIRE = 60; private long lockKeyExpireTime = DEFAULT_EXPIRE; private long waitTime = 30; protected byte[] lockValue; private RedisTemplate redisTemplate; private String key; private byte[] getKey() { return (PREFIX + key + ".lock").getBytes(); } public RedisLock(String key, RedisTemplate redisTemplate) { Assert.notNull(key); Assert.notNull(redisTemplate); this.key = key; this.redisTemplate = redisTemplate; lockValue = new byte[0]; } public void setWaitTime(long waitTime) { this.waitTime = waitTime; } @Override public void lock() { redisTemplate.execute((RedisCallback<Boolean>) connection -> { boolean locked = false; do { locked = connection.setNX(getKey(), lockValue); if (locked) { connection.expire(getKey(), lockKeyExpireTime); } else sleep(); } while (!locked); return true; }); } @Override public void lockInterruptibly() throws InterruptedException { boolean locked = (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection -> { boolean l = connection.setNX(getKey(), lockValue); if (l) connection.expire(getKey(), lockKeyExpireTime); return l; }); if (!locked) throw new InterruptedException(new String(getKey()) + " is locked!"); } @Override public boolean tryLock() { try { lockInterruptibly(); return true; } catch (InterruptedException e) { return false; } } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { byte[] error = new byte[1]; boolean success = (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection -> { boolean locked = false; long startWith = System.nanoTime(); do { locked = connection.setNX(getKey(), lockValue); if (locked) { connection.expire(getKey(), lockKeyExpireTime); return true; } long now = System.nanoTime(); if (now - startWith > unit.toNanos(time)) { error[0] = 1; return false; } sleep(); } while (!locked); return null; }); if (error[0] == 1) { throw new InterruptedException("lock time out!"); } return success; } @Override public void unlock() { redisTemplate.execute((RedisCallback) conn -> conn.del(getKey())); } @Override public Condition newCondition() { throw new UnsupportedOperationException(); } protected void sleep() { try { Thread.sleep(waitTime); } catch (InterruptedException e) { } } }