我有三个线程:A,B和C。
A执行代码段
synchronized (redisClient.getJedis()) {
List<String> values = redisClient.getJedis().mget(keys.toArray(new String[keys.size()]));
}
B执行代码段
synchronized (redisClient.getJedis()) {
List<String> values = redisClient.getJedis().mget(keys.toArray(new String[keys.size()]));
}
C执行代码段
synchronized (redisClient.getJedis()) {
String result = redisClient.getJedis().get("FEATUREID#TEST");
}
redisClient.getJedis()
返回一个jedis实例。
有时候,当三个使用相同的jedis实例时,我发现A获得C的结果,B获得A的结果,C获得B的结果。
但是,正如您所看到的,我使用过synchronized
,为什么会发生这种情况?
RedisClient是使用RedisClient redisClient = RedisClientFactory.obtainRedisClient(REDIS_GROUP);
从RedisClientFactory获得的,REDIS_GROUP
是redis组名。
RedisClient类如下:
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class RedisClient {
private static final Logger logger = LoggerFactory.getLogger(RedisClient.class);
private Jedis jedis;
private JedisPoolProxy jedisPoolProxy;
public RedisClient(Jedis jedis, JedisPoolProxy jedisPoolProxy) {
this.jedis = jedis;
this.jedisPoolProxy = jedisPoolProxy;
}
public void returnResource() {
try {
if (jedis != null && jedisPoolProxy != null) {
jedisPoolProxy.getPool().returnResource(jedis);
}
} catch (Exception e) {
logger.error("", e);
if (jedis != null && jedisPoolProxy != null) {
jedisPoolProxy.getPool().returnBrokenResource(jedis);
}
} finally {
jedis = null;
jedisPoolProxy = null;
}
}
public Jedis getJedis() {
return jedis;
}
}
class JedisPoolProxy {
private static final Logger logger = LoggerFactory.getLogger(JedisPoolProxy.class);
private static final long BLOCKED_TIME = 10 * 1000;
String server;
JedisPool pool;
AtomicLong blockedTime = new AtomicLong(-1);
public JedisPoolProxy(String server, JedisPool pool) {
this.server = server;
this.pool = pool;
}
public boolean isBlocked() {
if (blockedTime.get() < 0) {
return false;
}
if (System.currentTimeMillis() - blockedTime.get() > BLOCKED_TIME) {
logger.info("server " + server + " recovers");
blockedTime.set(-1);
return false;
}
return true;
}
public String getServer() {
return server;
}
public void setServer(String server) {
this.server = server;
}
public JedisPool getPool() {
return pool;
}
public void setPool(JedisPool pool) {
this.pool = pool;
}
public long getBlockedTime() {
return blockedTime.get();
}
public void setBlockedTime(long blockedTime) {
logger.info("server " + server + " is blocked");
this.blockedTime.set(blockedTime);
}
}
RedisClientFactory类如下:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.configuration2.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@SuppressWarnings("Duplicates")
public class RedisClientFactory {
private static final Logger logger = LoggerFactory.getLogger(RedisClientFactory.class);
private static final int MAX_TOTAL = 30;
private static final int MAX_IDLE = 10;
private static final int MAX_WAIT_MILLIS = 1000;
private static final boolean TEST_ON_BORROW = false;
private static final boolean TEST_WHILE_IDLE = false;
private static final int TIMEOUT = 5000;
private static final String CONFIG_FILE = "redis.properties";
private static volatile boolean init = false;
private static Map<String, List<JedisPoolProxy>> groupedJedisPools = new HashMap<String, List<JedisPoolProxy>>();
public static RedisClient obtainRedisClient(String group) {
if (!init) {
init();
}
if (groupedJedisPools.get(group) == null) {
return null;
}
return obtainRedisClient(groupedJedisPools.get(group));
}
private static RedisClient obtainRedisClient(List<JedisPoolProxy> jedisPools) {
int idx = (int)(Math.random() * jedisPools.size());
for (int index = idx; index < jedisPools.size(); index++) {
if (!jedisPools.get(index).isBlocked()) {
try {
Jedis x = jedisPools.get(index).getPool().getResource();
if (x != null) {
return new RedisClient(x, jedisPools.get(index));
}
} catch (Throwable e) {
logger.error("", e);
jedisPools.get(index).setBlockedTime(System.currentTimeMillis());
}
}
}
for (int index = 0; index < idx; index++) {
if (!jedisPools.get(index).isBlocked()) {
try {
Jedis x = jedisPools.get(index).getPool().getResource();
if (x != null) {
return new RedisClient(x, jedisPools.get(index));
}
} catch (Throwable e) {
logger.error("", e);
jedisPools.get(index).setBlockedTime(System.currentTimeMillis());
}
}
}
return null;
}
private static void init() {
if (!init) {
synchronized (RedisClientFactory.class) {
if (!init) {
try {
Configuration configuration = ConfigLoadAssist.propConfig(CONFIG_FILE);
String groups = configuration.getString("groups");
if (StringAssist.isBlank(groups)) {
throw new RuntimeException("no groups");
}
String[] groupArray = groups.split(",");
for (String group : groupArray) {
logger.info("generate jedispools for group: " + group);
Configuration subConfiguration = configuration.subset(group);
groupedJedisPools.put(group, generateJedisPools(subConfiguration));
}
} catch (Exception e) {
logger.error("", e);
throw new RuntimeException(e);
} finally {
init = true;
}
}
}
}
}
private static List<JedisPoolProxy> generateJedisPools(Configuration configuration) {
List<JedisPoolProxy> jedisPools = new ArrayList<JedisPoolProxy>();
if (configuration == null) {
return jedisPools;
}
String servers = configuration.getString("redis.servers");
if (StringAssist.isBlank(servers)) {
logger.error("no redis servers");
return jedisPools;
}
String[] serverArray = servers.split(",");
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxActive(MAX_TOTAL);
config.setMaxIdle(MAX_IDLE);
config.setMaxWait(MAX_WAIT_MILLIS);
config.setTestOnBorrow(TEST_ON_BORROW);
config.setTestWhileIdle(TEST_WHILE_IDLE);
for (String server : serverArray) {
try {
String host = server.split(":")[0];
int port = Integer.parseInt(server.split(":")[1]);
JedisPoolProxy proxy = new JedisPoolProxy(server, new JedisPool(config, host, port, TIMEOUT));
jedisPools.add(proxy);
} catch (Exception e) {
logger.error(server, e);
}
}
logger.info("jedisPools size : " + jedisPools.size());
return jedisPools;
}
}