auto commit

This commit is contained in:
CyC2018 2018-06-13 15:55:10 +08:00
parent c41fd3731a
commit e805cf5536
2 changed files with 137 additions and 31 deletions

View File

@ -569,9 +569,9 @@ ReentrantLock 多了一些高级功能。
## join()
在线程中调用另一个线程的 join() 方法,会将当前线程挂起,而不是忙等待, 直到目标线程结束。
在线程中调用另一个线程的 join() 方法,会将当前线程挂起,而不是忙等待,直到目标线程结束。
对于以下代码,虽然 b 线程先启动,但是因为在 b 线程中调用了 a 线程的 join() 方法,因此 b 线程会等待 a 线程结束才继续执行,因此最后能够保证 a 线程的输出先于 b 线程的输出。
对于以下代码,虽然 b 线程先启动,但是因为在 b 线程中调用了 a 线程的 join() 方法b 线程会等待 a 线程结束才继续执行,因此最后能够保证 a 线程的输出先于 b 线程的输出。
```java
public class JoinExample {
@ -667,8 +667,8 @@ after
**wait() 和 sleep() 的区别**
1. wait() 是 Object 的方法,而 sleep() 是 Thread 的静态方法;
2. wait() 会释放锁sleep() 不会。
- wait() 是 Object 的方法,而 sleep() 是 Thread 的静态方法;
- wait() 会释放锁sleep() 不会。
## await() signal() signalAll()
@ -886,7 +886,7 @@ other task is running...
java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现:
- **FIFO 队列** LinkedBlockingQueue、ArrayListBlockingQueue固定长度
- **FIFO 队列** LinkedBlockingQueue、ArrayBlockingQueue固定长度
- **优先级队列** PriorityBlockingQueue
提供了阻塞的 take() 和 put() 方法:如果队列为空 take() 将阻塞,直到队列中有内容;如果队列为满 put() 将阻塞,直到队列有空闲位置。
@ -1387,7 +1387,9 @@ synchronized 和 ReentrantLock。
互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题因此这种同步也称为阻塞同步Blocking Synchronization
从处理问题的方式上说互斥同步属于一种悲观的并发策略总是认为只要不去做正确的同步措施例如加锁那就肯定会出现问题无论共享数据是否真的会出现竞争它都要进行加锁这里讨论的是概念模型实际上虚拟机会优化掉很大一部分不必要的加锁、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。随着硬件指令集的发展我们有了另外一个选择基于冲突检测的乐观并发策略通俗地说就是先进行操作如果没有其他线程争用共享数据那操作就成功了如果共享数据有争用产生了冲突那就再采取其他的补偿措施最常见的补偿措施就是不断地重试直到成功为止这种乐观的并发策略的许多实现都不需要把线程挂起因此这种同步操作称为非阻塞同步Non-Blocking Synchronization
从处理问题的方式上说,互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施(例如加锁),那就肯定会出现问题,无论共享数据是否真的会出现竞争,它都要进行加锁(这里讨论的是概念模型,实际上虚拟机会优化掉很大一部分不必要的加锁)、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。
随着硬件指令集的发展我们有了另外一个选择基于冲突检测的乐观并发策略通俗地说就是先进行操作如果没有其他线程争用共享数据那操作就成功了如果共享数据有争用产生了冲突那就再采取其他的补偿措施最常见的补偿措施就是不断地重试直到成功为止这种乐观的并发策略的许多实现都不需要把线程挂起因此这种同步操作称为非阻塞同步Non-Blocking Synchronization
乐观锁需要操作和冲突检测这两个步骤具备原子性这里就不能再使用互斥同步来保证了只能靠硬件来完成。硬件支持的原子性操作最典型的是比较并交换Compare-and-SwapCAS

View File

@ -6,7 +6,8 @@
* [SET](#set)
* [HASH](#hash)
* [ZSET](#zset)
* [三、使用场景](#三使用场景)
* [三、字典](#三字典)
* [四、使用场景](#四使用场景)
* [缓存](#缓存)
* [计数器](#计数器)
* [消息队列](#消息队列)
@ -15,28 +16,28 @@
* [排行榜](#排行榜)
* [分布式 Session](#分布式-session)
* [分布式锁](#分布式锁)
* [四、Redis 与 Memcached](#四redis-与-memcached)
* [五、Redis 与 Memcached](#五redis-与-memcached)
* [数据类型](#数据类型)
* [数据持久化](#数据持久化)
* [分布式](#分布式)
* [内存管理机制](#内存管理机制)
* [五、键的过期时间](#五键的过期时间)
* [六、数据淘汰策略](#六数据淘汰策略)
* [七、持久化](#七持久化)
* [六、键的过期时间](#六键的过期时间)
* [七、数据淘汰策略](#七数据淘汰策略)
* [八、持久化](#八持久化)
* [快照持久化](#快照持久化)
* [AOF 持久化](#aof-持久化)
* [八、发布与订阅](#八发布与订阅)
* [九、事务](#九事务)
* [、事件](#十事件)
* [九、发布与订阅](#九发布与订阅)
* [十、事务](#十事务)
* [、事件](#十事件)
* [文件事件](#文件事件)
* [时间事件](#时间事件)
* [事件的调度与执行](#事件的调度与执行)
* [一、复制](#十一复制)
* [二、复制](#十二复制)
* [连接过程](#连接过程)
* [主从链](#主从链)
* [二、Sentinel](#十二sentinel)
* [三、分片](#十三分片)
* [四、一个简单的论坛系统分析](#十四一个简单的论坛系统分析)
* [三、Sentinel](#十三sentinel)
* [四、分片](#十四分片)
* [五、一个简单的论坛系统分析](#十五一个简单的论坛系统分析)
* [文章信息](#文章信息)
* [点赞功能](#点赞功能)
* [对文章进行排序](#对文章进行排序)
@ -204,7 +205,111 @@ OK
2) "982"
```
# 三、使用场景
# 三、字典
以下是 Redis 字典的主要数据结构,从上往下分析,一个 dict 有两个 dictht一个 dictht 有一个 dictEntry 数组,每个 dictEntry 有 next 指针因此是一个链表结构。从上面的分析可以看出 Redis 的字典是一个基于拉链法解决冲突的哈希表结构。
```c
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
unsigned long iterators; /* number of iterators currently running */
} dict;
```
```c
/* This is our hash table structure. Every dictionary has two of this as we
* implement incremental rehashing, for the old to the new table. */
typedef struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
} dictht;
```
```c
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;
} dictEntry;
```
哈希表需要具备扩容能力,在扩容时就需要对每个键值对进行 rehash。dict 有两个 dictht在 rehash 的时候会将一个 dictht 上的键值对重新插入另一个 dictht 上面,完成之后释放空间并交换两个 dictht 的角色。
rehash 操作不是一次性完成,而是采用渐进方式,这是为了避免一次性执行过多的 rehash 操作给服务器带来过大的负担。
渐进式 rehash 通过记录 dict 的 rehashidx 完成,它从 0 开始然后每执行一次 rehash 都会递增。例如在一次 rehash 中,要把 dict[0] rehash 到 dict[1],这一次会把 dict[0] 上 table[rehashidx] 的键值对 rehash 到 dict[1] 上table[rehashidx] 指向 null并令 rehashidx++。
在 rehash 期间,每次对字典执行添加、删除、查找或者更新操作时,都会执行一次渐进式 rehash。
采用渐进式 rehash 会导致字典中的数据分散在两个 dict 上,因此对字典的操作也需要到对应的 dict 去执行。
```c
/* Performs N steps of incremental rehashing. Returns 1 if there are still
* keys to move from the old to the new hash table, otherwise 0 is returned.
*
* Note that a rehashing step consists in moving a bucket (that may have more
* than one key as we use chaining) from the old to the new hash table, however
* since part of the hash table may be composed of empty spaces, it is not
* guaranteed that this function will rehash even a single bucket, since it
* will visit at max N*10 empty buckets in total, otherwise the amount of
* work it does would be unbound and the function may block for a long time. */
int dictRehash(dict *d, int n) {
int empty_visits = n * 10; /* Max number of empty buckets to visit. */
if (!dictIsRehashing(d)) return 0;
while (n-- && d->ht[0].used != 0) {
dictEntry *de, *nextde;
/* Note that rehashidx can't overflow as we are sure there are more
* elements because ht[0].used != 0 */
assert(d->ht[0].size > (unsigned long) d->rehashidx);
while (d->ht[0].table[d->rehashidx] == NULL) {
d->rehashidx++;
if (--empty_visits == 0) return 1;
}
de = d->ht[0].table[d->rehashidx];
/* Move all the keys in this bucket from the old to the new hash HT */
while (de) {
uint64_t h;
nextde = de->next;
/* Get the index in the new hash table */
h = dictHashKey(d, de->key) & d->ht[1].sizemask;
de->next = d->ht[1].table[h];
d->ht[1].table[h] = de;
d->ht[0].used--;
d->ht[1].used++;
de = nextde;
}
d->ht[0].table[d->rehashidx] = NULL;
d->rehashidx++;
}
/* Check if we already rehashed the whole table... */
if (d->ht[0].used == 0) {
zfree(d->ht[0].table);
d->ht[0] = d->ht[1];
_dictReset(&d->ht[1]);
d->rehashidx = -1;
return 0;
}
/* More to rehash... */
return 1;
}
```
# 四、使用场景
## 缓存
@ -238,7 +343,7 @@ Redis 这种内存数据库能支持计数器频繁的读写操作。
除了可以使用 SETNX 命令实现分布式锁之外,还可以使用官方提供的 RedLock 分布式锁实现。
# 、Redis 与 Memcached
# 、Redis 与 Memcached
两者都是非关系型内存键值数据库。有以下主要不同:
@ -262,14 +367,13 @@ Redis Cluster 实现了分布式的支持。
Memcached 将内存分割成特定长度的块来存储数据,以完全解决内存碎片的问题,但是这种方式会使得内存的利用率不高,例如块的大小为 128 bytes只存储 100 bytes 的数据,那么剩下的 28 bytes 就浪费掉了。
# 五、键的过期时间
# 六、键的过期时间
Redis 可以为每个键设置过期时间,当键过期时,会自动删除该键。
对于散列表这种容器,只能为整个键设置过期时间(整个散列表),而不能为键里面的单个元素设置过期时间。
# 、数据淘汰策略
# 、数据淘汰策略
可以设置内存最大使用量,当内存使用量超过时施行淘汰策略,具体有 6 种淘汰策略。
@ -286,7 +390,7 @@ Redis 可以为每个键设置过期时间,当键过期时,会自动删除
作为内存数据库出于对性能和内存消耗的考虑Redis 的淘汰算法LRU、TTL实际实现上并非针对所有 key而是抽样一小部分 key 从中选出被淘汰 key。
# 、持久化
# 、持久化
Redis 是内存型数据库,为了保证数据在断电后不会丢失,需要将内存中的数据持久化到硬盘上。
@ -320,7 +424,7 @@ Redis 是内存型数据库,为了保证数据在断电后不会丢失,需
随着服务器写请求的增多AOF 文件会越来越大。Redis 提供了一种将 AOF 重写的特性,能够去除 AOF 文件中的冗余写命令。
# 、发布与订阅
# 、发布与订阅
订阅者订阅了频道之后,发布者向频道发送字符串消息会被所有订阅者接收到。
@ -333,7 +437,7 @@ Redis 是内存型数据库,为了保证数据在断电后不会丢失,需
<div align="center"> <img src="../pics//bee1ff1d-c80f-4b3c-b58c-7073a8896ab2.jpg" width="400"/> </div><br>
# 、事务
# 、事务
一个事务包含了多个命令,服务器在执行事务期间,不会改去执行其它客户端的命令请求。
@ -341,7 +445,7 @@ Redis 是内存型数据库,为了保证数据在断电后不会丢失,需
Redis 最简单的事务实现方式是使用 MULTI 和 EXEC 命令将事务操作包围起来。
# 十、事件
# 十、事件
Redis 服务器是一个事件驱动程序。
@ -416,7 +520,7 @@ def main():
<div align="center"> <img src="../pics//dda1608d-26e0-4f10-8327-a459969b150a.png" width=""/> </div><br>
# 十、复制
# 十、复制
通过使用 slaveof host port 命令来让一个服务器成为另一个服务器的从服务器。
@ -436,11 +540,11 @@ def main():
<div align="center"> <img src="../pics//395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png" width="600"/> </div><br>
# 十、Sentinel
# 十、Sentinel
Sentinel哨兵可以监听主服务器并在主服务器进入下线状态时自动从从服务器中选举出新的主服务器。
# 十、分片
# 十、分片
分片是将数据划分为多个部分的方法,可以将数据存储到多台机器里面,也可以从多台机器里面获取数据,这种方法在解决某些问题时可以获得线性级别的性能提升。
@ -452,7 +556,7 @@ Sentinel哨兵可以监听主服务器并在主服务器进入下线状
- 代理分片:将客户端请求发送到代理上,由代理转发请求到正确的节点上。
- 服务器分片Redis Cluster。
# 十、一个简单的论坛系统分析
# 十、一个简单的论坛系统分析
该论坛系统功能如下: