用到的jar包:
<!-- Redis客户端 --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.7.2</version> </dependency> <!-- redisson --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.8.1</version> </dependency>
测试代码:
package redis; import org.redisson.Redisson; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; public class RedisThread { public static void main1(String[] args) { for (int i = 0; i < 3; i++) { // final int k = i; new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 10; j++) { int k = j; // 连接本地的 Redis 服务 // Jedis jedis = new Jedis("localhost"); // 创建连接池对象 JedisPool jedisPool = new JedisPool("127.0.0.1", 6379); // 从连接池中获取一个jedis对象 Jedis jedis = jedisPool.getResource(); // synchronized (RedisThread.class) { String v = jedis.get("a" + k); // if (v == null) { if (!jedis.exists("a" + k)) { jedis.set("a" + k, Thread.currentThread().getName()); jedis.expire("a" + k, 60); System.out.println(System.currentTimeMillis() + "--" + Thread.currentThread().getName() + "--key:" + ("a" + k) + "不存在,设置值为: " + Thread.currentThread().getName()); try { // Thread.sleep((long) (Math.random()*1000)); } catch (Exception e) { e.printStackTrace(); } } else { System.out.println(Thread.currentThread().getName() + "--key:" + ("a" + k) + "存在,值为: " + v); } // } jedis.close(); } } }).start(); } } /* 不加锁的运行结果: 可以看到一个键比如 a0被赋值了很多次,说明有线程安全问题(原因是getKey 和 setKey 的方法不同步), * 如果在单服务环境可以用synchronized来解决,但是如果分布式多节点服务,synchronized 就无效了 * * 1556503338616--Thread-2--key:a0不存在,设置值为: Thread-2 * 1556503338616--Thread-1--key:a0不存在,设置值为: Thread-1 * 1556503338616--Thread-0--key:a0不存在,设置值为: Thread-0 * 1556503338622--Thread-2--key:a1不存在,设置值为: Thread-2 Thread-0--key:a1存在,值为: * Thread-2 1556503338622--Thread-1--key:a1不存在,设置值为: Thread-1 * 1556503338627--Thread-2--key:a2不存在,设置值为: Thread-2 * 1556503338627--Thread-0--key:a2不存在,设置值为: Thread-0 * 1556503338628--Thread-1--key:a2不存在,设置值为: Thread-1 * 1556503338634--Thread-2--key:a3不存在,设置值为: Thread-2 * 1556503338634--Thread-0--key:a3不存在,设置值为: Thread-0 * 1556503338634--Thread-1--key:a3不存在,设置值为: Thread-1 * 1556503338644--Thread-2--key:a4不存在,设置值为: Thread-2 */ public static void main(String[] args) { //获取redisson Config config = new Config(); config.useSingleServer().setAddress("redis://localhost:6379"); RedissonClient redisson = Redisson.create(config); //获取锁 RLock lock = redisson.getLock("mylock"); for (int i = 0; i < 3; i++) { // final int k = i; new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 10; j++) { int k = j; // 连接本地的 Redis 服务 // Jedis jedis = new Jedis("localhost"); // 创建连接池对象 JedisPool jedisPool = new JedisPool("127.0.0.1", 6379); try { //加分布式锁 lock.lock(); // 从连接池中获取一个jedis对象 Jedis jedis = jedisPool.getResource(); if (!jedis.exists("a" + k)) { jedis.set("a" + k, Thread.currentThread().getName()); jedis.expire("a" + k, 60); System.out.println(System.currentTimeMillis() + "--" + Thread.currentThread().getName() + "--key:" + ("a" + k) + "不存在,设置值为: " + Thread.currentThread().getName()); try { // Thread.sleep((long) (Math.random()*1000)); } catch (Exception e) { e.printStackTrace(); } } else { System.out.println(Thread.currentThread().getName() + "--key:" + ("a" + k) + "存在,值为: " + jedis.get("a" + k)); } jedis.close(); } finally { //释放分布式锁 lock.unlock(); } } } }).start(); } } /* 加上分布式锁后的运行结果: 可以看到每一个键比如 a0只被赋值了一次,说明没有线程安全问题了 * (在这个例子中,redisson 连接redis只是用来做分布式锁,真正的业务中的redis操作用的还是其他连接方式,比如 jedisPool 等) * * 1556505103008--Thread-4--key:a0不存在,设置值为: Thread-4 Thread-2--key:a0存在,值为: * Thread-4 Thread-3--key:a0存在,值为: Thread-4 * 1556505103035--Thread-4--key:a1不存在,设置值为: Thread-4 Thread-2--key:a1存在,值为: * Thread-4 Thread-3--key:a1存在,值为: Thread-4 * 1556505103058--Thread-4--key:a2不存在,设置值为: Thread-4 Thread-2--key:a2存在,值为: * Thread-4 Thread-3--key:a2存在,值为: Thread-4 * 1556505103080--Thread-4--key:a3不存在,设置值为: Thread-4 Thread-2--key:a3存在,值为: * Thread-4 Thread-3--key:a3存在,值为: Thread-4 * 1556505103100--Thread-4--key:a4不存在,设置值为: Thread-4 Thread-2--key:a4存在,值为: * Thread-4 Thread-3--key:a4存在,值为: Thread-4 * 1556505103126--Thread-4--key:a5不存在,设置值为: Thread-4 Thread-2--key:a5存在,值为: * Thread-4 Thread-3--key:a5存在,值为: Thread-4 * 1556505103146--Thread-2--key:a6不存在,设置值为: Thread-2 Thread-4--key:a6存在,值为: * Thread-2 Thread-3--key:a6存在,值为: Thread-2 * 1556505103165--Thread-4--key:a7不存在,设置值为: Thread-4 Thread-2--key:a7存在,值为: * Thread-4 Thread-3--key:a7存在,值为: Thread-4 * 1556505103186--Thread-4--key:a8不存在,设置值为: Thread-4 Thread-2--key:a8存在,值为: * Thread-4 1556505103197--Thread-4--key:a9不存在,设置值为: Thread-4 * Thread-2--key:a9存在,值为: Thread-4 Thread-3--key:a8存在,值为: Thread-4 * Thread-3--key:a9存在,值为: Thread-4 */ }
---------------------------------------------------
注意:
如果不是想用分布式锁解决其他业务逻辑问题,而只是为了解决本文标题说的向redis中存入取出值(如果存在就取出,如果不存在就存入)的问题,那么完全可以用下面的方法来实现
其原理就是下面这个方法:
// jedis.set("key", "value", "nx", "ex", 50L); //第一个参数:key,第二个参数:value,第三、四个参数固定写法,第五个参数:超时毫秒值
// 上面这个方法其实就是redis的 setnx 和 expire 组合在一起的原子指令 (据说其是Redis2.8版本增加的新特性,但是我在2.4版本居然也能用)
public static void main(String[] args) { for (int i = 0; i < 3; i++) { new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 10; j++) { int k = j; // 连接本地的 Redis 服务 // Jedis jedis = new Jedis("localhost"); // 创建连接池对象 JedisPool jedisPool = new JedisPool("127.0.0.1", 6379); // 从连接池中获取一个jedis对象 Jedis jedis = jedisPool.getResource(); String setResult = jedis.set("a" + k, Thread.currentThread().getName(), "nx", "ex", 50L); if (setResult == null) { // 说明已经存在,设置值失败 System.out.println(Thread.currentThread().getName() + "--key:" + ("a" + k) + "存在,值为: " + jedis.get("a" + k)); } else { // 说明设置值成功 System.out.println(System.currentTimeMillis() + "--" + Thread.currentThread().getName() + "--key:" + ("a" + k) + "不存在,设置值为: " + Thread.currentThread().getName()); } jedis.close(); } } }).start(); } } // 运行结果: 说明这是没有问题的 // 也就是说,如果不是想用分布式锁解决其他业务逻辑问题,而只是为了解决向redis中存入取出值(如果存在就取出,如果不存在就存入)的问题,那么完全可以用上面的方法来实现 // 其原理就是下面这个方法: // jedis.set("key", "value", "nx", "ex", 50L); //第一个参数:key,第二个参数:value,第三、四个参数固定写法,第五个参数:超时毫秒值 // 上面这个方法其实就是redis的 setnx 和 expire 组合在一起的原子指令 (据说其是Redis2.8版本增加的新特性,但是我在2.4版本居然也能用) // 1556588977532--Thread-0--key:a0不存在,设置值为: Thread-0 // Thread-1--key:a0存在,值为: Thread-0 // Thread-2--key:a0存在,值为: Thread-0 // 1556588977535--Thread-0--key:a1不存在,设置值为: Thread-0 // Thread-2--key:a1存在,值为: Thread-0 // Thread-1--key:a1存在,值为: Thread-0 // 1556588977538--Thread-0--key:a2不存在,设置值为: Thread-0 // Thread-2--key:a2存在,值为: Thread-0 // Thread-1--key:a2存在,值为: Thread-0 // 1556588977541--Thread-0--key:a3不存在,设置值为: Thread-0 // 1556588977545--Thread-0--key:a4不存在,设置值为: Thread-0 // Thread-1--key:a3存在,值为: Thread-0 // Thread-2--key:a3存在,值为: Thread-0 // Thread-2--key:a4存在,值为: Thread-0 // 1556588977553--Thread-0--key:a5不存在,设置值为: Thread-0 // Thread-1--key:a4存在,值为: Thread-0 // 1556588977558--Thread-0--key:a6不存在,设置值为: Thread-0 // 1556588977562--Thread-0--key:a7不存在,设置值为: Thread-0 // Thread-1--key:a5存在,值为: Thread-0 // Thread-2--key:a5存在,值为: Thread-0 // 1556588977566--Thread-0--key:a8不存在,设置值为: Thread-0 // Thread-2--key:a6存在,值为: Thread-0 // Thread-1--key:a6存在,值为: Thread-0 // 1556588977578--Thread-0--key:a9不存在,设置值为: Thread-0 // Thread-2--key:a7存在,值为: Thread-0 // Thread-1--key:a7存在,值为: Thread-0 // Thread-2--key:a8存在,值为: Thread-0 // Thread-1--key:a8存在,值为: Thread-0 // Thread-2--key:a9存在,值为: Thread-0 // Thread-1--key:a9存在,值为: Thread-0