课程大纲
一、共享锁和排他锁的说明
- 共享锁:这份数据是共享的,可以多个线程同时过来获取同一个数据的,然后对这个数据执行读操作。
- 排他锁:是排他的操作,只能一个线程获取排他锁,然后执行增删改操作。
读写锁的分离
如果只是要读取数据的话,那么任意多个线程都可以同时读取数据,每个线程都可以加一个共享锁,但是这个时候,如果有线程要修改数据,那么这个线程就会尝试加排他锁,排他锁会跟共享锁互斥,也就是说,如果有线程加了共享锁,那么就不加排他锁,此时这个线程就必须等待共享锁释放。
这就是说,如果有线程在读数据,就不允许其他线程修改数据反之,也是一样的。如果线程在修改数据,就是加了排他锁。那么其他线程过来要修改数据,也会尝试加排他锁,此时会失败,同时只能有一个线程修改数据。同理,如果此时有线程读取数据,那么会尝试加共享锁,此时也会失败,因为共享锁和排他锁是冲突的。
二、共享锁和排他锁的实验
1、多线程同时加共享锁
有线程读数据,其他线程也能过来读数据
在elasticsearchconfigscripts目录下增加judge-lock-2.groovy脚本文件,文件内容为:if (ctx._source.lock_type == 'exclusive') { assert false }; ctx._source.lock_count++
(1)增加一个共享锁
POST /fs/lock/1/_update
{
"upsert": {
"lock_type": "shared",
"lock_count": 1
},
"script": {
"lang": "groovy",
"file": "judge-lock-2"
}
}
(2)其他线程再一次增加共享锁:
POST /fs/lock/1/_update
{
"upsert": {
"lock_type": "shared",
"lock_count": 1
},
"script": {
"lang": "groovy",
"file": "judge-lock-2"
}
}
(3)查看共享锁信息
GET /fs/lock/1
{
"_index": "fs",
"_type": "lock",
"_id": "1",
"_version": 2,
"found": true,
"_source": {
"lock_type": "shared",
"lock_count": 2
}
}
由上可以看出,对于同一份数据,可以由不同的线程增加共享锁。
2、在有共享锁的情况下,其他线程加排他锁
(1)再加排他锁
PUT /fs/lock/1/_create
{ "lock_type": "exclusive" }
排他锁用的不是upsert语法,用的是create语法,要求lock必须不能存在,也就是说要求上锁的线和是第一个上锁的,此时已有共享锁存在,显然lock这个type是存在的,所以会报错。
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[lock][1]: version conflict, document already exists (current version [3])",
"index_uuid": "IYbj0OLGQHmMUpLfbhD4Hw",
"shard": "3",
"index": "fs"
}
],
"type": "version_conflict_engine_exception",
"reason": "[lock][1]: version conflict, document already exists (current version [3])",
"index_uuid": "IYbj0OLGQHmMUpLfbhD4Hw",
"shard": "3",
"index": "fs"
},
"status": 409
}
3、对共享锁进行解锁
在elasticsearchconfigscripts目录下增加 unlock-shared.groovy脚本文件,文件内容为 if (--ctx.source.lock_count==0){ctx.op="delete"}
POST /fs/lock/1/_update
{
"script": {
"lang": "groovy",
"file": "unlock-shared"
}
}
连续解锁2次,此时共享锁就彻底没了。每次解锁一个共享锁,就对lock_count先减1,如果减了1之后是0,那么说明所有的共享锁都解锁完了,此时就就将/fs/lock/1删除,就彻底解锁所有的共享锁。在我的电脑上没有实验成功错误信息如下:
{
"error": {
"root_cause": [
{
"type": "remote_transport_exception",
"reason": "[SsqRO_3][127.0.0.1:9300][indices:data/write/update[s]]"
}
],
"type": "illegal_argument_exception",
"reason": "failed to execute script",
"caused_by": {
"type": "script_exception",
"reason": "error evaluating unlock-shared",
"caused_by": {
"type": "null_pointer_exception",
"reason": "Cannot get property 'lock_count' on null object"
},
"script_stack": [],
"script": "",
"lang": "groovy"
}
},
"status": 400
}
4、在已有排他锁的情况下,其他线程再加排他锁
PUT /fs/lock/1/_create
{ "lock_type": "exclusive" }
其他线程同时加锁
PUT /fs/lock/1/_create
{ "lock_type": "exclusive" }
错误信息
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[lock][1]: version conflict, document already exists (current version [7])",
"index_uuid": "IYbj0OLGQHmMUpLfbhD4Hw",
"shard": "3",
"index": "fs"
}
],
"type": "version_conflict_engine_exception",
"reason": "[lock][1]: version conflict, document already exists (current version [7])",
"index_uuid": "IYbj0OLGQHmMUpLfbhD4Hw",
"shard": "3",
"index": "fs"
},
"status": 409
}
5、在已有排他锁的情况下,其他线程加共享锁
POST /fs/lock/1/_update
{
"upsert": {
"lock_type": "shared",
"lock_count": 1
},
"script": {
"lang": "groovy",
"file": "judge-lock-2"
}
}
{
"error": {
"root_cause": [
{
"type": "remote_transport_exception",
"reason": "[4onsTYV][127.0.0.1:9300][indices:data/write/update[s]]"
}
],
"type": "illegal_argument_exception",
"reason": "failed to execute script",
"caused_by": {
"type": "script_exception",
"reason": "error evaluating judge-lock-2",
"caused_by": {
"type": "power_assertion_error",
"reason": "assert false "
},
"script_stack": [],
"script": "",
"lang": "groovy"
}
},
"status": 400
}
6、解锁排他锁
DELETE /fs/lock/1