
不做过多展开,本篇主要记录我对Redis如何保证原子性问题的思考,以及此问题衍生出的问题,如有错误和疑问欢迎大家在底部留言。
答案很简单,因为redis是单线程。
问题来了,既然Redis是单线程,可以保证原子性,那么它的异步和非阻塞是什么?单线程如何实现异步和非阻塞?
再查阅了很多资料之后,我发现我对于 同步/异步 、 阻塞/非阻塞 以及 单线程/多线程 的概念有些不清晰,下面给出简单解释。
同步/异步 :首先同步和异步主要是从消息通知机制来讲起的。
同步:一个任务的完成必须依赖另一个任务,两个要么都成功要么都失败,是一种可靠的任务序列。当一个同步调用发生后,调用者必须等待返回结果,才能继续后面任务的执行。
异步:不需要等待被依赖任务的完成,只需要完成自己的任务就可以,所以是不可靠任务序列。当一个异步调用发生后,调用者不必等待返回结果,调用者可以去做其他的事情,被调用部件在处理完成后,通过(状态、通知、回调)来通知调用者。
阻塞/非阻塞 :阻塞和非阻塞和调用者等待消息通知时的状态有关。很重要,不要和同步混淆。
阻塞:调用者在等待通知的过程中,不能执行其他业务,傻傻的等待通知到来。
非阻塞:和阻塞相反,调用者可以去执行其他业务。
我没有阅读源码,参考下面文章 Redis 网络架构及单线程模型
总结一下 :对于Redis的网络请求,Redis会有一个EventLoop,里面有两个数组events,fired。events存放被注册的事件,fired用于存放EventLoop从 多路复用器 (epoll)中读取到的,将要执行的事件。
异步和非阻塞就反映在这里,注册到 多路复用器 (epoll)后去做其他事,之后通过主动轮询多路复用器,来逐个取出将要执行的事件,放入fired,逐个执行,这个过程是单线程的,因此不会出现并发问题。
问题三:什么是多路复用?select 、poll、epoll的区别(待解决)
主要问题已经解决,这个问题等我整理好再发出来。
是在做项目过程中,多个客户端可能同时读写Redis数据库,set和get命令是否为原子 *** 作,关系到命令是否需要加锁机制的必要性。网上资料或者看书都说set和get等Redis命令为原子 *** 作,但是程序里怎么测试呢?
试想开1000个写线程,1000个读线程同时 *** 作Redis中的一个值,假如set和get不是原子的,那么当set的时候,把原来数据涂掉,还没来得及写进去,get *** 作已经执行,这个时候get到的值就可能为脏数据。如果get得到的值都为正确的数据,基本能判断set和get命令为原子 *** 作。
下面代码测试:
封装好的Redis读写类:
import java.util.HashMapimport java.util.Mapimport java.util.concurrent.locks.Lockimport java.util.concurrent.locks.ReadWriteLockimport java.util.concurrent.locks.ReentrantReadWriteLockimport redis.clients.jedis.Jedisimport redis.clients.jedis.JedisPoolimport redis.clients.jedis.JedisPoolConfigpublic class RedisUtils { private Jedis jedis private JedisPool jedisPool public RedisUtils() { initialPool() jedis = jedisPool.getResource() } private void initialPool() { JedisPoolConfig config = new JedisPoolConfig() config.setMaxActive(100) config.setMaxIdle(20) config.setMaxWait(1000l) config.setTestOnBorrow(false) jedisPool = new JedisPool(config, "127.0.0.1", 6379) } pu
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)