一句话说清分布式锁,进程锁,线程锁

在分布式集群系统的开发中,线程锁往往并不能支持全部场景的使用,必须引入新的技术方案分布式锁 。
线程锁,进程锁,分布式锁
线程锁:大家都不陌生,主要用来给方法、代码块加锁 。当某个方法或者代码块使用锁时,那么在同一时刻至多仅有有一个线程在执行该段代码 。当有多个线程访问同一对象的加锁方法/代码块时,同一时间只有一个线程在执行,其余线程必须要等待当前线程执行完之后才能执行该代码段 。但是,其余线程是可以访问该对象中的非加锁代码块的 。
进程锁:也是为了控制同一操作系统中多个进程访问一个共享资源,只是因为程序的独立性,各个进程是无法控制其他进程对资源的访问的,但是可以使用本地系统的信号量控制(操作系统基本知识) 。
分布式锁:当多个进程不在同一个系统之中时,使用分布式锁控制多个进程对资源的访问 。
分布式锁到底是什么,怎么实现?
intsmaze说简单点,实现分布式锁必须要依靠第三方存储介质来存储锁的元数据等信息 。比如分布式集群要操作某一行数据时,这个数据的流水号是唯一的,那么我们就把这个流水号作为一把锁的id,当某进程要操作该数据时,先去第三方存储介质中看该锁id是否存在,如果不存在,则将该锁id写入,然后执对该数据的操作;当其他进程要访问这个数据时,会先到第三方存储介质中查看有没有这个数据的锁id,有的话就认为这行数据目前已经有其他进程在使用了,就会不断地轮询第三方存储介质看其他进程是否释放掉该锁;当进程操作完该数据后,该进程就到第三方存储介质中把该锁id删除掉,这样其他轮询的进程就能得到对该锁的控制 。

redis中当然不能通过get,set操作判断,get,set操作不是一个原子的,可以使用redis的jedis.set(String key, String value, String nxxx, String expx, int time)命令来保证原子性 。
说了这么多,再补充一点,线程锁,进程锁,分布式锁的作用都是一样的,只是作用的范围大小不同 。范围大小:分布式锁——大于——进程锁——大于——线程锁 。能用线程锁,进程锁情况下使用分布式锁也是可以的,能用线程锁的情况下使用进程锁也是可以的 。只是范围越大技术复杂度就越大 。
多年j2EE开发生涯从未感觉到分布式锁的痛点!!!
关于分布式锁,有过JAVAEE开发经验的就会说了,系统为了应对高并发,会搭建一个比如Tomcat集群,集群内服务都是访问的同一台数据库,有多台服务器同时修改同一条数据库数据的操作,但是我们并没有在服务器中使用分布式锁?按照上面对分布式锁的解释,两个不同系统上的JVM进程同时访问数据库的同一个资源,这个时候我们应该使用分布式锁进行控制 。
这说的没有错,但是我们忘记了数据库的特性了 。如果两台服务器仅仅是直接访问(通过url)并操作某台服务器硬盘中某个文件同一行数据,这个时候我们必须用分布式锁 。
但是因为这两台服务器访问的数据是存储在数据库中的(数据库本身就是一个服务程序,多线程的接收外部系统发来的请求),两台服务器的请求通过网络IO发送到数据库服务器后,然后把请求交给数据库服务的进程处理,数据库服务器是多线程接收请求并处理的,这个时候关于某表某一行数据的多线程访问控制是由数据库服务进行控制的(就是数据库服务的代码中进行了线程上的加锁处理),这就是数据库服务器的行锁等特性,因为数据库那一端已经对外部多个系统的请求进行了一个锁操作,所以不需要我们在应用服务端进行分布式锁的开发 。
那如果想同时更新数据库的多行数据,这个时候数据库的行锁就无法保证了 。这个时候我们就要使用分布式锁,是的这个时候就可以使用,注意我用的是可以 。为什么说可以呢?因为数据库本身就提供了这个机制,事务以及他的隔离级别 。当然你也可以不用数据库提供的事务,用分布式锁 。
分布式锁的设计不需要考虑业务吗?
分布式锁的设计并不是完全美好的,只能针对某些业务场景下使用,如果要对所有业务使用,必须充分理解业务需求合理的设计,至于原因就和各位j2ee开发时mybatis的二级缓存以命名空间为单位所要注意的业务问题时一样的 。
intsmaze使用分布式锁,我们会把某表的第二第三行作为id来锁住,如果有相同的操作时更新该表第二第三行,我们才不让他修改,必须让他拿到锁才可以 。但是如果有个操作仅仅是修改第二行,这个时候他就获得了对该行的操作,而且等数据库释放掉之前操作对该行的锁后 。所以分布式锁并不是随处可用的,只是在某些场景下可以使用 。比如业务系统不会存在单独修改第二行的操作 。


推荐阅读