分布式锁RSemaphore介绍

本文介绍了一种基于Redis的分布式信号量锁——RSemaphore。该组件用于限制同时访问共享区域的线程数量,提供了多种获取和释放资源的方法,并通过示例展示了其用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一.简介

二.方法总结

三.方法详情

1.acquire()

2.acquire(int permits)

3.tryAcquire()

4.tryAcquire(int permits)

5.tryAcquire​(long waitTime, TimeUnit unit)

6.tryAcquire​(int permits, long waitTime, TimeUnit unit)

7.release()

8.release​(int permits)

9.availablePermits()

10.drainPermits()

11.trySetPermits​(int permits)

12.addPermits​(int permits)

四.使用示例

五.源码解析

1.semaphore.trySetPermits(3);

2.semaphore.acquire()

3.semaphore.release()

总结


一.简介

 RSemaphore基于 Redis 的Semaphore开发。使用RSemaphore获取资源的顺序是不可预测的,所以它是一种非公平锁。可以理解为分布式的信号量,它的作用是用来限制同时访问共享区域的线程数量

二.方法总结

Modifier and TypeMethodDescription
voidacquire()获取1个permit.
voidacquire(int permits)获取定义数量的permits.
voidaddPermits(int permits)按定义的值增加或减少可用许可证的数量。
intavailablePermits()返回可用许可证的数量。
intdrainPermits()获取并返回所有立即可用的许可证。
voidrelease()释放1个permit.
voidrelease(int permits)释放定义数量的permits.
booleantryAcquire()尝试获取当前可用的许可证。
booleantryAcquire​(int permits)尝试获取定义数量的permits
booleantryAcquire(int permits, long waitTime, TimeUnit unit)尝试获取定义数量的permits。如果获取不到,就一直等待,直到获取到指定数量的permits。最长等待waitTime时间。
booleantryAcquire(long waitTime, TimeUnit unit)尝试获取1个permit。如果获取不到,就一直等待,直到成功获取1个permit。最长等待waitTime时间。
booleantrySetPermits(int permits)尝试设置许可证数量。

三.方法详情

1.acquire()

void acquire() throws InterruptedException

获取1个permit。如果没有,会一直等待,直到获得1个permit

抛出:

InterruptedException - 如果当前线程被中断

InterruptedException

2.acquire(int permits)

void acquire​(int permits) throws InterruptedException

获取定义数量的permits。如果没有,会一直等待,直到获得定义数量的permits。

参数:

permits - 获得许可的数量

抛出:

InterruptedException - 如果当前线程被中断

IllegalArgumentException- 如果permits是负数

3.tryAcquire()

boolean tryAcquire() 

尝试获取当前可用的许可证。

返回:
如果成功获得许可证,返回true;否则返回 false

4.tryAcquire(int permits)

boolean tryAcquire​(int permits)

尝试获取定义数量的permits

参数:

permits - 获得许可的数量

返回:

如果成功获得指定数量的许可证,返回true;否则返回false

5.tryAcquire​(long waitTime, TimeUnit unit)

boolean tryAcquire​(long waitTime, TimeUnit unit) throws InterruptedException

尝试获取1个permit。如果获取不到,就一直等待,直到成功获取1个permit。最长等待waitTime时间。

参数:

waitTime - 最长等待时间

unit - 时间单位

返回:

如果成功获得1个permit,返回true;否则返回false

抛出:

InterruptedException - 如果当前线程被中断

6.tryAcquire​(int permits, long waitTime, TimeUnit unit)

boolean tryAcquire​(int permits, long waitTime, TimeUnit unit) throws InterruptedException

尝试获取定义数量的permits。如果获取不到,就一直等待,直到获取到指定数量的permits。最长等待waitTime时间。

参数:

permits - 许可证数量

waitTime - 最长等待时间

unit - 时间单位

返回:

如果成功获得指定数量的许可证,返回true;否则返回false

抛出:

InterruptedException - 如果当前线程被中断

7.release()

void release()

释放1个permit。增加一个可用permit的数量。

8.release​(int permits)

void release​(int permits)

释放定义数量的permits。增加定义数量个的可用permit的数量。

9.availablePermits()

int availablePermits()

返回可用许可证的数量。

10.drainPermits()

int drainPermits()

获取并返回所有立即可用的许可证。

11.trySetPermits​(int permits)

boolean trySetPermits​(int permits)

尝试设置许可数量。

返回:

如果许可证数量设置成功返回true;如果许可证数量已经设置过,返回false

12.addPermits​(int permits)

void addPermits (int permit)
按定义的值增加或减少可用许可的数量。

四.使用示例

//可以在使用前初始化,但它不是必需的
RSemaphore semaphore = redisson.getSemaphore("semaphore");
// 同时最多允许3个线程获取锁
semaphore.trySetPermits(3);

for(int i = 0; i < 10; i++) {
  new Thread(new Runnable() {

    @Override
    public void run() {
      try {
        System.out.println(new Date() + ":线程[" + Thread.currentThread().getName() + "]尝试获取Semaphore锁"); 
        semaphore.acquire();
        System.out.println(new Date() + ":线程[" + Thread.currentThread().getName() + "]成功获取到了Semaphore锁,开始工作"); 
        Thread.sleep(3000);  
        semaphore.release();
        System.out.println(new Date() + ":线程[" + Thread.currentThread().getName() + "]释放Semaphore锁"); 
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }).start();
}

五.源码解析

1.semaphore.trySetPermits(3);

public class RedissonSemaphore extends RedissonExpirable implements RSemaphore {
    @Override
    public boolean trySetPermits(int permits) {
        return get(trySetPermitsAsync(permits));
    }
    
    @Override
    public RFuture<Boolean> trySetPermitsAsync(int permits) {
        return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
                "local value = redis.call('get', KEYS[1]); " +
                "if (value == false or value == 0) then "
                    + "redis.call('set', KEYS[1], ARGV[1]); "
                    + "redis.call('publish', KEYS[2], ARGV[1]); "
                    + "return 1;"
                + "end;"
                + "return 0;",
                Arrays.<Object>asList(getName(), getChannelName()), permits);
    }
}

执行流程为:

  1. get semaphore,获取到semaphore信号量的当前的值
  2. 第一次数据为0, 然后使用set semaphore 3,将这个信号量同时能够允许获取锁的客户端的数量设置为3。(注意到,如果之前设置过了信号量,将无法再次设置,直接返回0。想要更改信号量总数可以使用addPermits方法)
  3. 然后redis发布一些消息,返回1

2.semaphore.acquire()

public class RedissonSemaphore extends RedissonExpirable implements RSemaphore {
    @Override
    public void acquire(int permits) throws InterruptedException {
        if (tryAcquire(permits)) {
            return;
        }

        RFuture<RedissonLockEntry> future = subscribe();
        commandExecutor.syncSubscription(future);
        try {
            while (true) {
                if (tryAcquire(permits)) {
                    return;
                }

                getEntry().getLatch().acquire(permits);
            }
        } finally {
            unsubscribe(future);
        }
//        get(acquireAsync(permits));
    }

    @Override
    public boolean tryAcquire(int permits) {
        return get(tryAcquireAsync(permits));
    }


    @Override
    public RFuture<Boolean> tryAcquireAsync() {
        return tryAcquireAsync(1);
    }
    
    @Override
    public RFuture<Boolean> tryAcquireAsync(int permits) {
        if (permits < 0) {
            throw new IllegalArgumentException("Permits amount can't be negative");
        }
        if (permits == 0) {
            return RedissonPromise.newSucceededFuture(true);
        }

        return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
                  "local value = redis.call('get', KEYS[1]); " +
                  "if (value ~= false and tonumber(value) >= tonumber(ARGV[1])) then " +
                      "local val = redis.call('decrby', KEYS[1], ARGV[1]); " +
                      "return 1; " +
                  "end; " +
                  "return 0;",
                  Collections.<Object>singletonList(getName()), permits);
    }
}

执行流程为:

  1. get semaphore,获取到一个当前的值,比如说是3,3 > 1
  2. decrby semaphore 1,将信号量允许获取锁的客户端的数量递减1,变成2
  3. decrby semaphore 1
  4. decrby semaphore 1
  5. 执行3次加锁后,semaphore值为0

此时如果再来进行加锁则直接返回0,然后进入死循环去获取锁

3.semaphore.release()

public class RedissonSemaphore extends RedissonExpirable implements RSemaphore {
    @Override
    public void release(int permits) {
        get(releaseAsync(permits));
    }
    
    @Override
    public RFuture<Void> releaseAsync() {
        return releaseAsync(1);
    }
    
    @Override
    public RFuture<Void> releaseAsync(int permits) {
        if (permits < 0) {
            throw new IllegalArgumentException("Permits amount can't be negative");
        }
        if (permits == 0) {
            return RedissonPromise.newSucceededFuture(null);
        }

        return commandExecutor.evalWriteAsync(getName(), StringCodec.INSTANCE, RedisCommands.EVAL_VOID,
            "local value = redis.call('incrby', KEYS[1], ARGV[1]); " +
            "redis.call('publish', KEYS[2], value); ",
            Arrays.<Object>asList(getName(), getChannelName()), permits);
    }
}

release semaphore 1,每次一个客户端释放掉这个锁的话,就会将信号量的值累加1,信号量的值就不是0了。

总结

这个组件还是比较简单的,实现了分布式信号量锁。

附上github链接:分布式锁和同步器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值