分布式锁的三种实现方式_分布式锁的常见实现方式简介

4d0bf14d94ad62be6deb2134dc57f97a.png

本文不对分布式锁概念进行介绍,主要介绍分布式锁的实现。分布式锁主要有三种实现方式:

  • 基于数据库的分布式锁
  • 基于Redis的分布式锁
  • 基于Zookeeper的分布式锁

基于数据库的分布式锁使用并不是很广泛,本文主要介绍后面两种分布式锁的实现方式。

基于Redis的分布式锁

基于Jedis API实现分布式锁

加锁操作主要是基于Redis setnx命令式原子操作,解锁操作则是基于Redis执行lua脚本也具有原子性,实现分布式锁的关键点就是要保证加锁和解锁操作都具有原子性,代码实现如下:

@Componentpublic class RedisDistributeLock {    @Autowired    JedisPool jedisPool;    private long internalLockLeaseTime = 30000;    private SetParams params = SetParams.setParams().nx().px(internalLockLeaseTime);    private static final String LOCK_SUCCESS = "OK";    private static final String LOCK_SUCCESS_FLAG = "1";    private long timeout = 999999;    private String redisKey = "redis_lock";    public boolean lock(String id) {        Long start = System.currentTimeMillis();        try (Jedis jedis = jedisPool.getResource()) {            for (; ; ) {                //SET命令返回OK ,则证明获取锁成功                String lock = jedis.set(redisKey, id, params);                if (LOCK_SUCCESS.equals(lock)) {                    return true;                }                //否则循环等待,在timeout时间内仍未获取到锁,则获取失败                long l = System.currentTimeMillis() - start;                if (l >= timeout) {                    return false;                }                try {                    Thread.sleep(100);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }    public boolean unlock(String id) {        String script =                "if redis.call('get',KEYS[1]) == ARGV[1] then" +                        "   return redis.call('del',KEYS[1]) " +                        "else" +                        "   return 0 " +                        "end";        try (Jedis jedis = jedisPool.getResource()) {            Object result = jedis.eval(script, Collections.singletonList(redisKey),                    Collections.singletonList(id));            return LOCK_SUCCESS_FLAG.equals(result.toString());        }    }}

在主线程中创建两个线程并启动,两个线程抢占同一个分布式锁

@Componentpublic class RedisLockService {    @Autowired    RedisDistributeLock redisDistributeLock;    private String lockKey = "lock_key";    private String requestId = "my_request_id_1";    @PostConstruct    public void testLock() throws InterruptedException {        Thread t1 = new Thread(() -> {           redisDistributeLock.lock( requestId);           System.out.println("线程1获取分布式锁成功,获取时间为:" + Instant.now().toString());            try {                Thread.sleep(5000);            } catch (InterruptedException e) {                e.printStackTrace();            }            redisDistributeLock.unlock( requestId);        });        Thread t2 = new Thread(() -> {            redisDistributeLock.lock( requestId);            System.out.println("线程2获取分布式锁成功,获取时间为:" + Instant.now().toString());            try {                Thread.sleep(5000);            } catch (InterruptedException e) {                e.printStackTrace();            }            redisDistributeLock.unlock( requestId);        });        t1.start();        t2.start();        t1.join();        t2.join();    }}

控制台输出结果如下,结果显示在线程1获取锁成功5秒之后,线程1释放了分布式锁,线程2获取到了分布式锁。时间相差5s左右,与sleep的时间吻合。

线程1获取分布式锁成功,获取时间为:2020-06-01T11:02:24.394Z线程2获取分布式锁成功,获取时间为:2020-06-01T11:02:29.494Z

Redisson分布式锁

Redis官方推荐使用Redisson实现分布式锁,基于Jedis原生API实现的分布式锁无法支持可重入功能,Redisson在原生API的基础上进行了封装,为分布式系统提供了很多实用的功能,分布式锁就是其中一个,Redisson分布式锁的使用方式如下:

@Componentpublic class RedissonDistributeLock {    private RedissonClient client = null;    @PostConstruct    public void initRLock() {        Config config = new Config();        config.useSingleServer().setAddress("redis://127.0.0.1:6379");        //redis没有设置密码,加上下面这一行链接不上redis        //config.useSingleServer().setPassword(standaloneRedisConfig.getPassword());        client = Redisson.create(config);    }    public void lock(String lockKey) {        RLock lock = client.getLock(lockKey);        lock.lock();    }    public void unlock(String lockKey) {        RLock lock = client.getLock(lockKey);        lock.unlock();    }}

在主线程中创建两个线程并启动,两个线程抢占同一个分布式锁

@Componentpublic class RedissonLockService {    @Autowired    RedissonDistributeLock redissonDistributeLock;    private static final String lockKey = "lock_key";    @PostConstruct    public void testRedissonDistributeLock() throws InterruptedException {        Thread t1 = new Thread(() -> {            redissonDistributeLock.lock(lockKey);            System.out.println("线程1获取分布式锁成功,获取时间为:" + Instant.now().toString());            try {                Thread.sleep(5000);            } catch (InterruptedException e) {                e.printStackTrace();            }            redissonDistributeLock.unlock(lockKey);        });        Thread t2 = new Thread(() -> {            redissonDistributeLock.lock(lockKey);            System.out.println("线程2获取分布式锁成功,获取时间为:" + Instant.now().toString());            try {                Thread.sleep(5000);            } catch (InterruptedException e) {                e.printStackTrace();            }            redissonDistributeLock.unlock(lockKey);        });        t1.start();        t2.start();        t1.join();        t2.join();    }}
线程2获取分布式锁成功,获取时间为:2020-06-01T11:57:31.951Z线程1获取分布式锁成功,获取时间为:2020-06-01T11:57:36.971Z

Jetcache分布式锁

JetCache是阿里巴巴提供的开源的缓存框架,也提供了分布式锁的功能,使用如下

@Component@DependsOn(value = "globalCacheConfig")public class JetCacheLockService {    private static final String LOCK_STR = "jetcache_lock_1";    /**     * 分布式锁     */    @CreateCache(name = "lock:", cacheType = CacheType.REMOTE)    private Cache lockCache;    @PostConstruct    public void testJetCacheLock() throws InterruptedException {        //lockCache是懒加载的        lockCache.put("hhhh", "jjjjjjjjjj");        Thread t1 = new Thread(() -> {            boolean getKey = lockCache.tryLockAndRun(LOCK_STR, 30, TimeUnit.SECONDS, () -> {                System.out.println("线程1获取分布式锁成功,获取时间为:" + Instant.now().toString());                try {                    Thread.sleep(5000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            });            if (getKey) {                System.out.println("线程1获取到了分布式锁");            } else {                System.out.println("线程1获取分布式锁失败");            }        });        Thread t2 = new Thread(() -> {            boolean getKey = lockCache.tryLockAndRun(LOCK_STR, 30, TimeUnit.SECONDS, () -> {                System.out.println("线程2获取分布式锁成功,获取时间为:" + Instant.now().toString());                try {                    Thread.sleep(5000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            });            if (getKey) {                System.out.println("线程2获取到了分布式锁");            } else {                System.out.println("线程2获取分布式锁失败");            }        });        t1.start();        t2.start();        t1.join();        t2.join();    }}

控制台输出结果如下,和上面两种分布式锁不一样,jetcache的分布式锁获取锁失败后不会等待,而是直接执行获取锁失败的逻辑,使用过程中可以根据具体的业务需要选择合适的分布式锁。

线程2获取分布式锁失败线程1获取分布式锁成功,获取时间为:2020-06-02T08:42:01.783Z线程1获取到了分布式锁

基于Zookeeper的分布式锁

关于zookeeper分布式锁,可以直接使用Curator框架提供的Mutex,支持可重入,公平锁等功能,使用如下:

public class CuratorDistributeLock {    public static void main(String[] args) throws InterruptedException {        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);        //client和client2模拟不同服务实例        CuratorFramework client = CuratorFrameworkFactory.newClient("10.89.232.9:2181",retryPolicy);        client.start();        CuratorFramework client2 = CuratorFrameworkFactory.newClient("10.89.232.9:2181",retryPolicy);        client2.start();        //创建分布式锁, 锁空间的根节点路径为/curator/lock        InterProcessMutex mutex  = new InterProcessMutex(client,"/curator/distributeLock");        final InterProcessMutex mutex2  = new InterProcessMutex(client2,"/curator/distributeLock");        Thread t1 = new Thread(() -> {            try {                mutex.acquire();            } catch (Exception e) {                e.printStackTrace();            }            System.out.println("线程1获取分布式锁成功,获取时间为:" + Instant.now().toString());            try {                Thread.sleep(5000);            } catch (InterruptedException e) {                e.printStackTrace();            }            try {                mutex.release();            } catch (Exception e) {                e.printStackTrace();            }        });        Thread t2 = new Thread(() -> {            try {                mutex2.acquire();            } catch (Exception e) {                e.printStackTrace();            }            System.out.println("线程2获取分布式锁成功,获取时间为:" + Instant.now().toString());            try {                Thread.sleep(5000);            } catch (InterruptedException e) {                e.printStackTrace();            }            try {                mutex2.release();            } catch (Exception e) {                e.printStackTrace();            }        });        t1.start();        t2.start();        t1.join();        t2.join();        //关闭客户端        client.close();        client2.close();    }}

总结本文主要介绍了分布式锁的四种使用方法,并展示了每一种用法及其作用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值