redisson 分布式锁及常用示例

需求背景:

在分布式多机部署的情况下,我们要求某一个方法,只能同一个时间只能被一台机器的一个线程执行。

在单机中,有synchronized,读写锁等方式可以解决同步问题。 但是,这些只能作用在同一个机器上,只能保证某一个机器中的方法,不会同时执行。多台机器还是可以同时执行。

这时,就需要借助介质redisson,基于redis的分布式锁。。

前提不多说了,先安装好redis,使用的Redis主从+哨兵模式。这里多台机器安装的redis哨兵,也可以单机多端口安装

添加依赖:

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.8.2</version>
</dependency>

1.1首先创建RedissonManager 获取redisson

package com.test.redisson.manager;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
/**
 * @version 1.0.0
 * @description
 * @date 2018/05/04 16:54
 **/
public class RedissonManager {

    private static RedissonClient redissonClient;
    private static Config config = new Config();
    /**
     * 初始化Redisson,使用哨兵模式
     */
    public static void init(){
        try {
            config.useSentinelServers()
                    .setMasterName("cache")
                    .addSentinelAddress("10.0.0.1:26379","10.0.0.2:26379", "10.0.0.3:26379")
                    //同任何节点建立连接时的等待超时。时间单位是毫秒。默认:10000
                    .setConnectTimeout(30000)
                    //当与某个节点的连接断开时,等待与其重新建立连接的时间间隔。时间单位是毫秒。默认:3000
                    .setReconnectionTimeout(10000)
                    //等待节点回复命令的时间。该时间从命令发送成功时开始计时。默认:3000
                    .setTimeout(10000)
                    //如果尝试达到 retryAttempts(命令失败重试次数) 仍然不能将命令发送至某个指定的节点时,将抛出错误。如果尝试在此限制之内发送成功,则开始启用 timeout(命令等待超时) 计时。默认值:3
                    .setRetryAttempts(5)
                    //在一条命令发送失败以后,等待重试发送的时间间隔。时间单位是毫秒。     默认值:1500
                    .setRetryInterval(3000)
            ;
            redissonClient = Redisson.create(config);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 获取Redisson的实例对象
     * @return
     */
    public static Redisson getRedisson(){
        init();
        return (Redisson) redissonClient;
    }
    /**
     * 测试Redisson是否正常
     */
    public static void main(String[] args) {
        Redisson redisson = RedissonManager.getRedisson();
        System.out.println("redisson = " + redisson);
    }
}

1.2 锁操作LockUtil

package com.test.redisson.lockutil;
import com.test.redisson.manager.RedissonManager;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import java.util.concurrent.TimeUnit;
/**
 * Redisson分布式锁 工具类
 */
public class LockUtil {
    private static Redisson redisson = RedissonManager.getRedisson();
    /**
     * 根据name对进行上锁操作,redisson Lock 一直等待获取锁
     * @param lockname
     */
    public static void lock(String lockname) throws InterruptedException {
        String key = lockname;
        RLock lock = redisson.getLock(key);
        //lock提供带timeout参数,timeout结束强制解锁,防止死锁 
        lock.lock(60L, TimeUnit.SECONDS);
    }

    /**
     * 根据name对进行上锁操作,redisson tryLock  根据第一个参数,一定时间内为获取到锁,则不再等待直接返回boolean。交给上层处理
     * @param lockname
     */
    public static boolean tryLock(String lockname) throws InterruptedException {
        String key = lockname;
        RLock lock = redisson.getLock(key);
        //tryLock,第一个参数是等待时间,5秒内获取不到锁,则直接返回。 第二个参数 60是60秒后强制释放
        return lock.tryLock(5L,60L, TimeUnit.SECONDS);
    }
    /**
     * 根据name对进行解锁操作
     * @param lockname
     */
    public static void unlock(String lockname){
        String key = lockname;
        RLock lock = redisson.getLock(key);
        lock.unlock();
    }
}

1.3 RedissonTestLock(测试封装方法)

package com.test.redisson.test;
import com.test.redisson.lockutil.LockUtil;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * @version 1.0.0
 * @description
 * @date 2018/05/04 17:02
 **/
public class RedissonTestLock {
    public static void main(String[] args) {
        //模拟2个线程
        for (int i = 1; i <= 2; i++) {
            //可以开2个IDE,分别测试以下三个方法
            //打开2个IDE同时执行时,这里可以分别取不同名,区分
            new Thread("IDE-ONE-"+i) {
                @Override
                public void run() {
                    /**
                     * 测试testLock结果,每个IDE中线程,依次排队等待获取锁。然后执行任务
                     */
                    testLock("redissonlocktest_testkey");

                    /**
                     * 测试testTryLock结果,每个IDE中线程,在TryLock的等待时间范围内,若获取到锁,返回true,则执行任务;若获取不到,则返回false,直接返回return;
                     */
//                    testTryLock("redissonlocktest_testkey");

                    /**
                     * 测试testSyncro结果,IDE之间的线程互不影响,同一个IDE中的线程排队值执行,不同IDE之间的互补影响,可同时执行
                     */
//                    testSyncro("redissonlocktest_testkey");
                }
            }.start();
        }
    }

    //测试lock,拿不到lock就不罢休,不然线程就一直block。
    public static void testLock(String preKey) {
        try {
            System.out.println(getDate()+Thread.currentThread().getName() + "准备开始任务。。。。");
            LockUtil.lock(preKey);
            System.out.println(getDate()+Thread.currentThread().getName() + "模拟正在执行任务。。。。");
            Thread.sleep(5000);//等待5秒,后面的所有线程都“依次”等待5秒,等待获取锁,执行任务

        } catch (Exception e) {
            System.out.println(getDate()+"线程锁 :" + Thread.currentThread().getId() + " exception :" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            e.printStackTrace();
        } finally {
            LockUtil.unlock(preKey);
            System.out.println(getDate()+Thread.currentThread().getName() + "释放。。。。");
        }
    }

    //带时间限制的tryLock(),拿不到lock,就等一段时间,超时返回false。
    public static void testTryLock(String preKey) {

        try {
            System.out.println(getDate()+Thread.currentThread().getName() + "准备开始任务。。。。");
            boolean falg = LockUtil.tryLock(preKey);
            //这里若获取不到锁,就直接返回了
            if(!falg){
                System.out.println(getDate()+Thread.currentThread().getName() + "--没有获取到锁直接返回--" + falg);
                return;
            }
            System.out.println(getDate()+Thread.currentThread().getName() + "--获取锁--" + falg);
            System.out.println(getDate()+Thread.currentThread().getName() + "模拟正在执行任务。。。。");

           //由于在LockUtil.tryLock设置的等待时间是5s,所以这里如果休眠的小于5秒,这第二个线程能获取到锁,
            // 如果设置的大于5秒,则剩下的线程都不能获取锁。可以分别试试2s,和8s的情况
            Thread.sleep(8000);//等待6秒

        } catch (Exception e) {
            System.out.println(getDate()+"线程锁 :" + Thread.currentThread().getId() + " exception :" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            e.printStackTrace();
        } finally {
            try {
                LockUtil.unlock(preKey);
                System.out.println(getDate()+Thread.currentThread().getName() + "释放。。。。");
            }catch (Exception e){
                e.printStackTrace();
            }

        }
    }

    //synchronized 这种锁只能锁住同一台机器的线程,若部署多台机器,则不能锁住
    public static void testSyncro(String preKey) {
        synchronized (preKey.intern()){//为什么要intern前篇文章有解释
            try {
                System.out.println(getDate()+Thread.currentThread().getName() + "准备开始任务。。。。");
                System.out.println(getDate()+Thread.currentThread().getName() + "--获取锁--" );
                System.out.println(getDate()+Thread.currentThread().getName() + "模拟正在执行任务。。。。");
                Thread.sleep(6000);//执行2秒
            } catch (Exception e) {
                System.out.println(getDate()+"线程锁 :" + Thread.currentThread().getId() + " exception :" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                e.printStackTrace();
            } finally {
                try {
                    System.out.println(getDate()+Thread.currentThread().getName() + "释放。。。。");
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
    public static String getDate(){
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"   ";
    }
}

redisson分布式锁的常用示例

模拟多线程竞争同一个资源,利用redisson分布式锁实现

 public void testMultiThread() throws Exception{
        final Config config = new Config();
        config.useSingleServer().setAddress("192.168.3.199:6379").setConnectionPoolSize(5);
        //开启多线程去竞争同一个资源锁
        for (int i = 0; i < 12; i++) {
            new Thread(new Runnable() {
                @SuppressWarnings("static-access")
                @Override
                public void run() {
                    RedissonClient redisson = Redisson.create(config);
                    RLock lock = redisson.getLock("anyLock"); //设置锁对象
                    boolean res = false;
                    try {
                        // 尝试加锁,最多等待10秒,上锁以后3秒自动解锁
                        res = lock.tryLock(10, 3, TimeUnit.SECONDS);
                      if(res){//获取锁成功
                        try { 
                            //业务操作 
                            Thread.currentThread().sleep(new Random().nextInt(2)*1000); 
                        }finally{ 
                            lock.unlock(); 
                            } 
                        } 
                      System.err.println("ThreadID:"+Thread.currentThread().getId()+",res="+res); } 
                    catch (InterruptedException e) { 
                        log("尝试获取锁异常:"+e); 
                    } 
                }}).start(); 
            } 
        }
发布了14 篇原创文章 · 获赞 2 · 访问量 807

猜你喜欢

转载自blog.csdn.net/breakaway_01/article/details/102724577
今日推荐