使用数据库悲观锁实现不可重入的分布式锁

Posted 技术原始积累

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用数据库悲观锁实现不可重入的分布式锁相关的知识,希望对你有一定的参考价值。

一、前言

在同一个jvm进程中时,可以使用JUC提供的一些锁来解决多个线程竞争同一个共享资源时候的线程安全问题,但是当多个不同机器上的不同jvm进程共同竞争同一个共享资源时候,juc包的锁就无能无力了,这时候就需要分布式锁了。常见的有使用zk的最小版本,redis的set函数,数据库锁来实现,本节我们谈谈使用数据库悲观锁机制来实现一个分布式锁。

二、使用数据库悲观锁实现不可重入的分布式锁

这个比较简单,先来看代码:

 
   
   
 
  1. public class DBdistributedLock {

  2.    private DataSource dataSource;

  3.    private static final String cmd = "select * from lock where uid = 1 for update";

  4.    public DBdistributedLock(DataSource ds) {

  5.        this.dataSource = ds;

  6.    }

  7.    public static interface CallBack{

  8.        public void doAction();

  9.    }

  10.    public void lock(CallBack callBack)  {

  11.        Connection conn = null;

  12.        PreparedStatement stmt = null;

  13.        ResultSet rs = null;

  14.        try {

  15.            //try get lock

  16.            System.out.println(Thread.currentThread().getName() + " begin try lock");

  17.            conn = dataSource.getConnection();

  18.            conn.setAutoCommit(false);

  19.            stmt = conn.prepareStatement(cmd);

  20.            rs = stmt.executeQuery();

  21.            //do business thing

  22.            callBack.doAction();

  23.            //release lock

  24.            conn.commit();

  25.            System.out.println(Thread.currentThread().getName() + " release lock");

  26.        } catch (SQLException e) {

  27.            e.printStackTrace();

  28.        } finally {

  29.            if (null != conn) {

  30.                try {

  31.                    conn.close();

  32.                } catch (SQLException e) {

  33.                    e.printStackTrace();

  34.                }

  35.            }

  36.        }

  37.    }

使用数据库悲观锁实现分布式锁主要用了数据库的for update命令,执行改命令后,对应行记录会被锁住,其它线程会被阻塞主,直到获取到这行记录的线程提交了事务。这里需要注意要把自动提交设置为false。

多个线程执行 rs = stmt.executeQuery();时候,只有一个线程会获取到行锁,其它线程被阻塞挂起,获取锁的线程执行传递的callback 的业务逻辑,执行完毕后 执行commit 提交事务,这意味着当前线程释放了获取的锁,这时候被阻塞的线程会竞争获取该锁。

如何使用:

 
   
   
 
  1. final DBdistributedLock bdistributedLock = new DBdistributedLock(dataSource);

  2. bdistributedLock.lock(new CallBack() {

  3.                        @Override

  4.                        public void doAction() {

  5.                            System.out.println(Thread.currentThread().getName() + "beging do somthing");

  6.                            try {

  7.                                Thread.sleep(2000);

  8.                            } catch (InterruptedException e) {

  9.                                // TODO Auto-generated catch block

  10.                                e.printStackTrace();

  11.                            }

  12.                            System.out.println(Thread.currentThread().getName() + "end do somthing");

  13.                        }

如上代码可知使用时候只需要创建的一个DBdistributedLock对象,然后调用其Lock方法,并且传递一个callback的实现,实现方法里具体做业务,这些业务是受分布式锁保护的,拥有原子性。

三、总结

本文使用数据库悲观锁实现不可重入的分布式锁机制实现了一个分布式锁,大家想想如何使用乐观锁来实现那?到这里已经讲解了三种方式实现分布式锁,欢迎大家留言讨论,他们各自的优缺点,以及使用场景。

最后

  • 推荐大家几个能够拓展我们视野的课程

       

  • 并发编程专辑


  • 分布式系统中服务降级策略探究


  • Dubbo专题

       


以上是关于使用数据库悲观锁实现不可重入的分布式锁的主要内容,如果未能解决你的问题,请参考以下文章

使用Redis单实例实现不可重入的分布式锁

一文看懂的 Zookeeper 分布式锁!

分布式锁实现介绍

分布式锁Redis实现可重入的分布式锁

多线程 锁策略 ( 悲观/乐观锁 读写/互斥锁 重量/轻量级锁挂起等待/自旋锁 公平/非公平锁 可重入/不可重入锁)

盘点 Java 的那些个锁呦!