为什么分布式环境下synchronized失效?如何解决这种情况?

synchronized关键字失效原因

在Java多线程编程中,经常会用到synchronized和lock和原子变量等,而在分布式系统中,由于分布式系统中的分布性,即多线程和多进程并发 分布在不同机器中,synchronized和lock这两种锁将失去原有锁的效果,因此需要自己实现分布式锁来处理并发问题,分布式处理并发的办法有以下三种:

队列

定义:将所有要执行的任务放入队列中,然后一个一个消费,从而避免并发问题

悲观锁

将数据记录加版本号,如果版本号不一致就不更新,这种方式同Java的CAS理念类似。

分布式锁

常见的分布式锁有以下三种实现:

基于数据库实现的分布式锁

利用数据库表:首先创建一张锁的表主要包含下列字段:方法名、时间戳等。——方法名称要有唯一性约束

  • 1、如果有多个请求同时提交到数据库时,数据库保证只有一个操作可以成功,那么操作成功的那个线程获得了该方法的锁,可以继续执行下面的方法体内容。
    优化: 记录当前获得该锁的机器的主机信息和线程信息,那么下次再次获取锁的时候就可以先查询数据库,如果当前机器的主机信息和线程信息可以在数据库中查到,那么就可以直接把锁分配给它,从而实现可重入锁。
  • 2、基于数据库排他锁
    在查询语句后面增加for update,数据库会在查询过程中给数据库表增加排他锁,当某条记录被加上排他锁之后,其他线程无法在该行记录增加排他锁。其他没有获取到的锁就会阻塞在select语句上,从而有两种可能的结果:在超时之前获取到了锁和在超时之前没有获取到锁。获得排他锁的线程即可获取分布式锁,当获取到锁之后,可以执行方法的业务逻辑,执行完方法之后,释放锁connection.commit()。存在的问题主要是性能不高和sql超时的异常。

基于Zookeeper实现分布式锁

基于Zookeeper临时有序节点可以实现的分布式锁。每个客户端对某个方法加锁时,在Zookeeper上与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。

  • 1、判断是否获取锁的方式只需要判断有序节点中的序号最小的一个。当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。
  • 2、提供的第三方库有curator,Curator提供的InterProcessMutex是分布式锁的实现。acquire方法获取锁,release方法获取锁。

基于缓存(redis)来实现分布式锁

采用jedis.setnx()和jedis.expire()组合实现加锁。setnx()方法作用就是SET IF NOT EXIST,expire方法就是给锁加一个过期时间。由于这是两条Redis命令,不具有原子性,如果程序在执行完setnx()之后突然崩溃,导致锁没有设置过期时间。那么就会发生死锁。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章