高并发下修改同天恒SSC平台制作Q> 1279829431【源码链接】dashengba.com 一个key遇到的问题:
1)定义一个hash类型的key,key为:lock_test,元素locker的值初始化为0。
2)实现高并发下对locker元素的值递增:定义64个多线程,并发的对lock_test元素locker的值进行修改。
package com.dx.es;
import java.util.concurrent.CountDownLatch;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class Test_UnLock {
public static void main(String[] args) {
final JedisPool pool = RedisUtil.getPool();
// 获得jedis对象
Jedis jedis = pool.getResource();
jedis.hset("lock_test", "locker", "0");
String val = jedis.hget("lock_test", "locker");
System.out.println("lock_test.locker的初始值為:" + val);
jedis.close();
int threahSize = 64;
final CountDownLatch threadsCountDownLatch = new CountDownLatch(threahSize);
Runnable handler = new Runnable() {
public void run() {
Jedis jedis = pool.getResource();
Integer integer = Integer.valueOf(jedis.hget("lock_test", "locker"));
jedis.hset("lock_test", "locker", String.valueOf(integer + 1));
jedis.close();
threadsCountDownLatch.countDown();
}
};
for (int i = 0;
i < threahSize;
i++) {
new Thread(handler).start();
}
// 等待所有并行子线程任务完成。
try {
threadsCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("complete");
val = jedis.hget("lock_test", "locker");
System.out.println(val);
}
}
RedisUtil.java
package com.dx.es;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisUtil {
public static JedisPool getPool() {
// 简单创建 Jedis的方法:
// final Jedis jedis = new Jedis("127.0.0.1",6379);
// 下边使用线程池的方案:jedis对象是线程不安全的,因此在并发情况下要使用JedisPool,默认情况下jedisPool只支持8个连接,因此在声明JedisPool时要先修改JedisPool的最大连接数
JedisPoolConfig config = new JedisPoolConfig();
// 修改最大连接数
config.setMaxTotal(32);
// 声明一个线程池
JedisPool pool = new JedisPool(config, "127.0.0.1", 6379);
return pool;
}
}
此时,会出现以下问题:
A线程获取key的值为0,而B线程也获取jkey的值0,则A把key值递增为1,B线程也实现把key值递增为1。两个线程都执行了key值修改:0到1。
在1)中最终key修改为了1,但是c线程获取key的值为0(因为c线程读取key值时,a、b线程还未触发修改,因此c线程读取到的值为0),此时d线程读取到的值为1(因为d线程读取key值时,a、b线程已触发修改,一次d线程取到的值为1)。
此时假设d线程优先触发递增,则在c线程未触发提交之前d线程已经把值修改了2,但是c此时并不知道在它获取到值到修改之前这段时间发生了什么,直接把值修改1。
此时执行打印结果为:
lock_test.locker的初始值為:0
complete
24 #备注:也可能是其他值,可能是正确值64的可能性比较小。
通过watch+mutil解决并发修改的问题:
需要掌握Redis 事务命令:
文章图片
Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:
批量操作在发送 EXEC 命令前被放入队列缓存。
收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历以下三个阶段:
开始事务。
命令入队。
执行事务。
备注:概念性摘自《http://www.runoob.com/redis/redis-transactions.html》
redis-cli.exe下的事务操作:
# 事务被成功执行
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> INCR user_id
QUEUED
redis 127.0.0.1:6379> INCR user_id
QUEUED
redis 127.0.0.1:6379> INCR user_id
QUEUED
redis 127.0.0.1:6379> PING
QUEUED
redis 127.0.0.1:6379> EXEC
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) PONG
并发情况下使用watch+mutil操作:
事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil 。
A线程:
# 监视 key ,且事务成功执行
redis 127.0.0.1:6379> WATCH lock lock_times
OK
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> SET lock "huangz"
QUEUED
redis 127.0.0.1:6379> INCR lock_times
QUEUED
redis 127.0.0.1:6379> EXEC
1) OK
2) (integer) 1
B线程:
# 监视 key ,且事务被打断
redis 127.0.0.1:6379> WATCH lock lock_times
OK
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> SET lock "joe" # 就在这时,另一个客户端修改了 lock_times 的值
QUEUED
redis 127.0.0.1:6379> INCR lock_times
QUEUED
redis 127.0.0.1:6379> EXEC # 因为 lock_times 被修改, joe 的事务执行失败
(nil)
上边演示了A、B线程并发下的watch+mutil操作情况。
解决高并发下修改同一个key遇到的问题:
package com.dx.es;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;
public class Test_Lock3 {
public static void main(String[] args) {
final JedisPool pool = RedisUtil.getPool();
// 对测试key赋初始值
Jedis jedis = pool.getResource();
jedis.hset("lock_test", "locker", "0");
String val = jedis.hget("lock_test", "locker");
System.out.println("lock_test.locker的初始值為:" + val);
jedis.close();
int threahSize = 64;
final CountDownLatch threadsCountDownLatch = new CountDownLatch(threahSize);
Runnable handler = new Runnable() {
public void run() {
Jedis jedis = pool.getResource();
while (true) {
jedis.watch("lock_test");
String val = jedis.hget("lock_test", "locker");
Integer integer = Integer.valueOf(val);
Transaction tx = jedis.multi();
tx.hset("lock_test", "locker", String.valueOf(integer + 1));
List
推荐阅读