精华内容
下载资源
问答
  • 一、共享锁和排他锁的说明 共享锁:这份数据是共享的,可以多个线程同时过来获取同一个数据的,然后对这个数据执行读操作。 排他锁:是排他的操作,只能一个线程获取排他锁,然后执行增删改操作。 ...

       

    课程大纲

       

    一、共享锁和排他锁的说明

       

    • 共享锁:这份数据是共享的,可以多个线程同时过来获取同一个数据的,然后对这个数据执行读操作。
    • 排他锁:是排他的操作,只能一个线程获取排他锁,然后执行增删改操作。

       

    读写锁的分离

    如果只是要读取数据的话,那么任意多个线程都可以同时读取数据,每个线程都可以加一个共享锁,但是这个时候,如果有线程要修改数据,那么这个线程就会尝试加排他锁,排他锁会跟共享锁互斥,也就是说,如果有线程加了共享锁,那么就不加排他锁,此时这个线程就必须等待共享锁释放。

    这就是说,如果有线程在读数据,就不允许其他线程修改数据反之,也是一样的。如果线程在修改数据,就是加了排他锁。那么其他线程过来要修改数据,也会尝试加排他锁,此时会失败,同时只能有一个线程修改数据。同理,如果此时有线程读取数据,那么会尝试加共享锁,此时也会失败,因为共享锁和排他锁是冲突的。

       

    二、共享锁和排他锁的实验

       

    1、多线程同时加共享锁

       

    有线程读数据,其他线程也能过来读数据

    \elasticsearch\config\scripts\目录下增加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、对共享锁进行解锁

    \elasticsearch\config\scripts\目录下增加 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\n"

    },

    "script_stack": [],

    "script": "",

    "lang": "groovy"

    }

    },

    "status": 400

    }

       

    6、解锁排他锁

       

    DELETE /fs/lock/1

       

    转载于:https://www.cnblogs.com/liuqianli/p/8542134.html

    展开全文
  • 1、共享锁和排他锁的说明 共享锁和排他锁概念 共享锁:这份数据是共享的,然后多个线程过来,都可以获取同一个数据的共享锁,然后对这个数据执行读操作 排他锁:是排他的操作,只能一个线程获取排他锁,然后执行...

     

    1、共享锁和排他锁的说明

    共享锁和排他锁概念

    共享锁:这份数据是共享的,然后多个线程过来,都可以获取同一个数据的共享锁,然后对这个数据执行读操作

    排他锁:是排他的操作,只能一个线程获取排他锁,然后执行增删改操作

    共享锁和排他锁举例说明

    读写锁的分离

     

    如果只是要读取数据的话,那么任意个线程都可以同时进来然后读取数据,每个线程都可以上一个共享锁

    但是这个时候,如果有线程要过来修改数据,那么会尝试上排他锁,排他锁会跟共享锁互斥,也就是说,如果有人已经上了共享锁了,那么排他锁就不能上,就得等

     

    如果有人在读数据,就不允许别人来修改数据

     

    反之,也是一样的

     

    如果有人在修改数据,就是加了排他锁

    那么其他线程过来要修改数据,也会尝试加排他锁,此时会失败,锁冲突,必须等待,同时只能有一个线程修改数据

    如果有人过来同时要读取数据,那么会尝试加共享锁,此时会失败,因为共享锁和排他锁是冲突的

     

    如果有在修改数据,就不允许别人来修改数据,也不允许别人来读取数据

     

    2、共享锁和排他锁的实验

    第一步:有人在读数据,其他人也能过来读数据

    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"

      }

    }

    结果:

     

    如果id 为1 的没有数据直接将

    upsert": {

        "lock_type":  "shared",

        "lock_count": 1

      }进行插入,否则判断id 为1 的lock_type数据是不是exclusive,如果是,说明有其他线程上排他锁,此时直接报错;如果不是将共享锁的数目++;

    1. 如果还有线程进行上共享锁

     

    POST /fs/lock/1/_update

    {

      "upsert": {

        "lock_type":  "shared",

        "lock_count": 1

      },

      "script": {

           "lang": "groovy",

           "file": "judge-lock-2"

      }

    }

    结果:{

      "_index": "fs",

      "_type": "lock",

      "_id": "1",

      "_version": 2,

      "result": "updated",

      "_shards": {

        "total": 2,

        "successful": 1,

        "failed": 0

      }

    }

     

    查看一下共享锁状态

    GET /fs/lock/1

    结果:

    {

      "_index": "fs",

      "_type": "lock",

      "_id": "1",

      "_version": 3,

      "found": true,

      "_source": {

        "lock_type": "shared",

        "lock_count": 3

      }

    }

     

     

    就给大家模拟了,有人上了共享锁,你还是要上共享锁,直接上就行了,没问题,只是lock_count加1

     

    第二步: 已经有人上了共享锁,然后有人要上排他锁

    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 [2])",

            "index_uuid": "eHHajw-rQTir3hzN0tIMHg",

            "shard": "3",

            "index": "fs"

          }

        ],

        "type": "version_conflict_engine_exception",

        "reason": "[lock][1]: version conflict, document already exists (current version [2])",

        "index_uuid": "eHHajw-rQTir3hzN0tIMHg",

        "shard": "3",

        "index": "fs"

      },

      "status": 409

    }

     

    排他锁用的不是upsert语法,create语法,要求lock必须不能存在,直接自己是第一个上锁的人,上的是排他锁

     

    如果已经有人上了共享锁,明显/fs/lock/1是存在的,create语法去上排他锁,肯定会报错,此时该线程属于等待状态。(代码中可以使用死循环)

     

    第三步: 对共享锁进行解锁

    unlock-shared.groovy:  if( --ctx._source.lock_count == 0 ){ ctx.op = 'delete' }

    POST /fs/lock/1/_update

    {

      "script": {

           "lang": "groovy",

           "file": "unlock-shared"

      }

    }

    结果:

    {

      "_index": "fs",

      "_type": "lock",

      "_id": "1",

      "_version": 5,

      "result": "deleted",

      "_shards": {

        "total": 2,

        "successful": 1,

        "failed": 0

      }

    }

     

     

    连续解锁3次,此时共享锁就彻底没了

     

    每次解锁一个共享锁,就对lock_count先减1,如果减了1之后,是0,那么说明所有的共享锁都解锁完了,此时就就将/fs/lock/1删除,就彻底解锁所有的共享锁

     

    第四步: 上排他锁,再上排他锁

    PUT /fs/lock/1/_create

    { "lock_type": "exclusive" }

    结果:

    {

      "_index": "fs",

      "_type": "lock",

      "_id": "1",

      "_version": 1,

      "result": "created",

      "_shards": {

        "total": 2,

        "successful": 1,

        "failed": 0

      },

      "created": true

    }

     

    其他线程

    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

    }

     

     

    第五步: 上排他锁后上共享锁

    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\n"

          },

          "script_stack": [],

          "script": "",

          "lang": "groovy"

        }

      },

      "status": 400

    }

     

     

    第六步: 解锁排他锁

    DELETE /fs/lock/1

    结果:

    {

      "error": {

        "root_cause": [

          {

            "type": "remote_transport_exception",

            "reason": "[21o2lqR][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\n"

          },

          "script_stack": [],

          "script": "",

          "lang": "groovy"

        }

      },

      "status": 400

    }

     

     

    展开全文
  • 本文通过代码实操讲解了如何使用 python 实现简单的共享锁和排他锁。上篇文章回顾:记一次容量提升5倍的HttpDns业务Cache调优共享锁和排它锁1、什么是共享锁共享锁又称为读锁。从多线程的角度来讲,共享锁允许多个...
    本文通过代码实操讲解了如何使用 python 实现简单的共享锁和排他锁。
    上篇文章回顾:记一次容量提升5倍的HttpDns业务Cache调优

    共享锁和排它锁

    1、什么是共享锁

    共享锁又称为读锁。

    从多线程的角度来讲,共享锁允许多个线程同时访问资源,但是对写资源只能又一个线程进行。

    从事务的角度来讲,若事务 T 对数据 A 加上共享锁,则事务 T 只能读 A; 其他事务也只能对数据 A 加共享锁,而不能加排他锁,直到事务 T 释放 A 上的 S 锁。这就保证了其他事务可以读 A,但是在事务 T 释放 A 上的共享锁之前,不能对 A 做任何修改。

    2、什么是排它锁

    排他锁又成为写锁。

    从多线程的角度来讲,在访问共享资源之前对进行加锁操作,在访问完成之后进行解锁操作。 加锁后,任何其他试图再次加锁的线程会被阻塞,直到当前进程解锁。如果解锁时有一个以上的线程阻塞,那么所有该锁上的线程都被编程就绪状态, 第一个变为就绪状态的线程又执行加锁操作,那么其他的线程又会进入等待。 在这种方式下,只有一个线程能够访问被互斥锁保护的资源。

    从事务的角度来讲,若事务T对数据对象A加上排它锁,则只允许T读取和修改数据A,其他任何事务都不能再对A加任何类型的锁,直到事务T释放X锁。它可以防止其他事务获取资源上的锁,直到事务末尾释放锁。

    InnoDB 中的行锁

    InnoDB实现了以下两种类型的行锁:

    共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。

    排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。

    另外,为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB 还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁。

    意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的 IS 锁。

    意向排他锁(IX)事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的 IX 锁。

    如果一个事务请求的锁模式与当前的锁兼容,InnoDB 就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。

    意向锁是 InnoDB 自动加的,不需用户干预。对于 UPDATE、DELETE 和 INSERT 语句,InnoDB 会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB 不会加任何锁;事务可以通过以下语句显示给记录集加共享锁或排他锁。

    共享锁(S):

    SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE复制代码

    排他锁(X):

    SELECT * FROM table_name WHERE ... FOR UPDATE复制代码

    用 SELECT ... IN SHARE MODE获得共享锁,主要用在需要数据依存关系时来确认某行记录是否存在,并确保没有人对这个记录进行UPDATE或者DELETE操作。但是如果当前事务也需要对该记录进行更新操作,则很有可能造成死锁,对于锁定行记录后需要进行更新操作的应用,应该使用 SELECT... FOR UPDATE 方式获得排他锁。

    使用Python实现

    1、代码实现

    不多说,直接上代码:

    # -*- coding: utf-8 -*-import threadingclass Source:    # 队列成员标识    __N = None    # 排他锁    __X = 0    # 意向排他锁    __IX = 1    # 共享锁标识    __S = 2    # 意向共享标识    __IS = 3    # 同步排他锁    __lockX = threading.Lock()       # 事件通知    __events = [        threading.Event(),        threading.Event(),        threading.Event(),        threading.Event()   ]        # 事件通知队列    __eventsQueue = [       [],       [],       [],       []   ]        # 事件变更锁    __eventsLock = [        threading.Lock(),        threading.Lock(),        threading.Lock(),        threading.Lock()   ]         # 相互互斥的锁    __mutexFlag = {}         # 锁类    class __ChildLock:        # 锁标识        __flag = 0        # 锁定的资源        __source = None        def __init__(self, source, flag):            self.__flag = flag            self.__source = source                # 加锁        def lock(self):            self.__source.lock(self.__flag)                        # 解锁        def unlock(self):            self.__source.unlock(self.__flag)     def __init__(self):        self.__initMutexFlag()        self.__initEvents()        # 不建议直接在外面使用,以免死锁    def lock(self, flag):        # 如果是排他锁,先进进行枷锁        if flag == self.__X: self.__lockX.acquire()        self.__events[flag].wait()        self.__lockEvents(flag)            # 不建议直接在外面使用,以免死锁    def unlock(self, flag):        self.__unlockEvents(flag)                if flag == self.__X: self.__lockX.release()            # 获取相互互斥    def __getMutexFlag(self, flag):        return self.__mutexFlag[flag]        def __initMutexFlag(self):        self.__mutexFlag[self.__X] = [self.__X, self.__IX, self.__S, self.__IS]        self.__mutexFlag[self.__IX] = [self.__X, self.__S]        self.__mutexFlag[self.__S] = [self.__X, self.__IX]        self.__mutexFlag[self.__IS] = [self.__X]            def __initEvents(self):        for event in self.__events:            event.set()            # 给事件加锁, 调用 wait 时阻塞    def __lockEvents(self, flag):        mutexFlags = self.__getMutexFlag(flag)                for i in mutexFlags:                        # 为了保证原子操作,加锁            self.__eventsLock[i].acquire()            self.__eventsQueue[i].append(self.__N)            self.__events[i].clear()            self.__eventsLock[i].release()        # 给事件解锁, 调用 wait 不阻塞    def __unlockEvents(self, flag):        mutexFlags = self.__getMutexFlag(flag)                for i in mutexFlags:                        # 为了保证原子操作,加锁            self.__eventsLock[i].acquire()            self.__eventsQueue[i].pop(0)                        if len(self.__eventsQueue[i]) == 0: self.__events[i].set()            self.__eventsLock[i].release()        # 获取锁    def __getLock(self, flag):        lock = self.__ChildLock(self, flag)        lock.lock()                return lock       # 获取 X 锁    def lockX(self):        return self.__getLock(self.__X)        # 获取 IX 锁    def lockIX(self):        return self.__getLock(self.__IX)            # 获取 S 锁    def lockS(self):        return self.__getLock(self.__S)            # 获取 IS 锁    def lockIS(self):        return self.__getLock(self.__IS)复制代码

    使用方式:

    from lock import Source# 初始化一个锁资源source = Source()# 获取资源的X锁,获取不到则线程被阻塞,获取到了继续往下执行lock = source.lockX() lock.unlock()lock = source.lockIX() lock.unlock()lock = source.lockS() lock.unlock()lock = source.lockIS() lock.unlock()复制代码

    2、实现思路

    以 S 锁为例,获取锁的步骤如下:

    • 检测 S 锁是否可以取到,取到了话继续执行,没有取到则阻塞,等待其他线程解锁唤醒。

    • 获取与 S 锁相互冲突的锁(IX,X),并将 IX 锁和 X 锁 锁住,后续想获得 IX 锁或者 X 锁的线程就会被阻塞。

    • 向 IX 锁和 X 锁的标识队列插入标识,如果此时另外一个线程拿到了 IS 锁,则会继续想 IX 锁队列标识插入标识。

    • 完成加锁,返回 S 锁。

    以 S 锁为例,解锁的步骤如下:

    • 获取与 S 锁相互冲突的锁(IX,X),向 IX 锁和 X 锁的标识队列移除一个标识。

    • 判断 IX 锁和 X 锁队列标识是否为空,如果不为空,则继续锁定,为空则解锁并唤醒被 IX 锁和 X 锁阻塞的线程。

    • 完成 S 锁解锁。

    3、锁兼容测试

    测试代码

    # -*- coding: utf-8 -*-import threadingimport timefrom lock import Source# 初始化资源source= Source()maplockname=['X','IX','S','IS']class MyThread(threading,Thread):    flag = None    def __init__(self, flag):        super().__init__()        self.flag = flag    def run(self):        lock = self.lock()        time1 = time.time()        strtime1 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time1))        print('我拿到 %s 锁,开始执行了喔,现在时间是 %s' % (maplockname[self.flag], strtime1))        time.sleep(1)        time2 = time.time()        strtime2 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time2))        print('我释放 %s 锁,结束执行了,现在时间是 %s' % (maplockname[self.flag], strtime2))        lock.unlock()       def lock(self):        if self.flag == 0:                  return source.lockX()                elif self.flag == 1:               return source.lockIX()               elif self.flag == 2:                         return source.lockS()                else:                         return source.lockIS()             def unlock(self, lock):        lock.unlock()def test_lock():    for x in range(0, 4):                  for y in range(0, 4):                time1 = time.time()                thread1 = MyThread(x)                thread2 = MyThread(y)                thread1.start()                thread2.start()                thread1.join()                thread2.join()                time2 = time.time()                difftime = time2 - time1                            if difftime > 2:                     print('%s 锁和 %s 锁 冲突了!' % (maplockname[x], maplockname[y]))                            elif difftime > 1:                     print('%s 锁和 %s 锁 没有冲突!' % (maplockname[x], maplockname[y]))            print('')if __name__ == '__main__':    test_lock()复制代码

    运行结果:

    我拿到 X 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:09我释放 X 锁了,结束执行了,现在时间是 2019-02-17 18:38:10我拿到 X 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:10我释放 X 锁了,结束执行了,现在时间是 2019-02-17 18:38:11X 锁和 X 锁 冲突了!我拿到 X 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:11我释放 X 锁了,结束执行了,现在时间是 2019-02-17 18:38:12我拿到 IX 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:12我释放 IX 锁了,结束执行了,现在时间是 2019-02-17 18:38:13X 锁和 IX 锁 冲突了!我拿到 X 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:13我释放 X 锁了,结束执行了,现在时间是 2019-02-17 18:38:14我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:14我释放 S 锁了,结束执行了,现在时间是 2019-02-17 18:38:15X 锁和 S 锁 冲突了!我拿到 X 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:15我释放 X 锁了,结束执行了,现在时间是 2019-02-17 18:38:16我拿到 IS 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:16我释放 IS 锁了,结束执行了,现在时间是 2019-02-17 18:38:17X 锁和 IS 锁 冲突了!我拿到 IX 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:17我释放 IX 锁了,结束执行了,现在时间是 2019-02-17 18:38:18我拿到 X 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:18我释放 X 锁了,结束执行了,现在时间是 2019-02-17 18:38:19IX 锁和 X 锁 冲突了!我拿到 IX 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:19我拿到 IX 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:19我释放 IX 锁了,结束执行了,现在时间是 2019-02-17 18:38:20我释放 IX 锁了,结束执行了,现在时间是 2019-02-17 18:38:20IX 锁和 IX 锁 没有冲突!我拿到 IX 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:20我释放 IX 锁了,结束执行了,现在时间是 2019-02-17 18:38:21我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:21我释放 S 锁了,结束执行了,现在时间是 2019-02-17 18:38:22IX 锁和 S 锁 冲突了!我拿到 IX 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:22我拿到 IS 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:22我释放 IX 锁了,结束执行了,现在时间是 2019-02-17 18:38:23我释放 IS 锁了,结束执行了,现在时间是 2019-02-17 18:38:23IX 锁和 IS 锁 没有冲突!我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:23我释放 S 锁了,结束执行了,现在时间是 2019-02-17 18:38:24我拿到 X 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:24我释放 X 锁了,结束执行了,现在时间是 2019-02-17 18:38:25S 锁和 X 锁 冲突了!我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:25我释放 S 锁了,结束执行了,现在时间是 2019-02-17 18:38:26我拿到 IX 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:26我释放 IX 锁了,结束执行了,现在时间是 2019-02-17 18:38:27S 锁和 IX 锁 冲突了!我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:27我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:27我释放 S 锁了,结束执行了,现在时间是 2019-02-17 18:38:28我释放 S 锁了,结束执行了,现在时间是 2019-02-17 18:38:28S 锁和 S 锁 没有冲突!我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:28我拿到 IS 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:28我释放 S 锁了,结束执行了,现在时间是 2019-02-17 18:38:29我释放 IS 锁了,结束执行了,现在时间是 2019-02-17 18:38:29S 锁和 IS 锁 没有冲突!我拿到 IS 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:29我释放 IS 锁了,结束执行了,现在时间是 2019-02-17 18:38:30我拿到 X 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:30我释放 X 锁了,结束执行了,现在时间是 2019-02-17 18:38:31IS 锁和 X 锁 冲突了!我拿到 IS 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:31我拿到 IX 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:31我释放 IX 锁了,结束执行了,现在时间是 2019-02-17 18:38:32我释放 IS 锁了,结束执行了,现在时间是 2019-02-17 18:38:32IS 锁和 IX 锁 没有冲突!我拿到 IS 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:32我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:32我释放 IS 锁了,结束执行了,现在时间是 2019-02-17 18:38:33我释放 S 锁了,结束执行了,现在时间是 2019-02-17 18:38:33IS 锁和 S 锁 没有冲突!我拿到 IS 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:33我拿到 IS 锁了,开始执行了喔,现在时间是 2019-02-17 18:38:33我释放 IS 锁了,结束执行了,现在时间是 2019-02-17 18:38:34我释放 IS 锁了,结束执行了,现在时间是 2019-02-17 18:38:34IS 锁和 IS 锁 没有冲突!复制代码

    4、公平锁与非公平锁

    (1)问题分析

    仔细想了想,如果有一种场景,就是用户一直再读,写获取不到锁,那么不就造成脏读吗?这不就是由于资源的抢占不就是非公平锁造成的。如何避免这个问题呢?这就涉及到了公平锁与非公平锁。

    对产生的结果来说,如果一个线程组里,能保证每个线程都能拿到锁,那么这个锁就是公平锁。相反,如果保证不了每个线程都能拿到锁,也就是存在有线程饿死,那么这个锁就是非公平锁。

    (2)非公平锁测试

    上述代码锁实现的是非公平锁,测试代码如下:

    def test_fair_lock():    threads = []        for i in range(0, 10):                  if i == 2:                       # 0 代表排他锁(X)            threads.append(MyThread(0))                  else:                        # 2 代表共享锁(S)            threads.append(MyThread(2))    for thread in threads: thread.start()复制代码

    运行结果:

    我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:06:33我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:06:33我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:06:33我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:06:33我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:06:33我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:06:33我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:06:33我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:06:33我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:06:33我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:06:34我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:06:34我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:06:34我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:06:34我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:06:34我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:06:34我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:06:34我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:06:34我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:06:34我拿到 X 锁了,开始执行了喔,现在时间是 2019-02-17 19:06:34我释放 X 锁了,结束执行了,现在时间是 2019-02-17 19:06:35复制代码

    可以看到由于资源抢占问题,排他锁被最后才被获取到了。

    (3)公平锁的实现

    实现公平锁,只需要在原有的代码进行小小得修改就行了。

    class Source:    # ...... 省略    def __init__(self, isFair=False):          self.__isFair = isFair          self.__initMutexFlag()          self.__initEvents()        # ...... 省略        def lock(self, flag):        # 如果是排他锁,先进进行枷锁        if flag == self.__X: self.__lockX.acquire()                if self.__isFair:                   # 如果是公平锁则,先将互斥的锁给阻塞,防止其他线程进入            self.__lockEventsWait(flag)            self.__events[flag].wait()            self.__lockEventsQueue(flag)                else:                    # 如果是非公平锁,如果锁拿不到,则先等待            self.__events[flag].wait()            self.__lockEvents(flag)       def __lockEventsWait(self, flag):        mutexFlags = self.__getMutexFlag(flag)                for i in mutexFlags:                        # 为了保证原子操作,加锁            self.__eventsLock[i].acquire()            self.__events[i].clear()            self.__eventsLock[i].release()        def __lockEventsQueue(self, flag):          mutexFlags = self.__getMutexFlag(flag)               for i in mutexFlags:                            # 为了保证原子操作,加锁                self.__eventsLock[i].acquire()                self.__eventsQueue[i].append(self.__N)                self.__eventsLock[i].release()复制代码

    测试代码:

    source = Source(True)def test_fair_lock():    threads = []        for i in range(0, 10):                  if i == 2:                            # 0 代表排他锁(X)                threads.append(MyThread(0))                  else:                           # 2 代表共享锁(S)                threads.append(MyThread(2))            for thread in threads: thread.start()复制代码

    运行结果:

    我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:35:16我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:35:16我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:35:17我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:35:17我拿到 X 锁了,开始执行了喔,现在时间是 2019-02-17 19:35:17我释放 X 锁了,结束执行了,现在时间是 2019-02-17 19:35:18我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:35:18我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:35:18我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:35:18我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:35:18我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:35:18我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:35:18我拿到 S 锁了,开始执行了喔,现在时间是 2019-02-17 19:35:18我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:35:19我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:35:19我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:35:19我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:35:19我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:35:19我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:35:19我释放 S 锁了,结束执行了,现在时间是 2019-02-17 19:35:19复制代码

    可以看到排他锁在第二次的时候就被获取到了。

    (4)优缺点

    非公平锁性能高于公平锁性能。首先,在恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟。而且,非公平锁能更充分的利用cpu的时间片,尽量的减少cpu空闲的状态时间。

    参考文献:

    共享锁(S锁)和排它锁(X锁):https://www.jianshu.com/p/bd3b3ccedda9

    Java多线程--互斥锁/共享锁/读写锁 快速入门:https://www.jianshu.com/p/87ac733fda80

    Java多线程 -- 公平锁和非公平锁的一些思考:https://www.jianshu.com/p/eaea337c5e5b

    MySQL- InnoDB锁机制:https://www.cnblogs.com/aipiaoborensheng/p/5767459.html


    文章首发于共公众号“小米运维”,点击查看原文


    转载于:https://juejin.im/post/5c9c85d0e51d450bc6039572

    展开全文
  • mysql共享锁和排他锁

    2018-06-27 16:01:45
    1.Mysql6大锁 乐观锁:自己实现 ...mysql锁机制分为表级锁和行级锁,本文要介绍Mysql行级锁中的共享锁和排他锁。 共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一...

    1.Mysql6大锁

    乐观锁:自己实现
    悲观锁:数据库自己实现了
    共享锁:读锁(也叫S锁)
    排他锁:写锁(也叫X锁)
    行锁:一条记录加上锁
    表锁:给这个表加上锁


    2.共享锁与排他锁

    mysql锁机制分为表级锁和行级锁,本文要介绍Mysql行级锁中的共享锁和排他锁。

    共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。


    排他锁又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。

    注意的小问题

    1.排他锁可以使用select …for update语句。
    2.共享锁可以使用select … lock in share mode语句。
    3.排他锁指的是一个事务在一行数据加上排他锁后,其他事务不能再在其上加其他的锁。
    4.Mysql InnoDB引擎 update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型。

    所以加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select …from…查询数据,因为普通查询没有任何锁机制。


    3.测试例子

    这里写图片描述

    CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `username` varchar(32) NOT NULL,
      `password` varchar(32) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8;

    现在我们对id=29的数据行排他锁查询,这里会使用begin开启事务,而不会看见我关闭事务,这样做是用来测试,因为提交事务或回滚事务就会释放锁。
    这里写图片描述


    现在打开另一个查询窗口,对同一数据分别使用排他锁查询和共享锁查询两种方式查询。

    普通查询:
    这里写图片描述

    排他锁查询:
    这里写图片描述

    共享锁查询:
    这里写图片描述

    我们看到开了排他锁查询和共享锁查询都会处于阻塞状态,因为id=29的数据已经被加上了排他锁,此处阻塞是等待排他锁释放。

    展开全文
  • 如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。 排他锁 排他锁(X锁):用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时...
  • JAVA共享锁和排他锁总结

    千次阅读 2020-05-11 17:32:16
    共享锁和排他锁总结1.ReentrantReadWriteLock2.锁申请和释放策略3.插队策略4.升降级策略5.使用场合总结 1.ReentrantReadWriteLock 实现了ReadWriteLock接口,最主要的有两个方法:readLock()和writeLock()用来获取读...
  • 接下来来介绍悲观锁和乐观锁以及共享锁和排他锁 乐观锁(靠表的设计和代码) 乐观锁不是数据库自带的,需要我们自己去实现。乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据...
  • 最近在工作过程中,用JPA的时候有一个注解。是 @Lock(value = LockModeType.PESSIMITIC_READ) BizDistributeLock findFirstByBizName(String bizName); 此外,还有一个 ...代码实现之前,先调用了上面的查询,再
  • 表锁: 是mysql中开销最小的策略,它会锁定整张表,用户在对表...行锁: 可以最大程度的支持并发处理,同时也带来了最大的开销,InnoDB存储引擎实现了行锁,行锁只在存储引擎层实现,不会在mysql服务器层实现。 ...
  • InnoDB 实现了两种类型的锁机制:共享锁(S)和排他锁(X)。共享锁允许一个事务读数据,不允许修改数据,如果其他事务要再对该行加锁,只能加共享锁;排他锁是修改数据时加的锁,可以读取和修改数据,一旦一个事务...
  • 共享锁就是多个事务可以同时读某一资源,而排他锁意味着一个事务获得写权限时,其他事务只能等待。 而自旋锁是排它锁的一种实现。当线程获取锁而锁被其他线程占用的时候,线程不会发生状态转换,保持active的状态。...
  • 排他锁(ExculusiveLocks) 又称为写锁或独占锁。是一种锁类型,如果事务T1对数据对象O加上了排他锁。 那么在整个加锁期间,只有事务T1可以对数据对象O进行读写操作,其它任何事务都不能对数据对象O进行任何类型的...
  • 一、相关名词 |--表级锁(锁定整个表) |--页级锁(锁定一页) |--行级锁(锁定一行) ...其中,共享锁和拍他所都是悲观锁,乐观锁不存在于mysql中,只是一种代码的逻辑实现,所以mysql的锁都是悲...
  • 数据库锁(行锁,表锁,共享锁排他锁) 行锁 我们知道mysql的Innodb引擎是支持行锁的,与Oracle不同,mysql的行锁是通过索引加载的,即行锁是加载索引响应的行上的,要是对应的SQL语句没有索引,则会走表锁。 行锁...
  • 在DBMS中,可以按照的粒度把数据库分为行级(INNODB引擎)、表级(MYISAM引擎)页级(BDB引擎 )。 下面这张图可以比较直观地看出各种之间的关系: 表级: 表级是MySQL中锁定粒度最大的一种,...
  • 数据库通过以及协议来进行并发控制,解决并发事务带来的问题,本篇博文主要是解析数据库的协议Mysql的默认存储引擎InnoDB的机制。 如果对事务隔离级别以及并发事务带来的问题不熟悉可以翻阅我的另外一篇...
  • 在介绍之前,我先介绍下协议,协议规定了何时加锁、释放的规则,不同的规则可用于实现不同的隔离级别,解决不同的并发事务问题。 一级封锁协议:更新数据前需要先加X,直到事务结束才释放X,读数据是不...
  • 读写锁是数据库中很常见的锁,又叫共享-排他锁,S锁和X锁。读写锁在大量读少量写的情况下有很高的效率优势。 读写锁是基于普通的互斥锁构建出来的更复杂的锁,它有两个基本特点: 1. 当任一线程持有读锁或写锁时,...
  • 读写锁是数据库中很常见的锁,又叫共享-排他锁,S锁和X锁。读写锁在大量读少量写的情况下有很高的效率优势。 读写锁是基于普通的互斥锁构建出来的更复杂的锁,它有两个基本特点: 1. 当任一线程持有读锁或写锁时,...
  • 65_基于共享锁和排他锁实现悲观锁并发控制更多干货分布式实战(干货)spring cloud 实战(干货)mybatis 实战(干货)spring boot 实战(干货)React 入门实战(干货)构建中小型互联网企业架构(干货)python 学习...
  • 独占锁与共享锁

    2020-09-02 17:28:05
    独占锁和共享锁同样是一种概念。我们先介绍一下具体的概念,然后通过 ReentrantLock 和 ReentrantReadWriteLock 的源码来介绍独占锁和共享锁。 概念引入 独占锁概念 独占锁也叫排他锁,是指该锁一次只能被一个...
  • 文章目录概述方式一:基于全局锁实现悲观锁并发控制方式二:基于document锁实现悲观锁并发控制方式三:基于共享锁和排他锁实现悲观锁并发控制 概述 继续跟中华石杉老师学习ES,第57篇 课程地址: ...
  • MySQL 如何实现悲观锁和乐观锁? 乐观锁:更新时带上版本号(cas更新) 悲观锁:mysql使用的共享锁和排它锁来实现悲观锁,select…lock in share mode(共享锁),select…for update(排他锁)。
  • 在上篇博客中了解了排他锁的基本源码实现,所以现在我们学习下共享锁的源码,二者的源码实现大部分都是相同的,而且了解了排他锁的原理之后,我们现在阅读共享锁的源码会更加得心应手。 排他锁:当前锁只能被一个...
  • 为了解决多并发的过程中,对数据的同时操作导致的脏读、换读和不可重复读等问题,提出了悲观锁和乐观锁的概念 悲观锁 简易概念:在数据修改之前,先给数据加锁,然后再修改数据。其他事务遇到锁,先挂起等待当前...
  • 独享锁和共享锁同样是一种概念。然后通过ReentrantLock和ReentrantReadWriteLock的源码来介绍独享锁和共享锁。 1、概念: 独享锁 独享锁也叫排他锁,是指该锁一次只能被一个线程所持有。如果线程T对数据A加上排它锁...
  • 两把锁缺一不可,InnoDB 引擎中的四种隔离级别就是用 排他锁 + 共享锁 实现的。 下面是个人理解,可能并不严谨。 首先说一下并发可能产生的四种问题,如果你还不了解这四个问题,最好拿至少 20 分钟时间模拟一下...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 561
精华内容 224
关键字:

共享锁和排他锁实现