1 并发与并行![](https://img-blog.csdnimg.cn/20190712121612385.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
2 Runnbale与Callable的区别
![](https://img-blog.csdnimg.cn/20190712122613242.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
3 线程生命周期
![](https://img-blog.csdnimg.cn/2019071214123321.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
4 线程同步方式
![](https://img-blog.csdnimg.cn/20190712142037329.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
5 线程死锁
![](https://img-blog.csdnimg.cn/20190712143927446.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
6 产生死锁的必要条件
![](https://img-blog.csdnimg.cn/20190712144243659.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
7 线程死锁处理
![](https://img-blog.csdnimg.cn/20190712144417814.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
8 线程通讯
![](https://img-blog.csdnimg.cn/20190712145308671.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20190712145230454.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
9 java内存模型
![](https://img-blog.csdnimg.cn/20190712155210536.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
10 保证线程安全的三大特性
![](https://img-blog.csdnimg.cn/20190712172038145.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
11 为了保证线程安全三大特性,提供了以下控制类
![](https://img-blog.csdnimg.cn/2019071217304167.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
Threadlocal类
![](https://img-blog.csdnimg.cn/20190712174130382.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
原子类原理(CAS)
![](https://img-blog.csdnimg.cn/20190712180304676.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
原子类(CAS)导致的ABA问题
![](https://img-blog.csdnimg.cn/20190712182000706.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
Lock类
![](https://img-blog.csdnimg.cn/201907121821536.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
volatile变量(可见性)
![](https://img-blog.csdnimg.cn/20190712183000501.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
12 非线程安全容器
![](https://img-blog.csdnimg.cn/20190712195704824.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
13 非线程安全容器不安全原因
![](https://img-blog.csdnimg.cn/20190712200502286.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
14 并发容器
![](https://img-blog.csdnimg.cn/20190712201210190.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
15 redis队列,防止超卖
![](https://img-blog.csdnimg.cn/20190712203641495.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0Nzg5MQ==,size_16,color_FFFFFF,t_70)
redis
Redis是一个内存中的数据结构存储系统,它可以用作:数据库、缓存和消息中间件
#为什么要使用redis?
性能和并发
#性能?
我们在碰到需要执行耗时特别久,且结果不频繁变动的 SQL,就特别适合将运行结果放入缓存。
这样,后面的请求就去缓存中读取,使得请求能够迅速响应 #特别是在秒杀系统,在同一时间,
几乎所有人都在点,都在下单。。。执行的是同一操作———向数据库查数据
redis的速度:单机读可达10000次/s 写可达5000/s
# mysql 经过了这么多年优化 才1000次/S,500次/S
redis为何这么厉害?
redis 是操作内存,操作速度自然比mysql的磁盘操作要快太多了
redis单线程+多路I/O复用模型
单线程的模式解决了数据存储的顽疾:数据并发安全,任何运行多线程同时访问数据库都会存在这个问题,
所以才有了mysql的mvcc和锁, Memcached 的cas 乐观锁,来保证数据不会出现并发导致的数据问题,
但是redis 使用单线程就不存在这个问题:1,单线程足够简单,无论在redis的实现还是作为调用方,
都不需要为数据并发提心吊胆,不需要加锁。 2.不会出现不必要的线程调度,你知道多线程,
频繁切换上下文,也会带来很多性能消耗
#什么是切换上下文?
#线程每次执行需要把数据从主内存读到工作内存,然而当线程被调度到阻塞的时候,
这些工作内存的数据需要被快照到线程上下文中,其实就是一个记录各个线程状态的存储结构,
等到线程被唤醒的时候,
再从上下文中读取,称之为上下文切换;减少上下文切换操作,也是使用单线程的奥妙
再说 多路 I/O 复用模型,这个也是java 的NIO体系使用的IO模型,
也是linux诸多IO模型中的一种,说白了就是当一个请求来访问redis后,
redis去组织数据要返回给请求,这个时间段,redis的请求入口不是阻塞的,
其他请求可以继续向redis发送请求,等到redis io流完成后,再向调用者返回数据,
这样一来,单线程也不怕会影响速度了
# 这里“多路”指的是多个网络连接
# “复用”指的是复用同一个线程
##我们使用单线程的方式是无法发挥多核CPU 性能,不过我们可以通过在单机开多个Redis 实例来完善
#什么是缓存击穿?
缓存一般作为RDS的前置系统和服务器直连,减轻rds的负担,常理而言,
如果服务器查询缓存而不得的话,需要从rds中获取然后更新到缓存中,
但是如果在“从rds中获取然后更新到缓存中”,这个阶段,缓存尚未更新成功,
大量请求进来的话,rds势必压力暴增,甚至雪崩,或者歹人恶意攻击,
一直查询rds和缓存中未存在key,也会导致缓存机制失效,rds压力暴增,称之为缓存击穿
#那怎么办?
缓存永不失效,定时同步rds redis,不允许应用直接请求查询rds,所有的查询以缓存中为准
##并发
在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。
这个时候,就需要使用 Redis 做一个缓冲操作,让请求先访问到 Redis,而不是直接访问数据库
##Redis 的过期策略和内存淘汰机制
Redis 采用的是定期删除+惰性删除策略
"""""""""""""""""""""""""""""""""""""""""""""""""""""""
##采用定期删除+惰性删除就没其他问题了么
不是的,如果定期删除没删除掉 Key。并且你也没及时去请求 Key,
也就是说惰性删除也没生效。这样,Redis 的内存会越来越高
##如果一个键过期了,那么它什么时候会被删除呢?
定时删除:在设置键的过期时间的同时,创建一个定时器( timer ).
让定时器在键的过期时间来临时,立即执行对键的删除操作。
惰性删除:放任键过期不管,但是每次从键空间中获取键时,
都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。
定期删除: 每隔一段时间,程序就对数据库进行一次检查,
删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库, 则由算法决定。
在这三种策略中,第一种和第三种为主动删除策略, 而第二种则为被动删除策略
#定时删除
定时删除策略对内存是最友好的:通过使用定时器,
定时删除策略可以保证过期键会尽可能快地被删除,并释放过期键所占用的内存。
另一方面,定时删除策略的缺点是,它对CPU 时间是最不友好的:在过期键比较多的情况下,
删除过期键这一行为可能会占用相当一部分CPU 时间,在内存不紧张但是CPU 时间非常紧张的情况下.
将CPU 时间用在删除和当前任务无关的过期键上,无疑会对服务器的响应时间和吞吐量造成影响。
例如,如果正有大量的命令请求在等待服务器处理,并且服务器当前不缺少内存,
那么服务器应该优先将CPU 时间用在处理客户端的命令请求上面,而不是用在删除过期键上面。
除此之外,创建一个定时器需要用到Redis 服务器中的时间事件,
而当前时间事件的实现方式一一无序链表,
查找一个事件的时间复杂度为O(N)一并不能高效地处理大量时间事件。
因此,要让服务器创建大量的定时器,从而实现定时删除策略,在现阶段来说并不现实。
#惰性删除
惰性删除策略对CPU 时间来说是最友好的:程序只会在取出键时才对键进行过期检查,
这可以保证删除过期键的操作只会在非做不可的情况下进行,
并且删除的目标仅限于当前处理的键,这个策略不会在删除其他无关的过期键上花费任何CPU时间。
惰性删除策略的缺点是,它对内存是最不友好的: 如果一个键已经过期,
而这个键又仍然保留在数据库中,那么只要这个过期键不被删除,
它所占用的内存就不会释放。在使用惰性删除策略时,如果数据库中有非常多的过期键,
而这些过期键又恰好没有被访问到的话,那么它们也许永远也不会被删除(除非用户手动执行FLUSHDB),
我们甚至可以将这种情况看作是一种内存泄漏一一无用的垃圾数据占用了大量的内存,
而服务器却不会自己去释放它们,这对于运行状态非常依赖于内存的Redis服务器来说,
肯定不是一个好消息。
举个例子,对于一些和时间有关的数据,比如日志(log) ,在某个时间点之后,
对它们的访问就会大大减少,甚至不再访问,如果这类过期数据大量地积压在数据库中,
用户以为服务器已经自动将它们删除了,但实际上这些键仍然存在,
而且键所占用的内存也没有释放,那么造成的后果肯定是非常严重的。
#定期删除
从上面对定时删除和惰性删除的讨论来看,这两种删除方式在单一使用时都有明显的缺陷:
定时删除占用太多CPU 时间,影响服务器的响应时间和吞吐量。
惰性删除浪费太多内存,有内存泄漏的危险。
定期删除策略是前两种策略的一种整合和折中:
定期删除策略每隔一段时间执行一次删除过期键操作,
并通过限制删除操作执行的时长和频率来减少删除操作对CPU 时间的影响。
除此之外,通过定期删除过期键,定期删除策略有效地减少了因为过期键而带来的内存浪费。
定期删除策略的难点是确定删除操作执行的时长和频率:
如果删除操作执行得太频繁,或者执行的时间太长,定期删除策略就会退化成定时删除策略,
以至于将C P U 时间过多地消耗在删除过期键上面。
如果删除操作执行得太少,或者执行的时间太短,定期删除策略又会和惰性删除策略一样,
出现浪费内存的情况。
因此,如果采用定期删除策略的话,服务器必须根据情况,合理地设置删除操作的执行时长和执行频率。
""""""""""""""""""""""""""""""""""""""""""""""""""""
# 注意:如果对数据有强一致性要求,不能放缓存
#Redis的事务功能
Redis会将一个事务中的所有命令序列化,然后按顺序执行。
Redis不可能在一个Redis事务的执行过程中插入执行另一个客户端发出的请求。
这样便能保证Redis将这些命令作为一个单独的隔离操作执行。 > 在一个Redis事务中,
Redis要么执行其中的所有命令,要么什么都不执行。因此,Redis事务能够保证原子性。
EXEC命令会触发执行事务中的所有命令。因此,当某个客户端正在执行一次事务时,
如果它在调用MULTI命令之前就从Redis服务端断开连接,那么就不会执行事务中的任何操作;
相反,如果它在调用EXEC命令之后才从Redis服务端断开连接,那么就会执行事务中的所有操作
#为什么Redis不支持回滚?
如果你具备关系型数据库的知识背景,你就会发现一个事实:在事务运行期间,
虽然Redis命令可能会执行失败,但是Redis仍然会执行事务中余下的其他命令,
而不会执行回滚操作,你可能会觉得这种行为很奇怪然而,这种行为也有其合理之处:
只有当被调用的Redis命令有语法错误时,这条命令才会执行失败(
在将这个命令放入事务队列期间,Redis能够发现此类问题),
或者对某个键执行不符合其数据类型的操作:
实际上,这就意味着只有程序错误才会导致Redis命令执行失败,
这种错误很有可能在程序开发期间发现,一般很少在生产环境发现。
Redis已经在系统内部进行功能简化,这样可以确保更快的运行速度,
因为Redis不需要事务回滚的能力。
对于Redis事务的这种行为,有一个普遍的反对观点,那就是程序有可能会有缺陷(bug)。
但是,你应当注意到:事务回滚并不能解决任何程序错误。例如,如果某个查询会将一个键的值递增2,
而不是1,或者递增错误的键,那么事务回滚机制是没有办法解决这些程序问题的。请注意,
没有人能解决程序员自己的错误,这种错误可能会导致Redis命令执行失败。
正因为这些程序错误不大可能会进入生产环境,所以我们在开发Redis时选用更加简单和快速的方法,
没有实现错误回滚的功能
#redis的持久化
Redis也提供了持久化的选项,这些选项可以让用户将自己的数据保存到磁盘上面进行存储。
根据实际情况,可以每隔一定时间将数据集导出到磁盘(快照),
或者追加到命令日志中(AOF只追加文件),他会在执行写命令时,
将被执行的写命令复制到硬盘里面。您也可以关闭持久化功能,
将Redis作为一个高效的网络的缓存数据功能使用
###Redis不使用表,他的数据库不会预定义或者强制去要求用户对Redis存储的不同数据进行关联
Redis的应用
存储 缓存 用的数据;
需要高速读/写的场合使用它快速读/写;