java 基于redis分布式锁

1、基于 redis 分布式锁

package com.example.demo;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class DistributedLockComponent {

@Autowired
StringRedisTemplate stringRedisTemplate;

</span><span style="color: rgba(0, 0, 255, 1)">private</span> org.slf4j.Logger logger = LoggerFactory.getLogger(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.getClass());

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 *
 * @desc 加锁
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> key
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> value
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> timeout 超时时间
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> autoReleaseTime 自动释放锁时间
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span> lock(String key, String value, <span style="color: rgba(0, 0, 255, 1)">long</span> timeout, <span style="color: rgba(0, 0, 255, 1)">long</span><span style="color: rgba(0, 0, 0, 1)"> autoReleaseTime) {
    </span><span style="color: rgba(0, 0, 255, 1)">boolean</span> flag = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;

    </span><span style="color: rgba(0, 0, 255, 1)">long</span> time =<span style="color: rgba(0, 0, 0, 1)"> System.currentTimeMillis();
    </span><span style="color: rgba(0, 0, 255, 1)">long</span> maxTime = time +<span style="color: rgba(0, 0, 0, 1)"> timeout;

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 自旋等待-如果在指定时间内还没获取到锁就退出自旋,并且设置过期时间避免死锁。</span>
    <span style="color: rgba(0, 0, 255, 1)">while</span> (!stringRedisTemplate.opsForValue().setIfAbsent(key, value) &amp;&amp; time &lt;=<span style="color: rgba(0, 0, 0, 1)"> maxTime) {
        </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
            TimeUnit.MICROSECONDS.sleep(</span>10<span style="color: rgba(0, 0, 0, 1)">);
        } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (InterruptedException e) {
            Thread.currentThread().interrupt();
            flag </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
        }

        time </span>=<span style="color: rgba(0, 0, 0, 1)"> System.currentTimeMillis();
    }

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置过期时间</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (flag) {
        stringRedisTemplate.expire(key, autoReleaseTime, TimeUnit.MILLISECONDS);
    }

    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> flag;
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 *
 * @desc 解锁
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> key
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> value
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> unLock(String key, String value) {
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)">(StringUtils.isNotBlank(stringRedisTemplate.opsForValue().get(key))
                </span>&amp;&amp;<span style="color: rgba(0, 0, 0, 1)"> stringRedisTemplate.opsForValue().get(key).equals(value)) {
            stringRedisTemplate.delete(key);
        }
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e) {
        logger.error(e.getMessage(), e);
    }
}


</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 扣减库存
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String decreaseStock(String key, String value){
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">{
        lock(key,value,</span>6000,6000 * 2<span style="color: rgba(0, 0, 0, 1)">);
    }</span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e){
        logger.error(e.getMessage(),e);
    }</span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)"> {
        unLock(key,value);
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> ""<span style="color: rgba(0, 0, 0, 1)">;
}


</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 *  测试可模拟多个线程扣减库存
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> skuId 商品ID
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String test(String skuId) {

    decreaseStock(</span>"KEY_SKU_"+<span style="color: rgba(0, 0, 0, 1)">skuId, skuId);

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">线程1</span>
    <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(()-&gt;<span style="color: rgba(0, 0, 0, 1)">{
        decreaseStock(</span>"KEY_SKU_"+<span style="color: rgba(0, 0, 0, 1)">skuId, skuId);
    });

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">线程2</span>
    <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(()-&gt;<span style="color: rgba(0, 0, 0, 1)">{
        decreaseStock(</span>"KEY_SKU_"+<span style="color: rgba(0, 0, 0, 1)">skuId, skuId);
    });

    </span><span style="color: rgba(0, 0, 255, 1)">return</span> ""<span style="color: rgba(0, 0, 0, 1)">;
}

}