redistemplate 乐观锁实践

来源:互联网 发布:淘宝c店转让 编辑:程序博客网 时间:2024/05/16 15:04
    public Object testRedisWatch() {        try {            stringRedisTemplate.execute(new SessionCallback() {                @Override                public Object execute(RedisOperations operations) throws DataAccessException {                    operations.watch("testRedisWatch");                    operations.multi();                    operations.opsForValue().set("testRedisWatch", "0");                    try {                        Thread.sleep(5000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    Object rs = operations.exec();                    System.out.println(rs);                    return rs;                }            });        } catch (Exception e) {            e.printStackTrace();        }        return null;    }


初始值:

127.0.0.1:6389> get testRedisWatch

"initial"

考虑两种情况:

A

代码执行:

operations.watch("testRedisWatch");                    operations.multi();                    operations.opsForValue().set("testRedisWatch", "0");                    try {                        Thread.sleep(5000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }

代码执行:

Object rs = operations.exec();

输出:

[]

此时的值为:

127.0.0.1:6389> get testRedisWatch

"0"



表明watch与exec之间没有其它客户端改变值的情况下,rs输出非空对象



B

代码执行:

operations.watch("testRedisWatch");                    operations.multi();                    operations.opsForValue().set("testRedisWatch", "0");                    try {                        Thread.sleep(5000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }


客户端执行:

127.0.0.1:6389> set testRedisWatch another

OK



代码执行:

Object rs = operations.exec();


输出:

null

此时的最终值为:

127.0.0.1:6389> get testRedisWatch

"another"


代码中的

operations.opsForValue().set("testRedisWatch", "0");
失效了

表明watch与exec之间有其它客户端改变值的情况下,rs输出空对象,改值失败



参考:

SpringDataRedis事务处理

本文主要讲述如何在java里头使用redis进行cas操作。其实呢,redis不像memcached那样显示地支持cas操作,不过它有事务的概念。

准备

  • redis的docker搭建

  • SpringBoot应用之分布式缓存

redis的乐观锁支持

Redis通过使用WATCH, MULTI, and EXEC组成的事务来实现乐观锁(注意没有用DISCARD),Redis事务没有回滚操作。在SpringDataRedis当中通过RedisTemplate的SessionCallback中来支持(否则事务不生效)。discard的话不需要自己代码处理,callback返回null,成的话,返回非null,依据这个来判断事务是否成功(没有抛异常)。

实例

@Test    public void cas() throws InterruptedException, ExecutionException {        String key = "test-cas-1";        ValueOperations<String, String> strOps = redisTemplate.opsForValue();        strOps.set(key, "hello");        ExecutorService pool  = Executors.newCachedThreadPool();        List<Callable<Object>> tasks = new ArrayList<>();        for(int i=0;i<5;i++){            final int idx = i;            tasks.add(new Callable() {                @Override                public Object call() throws Exception {                    return redisTemplate.execute(new SessionCallback() {                        @Override                        public Object execute(RedisOperations operations) throws DataAccessException {                            operations.watch(key);                            String origin = (String) operations.opsForValue().get(key);                            operations.multi();                            operations.opsForValue().set(key, origin + idx);                            Object rs = operations.exec();                            System.out.println("set:"+origin+idx+" rs:"+rs);                            return rs;                        }                    });                }            });        }        List<Future<Object>> futures = pool.invokeAll(tasks);        for(Future<Object> f:futures){            System.out.println(f.get());        }        pool.shutdown();        pool.awaitTermination(1000, TimeUnit.MILLISECONDS);    }

输出

set:hello2 rs:nullset:hello3 rs:[]set:hello1 rs:nullset:hello4 rs:nullset:hello0 rs:null

我:其中1个拿到了锁,其余4个挂了

查看该值

127.0.0.1:6379> get test-cas-1"\"hello3\""

SessionCallback

没有在SessionCallback里头执行watch、multi、exec,而是自己单独写

与数据库事务的混淆

template.setEnableTransactionSupport(true);

这个应该是支持数据库的事务成功才执行的意思。

/**     * Gets a Redis connection. Is aware of and will return any existing corresponding connections bound to the current     * thread, for example when using a transaction manager. Will create a new Connection otherwise, if     * {@code allowCreate} is <tt>true</tt>.     *      * @param factory connection factory for creating the connection     * @param allowCreate whether a new (unbound) connection should be created when no connection can be found for the     *          current thread     * @param bind binds the connection to the thread, in case one was created     * @param enableTransactionSupport     * @return an active Redis connection     */    public static RedisConnection doGetConnection(RedisConnectionFactory factory, boolean allowCreate, boolean bind,            boolean enableTransactionSupport) {        Assert.notNull(factory, "No RedisConnectionFactory specified");        RedisConnectionHolder connHolder = (RedisConnectionHolder) TransactionSynchronizationManager.getResource(factory);        if (connHolder != null) {            if (enableTransactionSupport) {                potentiallyRegisterTransactionSynchronisation(connHolder, factory);            }            return connHolder.getConnection();        }        if (!allowCreate) {            throw new IllegalArgumentException("No connection found and allowCreate = false");        }        if (log.isDebugEnabled()) {            log.debug("Opening RedisConnection");        }        RedisConnection conn = factory.getConnection();        if (bind) {            RedisConnection connectionToBind = conn;            if (enableTransactionSupport && isActualNonReadonlyTransactionActive()) {                connectionToBind = createConnectionProxy(conn, factory);            }            connHolder = new RedisConnectionHolder(connectionToBind);            TransactionSynchronizationManager.bindResource(factory, connHolder);            if (enableTransactionSupport) {                potentiallyRegisterTransactionSynchronisation(connHolder, factory);            }            return connHolder.getConnection();        }        return conn;    }

不要跟本文的乐观锁说的事务混淆在一起。




原创粉丝点击