分布式锁的三种实现方法

无需Redis仅用数据库即可优雅实现分布式锁的三种方法 详细介绍基于数据库实现分布式锁的三种方法:基于数据库唯一索引、基于数据库悲观锁和基于数据库乐观锁的思路、代码实现及测试方法 阅读详情

 

目前几乎所有的大型web应用全都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式应用中的CAP理论告诉我们:

任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)、分区容错性(Partation tolerance)。最多只能同时满足其中两项。

所以在设计之初,就需要对三者做出取舍。一般在互联网场景中,都会选择牺牲强一致性,来换取高可用性,系统只需要保证最终一致性。

那么问题就是,如何保证最终一致性?通常的技术方案有分布式事务,分布式锁。我们需要保证,在同一时刻,只有一个线程执行某一个特定方法。我们知道,在单机环境中,或者是单JVM中,java的并发api可以保证并发安全。但是在集群情况下,java的并发api就没办法了。我们需要分布式锁。

通常分布式锁的实现方案有三种:

1.基于数据库实现 
2.基于缓存(redis、memcached、tair) 
3.基于zookeeper

在思考方案前,首先要考虑目标,我们需要什么样的分布式锁?

1、安全,要保证加锁的方案同一时刻只能被一台机器上的一个线程访问。 
2、可重入锁,避免死锁 
3、最好是阻塞锁 
4、高可用的获取锁和释放锁功能,尤其要关注释放锁的可靠性。

1.基于数据库的实现

基于数据库表

数据库实现分布式锁####

利用DB来实现分布式锁,有两种方案。两种方案各有好坏,但是总体效果都不是很好。但是实现还是比较简单的。

  1. 利用主键唯一规则:

我们知道数据库是有唯一主键规则的,主键不能重复,对于重复的主键会抛出主键冲突异常。
了解JDK reentrantlock的人都知道,reentrantlock是利用了OS的CAS特性实现的锁。主要是维护一个全局的状态,每次竞争锁都会CAS修改锁的状态,修改成功之后就占用了锁,失败的加入到同步队列中,等待唤醒。
其实这和分布式锁实现方案基本是一致的,首先我们利用主键唯一规则,在争抢锁的时候向DB中写一条记录,这条记录主要包含锁的id、当前占用锁的线程名、重入的次数和创建时间等,如果插入成功表示当前线程获取到了锁,如果插入失败那么证明锁被其他人占用,等待一会儿继续争抢,直到争抢到或者超时为止。

这里我主要写了一个简单的实现:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 利用mysql实现可重入分布式锁
 */
public class MysqlprimaryLock {
    private static Connection connection;
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        String url = "jdbc:mysql://10.0.0.212:3308/dbwww_lock?user=lock_admin&password=lock123";
        try {
            connection = DriverManager.getConnection(url);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 加锁
     * @param lockID
     */
    public void lock(String lockID) {
        acquire(lockID);
    }

    /**
     * 获取锁
     * @param lockID
     * @return
     */
    public boolean acquire(String lockID) {
        String sql = "insert into test_lock('id','count','thName','addtime') VALUES (?,?,?,?)";
        while (true) {
            try {
                PreparedStatement statement = connection.prepareStatement(sql);
                statement.setString(1, lockID);
                statement.setInt(2, 1);
                statement.setLong(1, System.currentTimeMillis());
                boolean ifsucess = statement.execute();//如果成功,那么就是获取到了锁
                if (ifsucess)
                    return true;
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            continue;
        }
    }

    /**
     * 超时获取锁
     * @param lockID
     * @param timeOuts
     * @return
     * @throws InterruptedException
     */
    public boolean acquire(String lockID, long timeOuts) throws InterruptedException {

        String sql = "insert into test_lock('id','count','thName','addtime') VALUES (?,?,?,?)";
        long futureTime = System.currentTimeMillis() + timeOuts;
        long ranmain = timeOuts;
        long timerange = 500;
        while (true) {
            CountDownLatch latch = new CountDownLatch(1);
            try {
                PreparedStatement statement = connection.prepareStatement(sql);
                statement.setString(1, lockID);
                statement.setInt(2, 1);
                statement.setLong(1, System.currentTimeMillis());
                boolean ifsucess = statement.execute();//如果成功,那么就是获取到了锁
                if (ifsucess)
                    return true;
            } catch (SQLException e) {
                e.printStackTrace();
            }
            latch.await(timerange, TimeUnit.MILLISECONDS);
            ranmain = futureTime - System.currentTimeMillis();
            if (ranmain <= 0)
                break;
            if (ranmain < timerange) {
                timerange = ranmain;
            }
            continue;
        }
        return false;

    }

    /**
     * 释放锁
     * @param lockID
     * @return
     * @throws SQLException
     */
    public boolean unlock(String lockID) throws SQLException {
        String sql = "DELETE  from test_lock where id = ?";
        PreparedStatement statement = connection.prepareStatement(sql);
        statement.setString(1, lockID);
        boolean ifsucess = statement.execute();
        if (ifsucess)
            return true;
        return false;

    }

}

这里是利用主键冲突规则,加入了id','count','thName','addtime',count主要是为了重入计数,thName为了判断占用锁的线程,addtime是记录占用时间。上面代码没有实现重入的逻辑。

重入主要实现思路是,在每次获取锁之前去取当前锁的信息,如果锁的线程是当前线程,那么更新锁的count+1,并且执行锁之后的逻辑。如果不是当前锁,那么进行重试。释放的时候也要进行count-1,最后减到0时,删除锁标识释放锁。

优点:实现简单

缺点:没有超时保护机制,mysql存在单点,并发量大的时候请求量太大、没有线程唤醒机制,用异常去控制逻辑多少优点恶心。

对于超时保护:如果可能,可以采用定时任务去扫描超过一定阈值的锁,并删除。但是也会存在,锁住的任务执行时间很长,删除锁会导致并发问题。所以需要对超时时间有一个很好的预估。

对于单点问题:有条件可以搞一个主从,但是为了一个锁来搞一个主从是不是优点浪费?同时主从切换的时候系统不可用,这也是一个问题。

并发量大的时候请求量太大:因为这种实现方式是没有锁的唤醒机制的,不像reentrantlock在同步队列中的节点,可以通过唤醒来避免多次的循环请求。但是分布式环境数据库这种锁的实现是不能做到唤醒的。所以只能将获取锁的时间间隔调高,避免死循环给系统和DB带来的巨大压力。这样也牺牲了系统的吞吐量,因为总会有一定的间隔锁是空闲的。

用异常去控制逻辑多少优点恶心:就不说了,每次失败都抛异常.....

2. 利用Mysql行锁的特性

Mysql是有表锁、页锁和行锁的机制的,可以利用这个机制来实现锁。这里尽量使用行锁,它的吞吐量是最高的。

    /**
     * 超时获取锁
     * @param lockID
     * @param timeOuts
     * @return
     * @throws InterruptedException
     */
    public boolean acquireByUpdate(String lockID, long timeOuts) throws InterruptedException, SQLException {

        String sql = "SELECT id from test_lock where id = ? for UPDATE ";
        long futureTime = System.currentTimeMillis() + timeOuts;
        long ranmain = timeOuts;
        long timerange = 500;
        connection.setAutoCommit(false);
        while (true) {
            CountDownLatch latch = new CountDownLatch(1);
            try {
                PreparedStatement statement = connection.prepareStatement(sql);
                statement.setString(1, lockID);
                statement.setInt(2, 1);
                statement.setLong(1, System.currentTimeMillis());
                boolean ifsucess = statement.execute();//如果成功,那么就是获取到了锁
                if (ifsucess)
                    return true;
            } catch (SQLException e) {
                e.printStackTrace();
            }
            latch.await(timerange, TimeUnit.MILLISECONDS);
            ranmain = futureTime - System.currentTimeMillis();
            if (ranmain <= 0)
                break;
            if (ranmain < timerange) {
                timerange = ranmain;
            }
            continue;
        }
        return false;

    }

    /**
     * 释放锁
     * @param lockID
     * @return
     * @throws SQLException
     */
    public void unlockforUpdtate(String lockID) throws SQLException {
        connection.commit();

    }

利用for update加显式的行锁,这样就能利用这个行级的排他锁来实现分布式锁了,同时unlock的时候只要释放commit这个事务,就能达到释放锁的目的。

优点:实现简单

缺点:连接池爆满和事务超时的问题单点的问题,单点问题,行锁升级为表锁的问题,并发量大的时候请求量太大、没有线程唤醒机制。

连接池爆满和事务超时的问题单点的问题:利用事务进行加锁的时候,query需要占用数据库连接,在行锁的时候连接不释放,这就会导致连接池爆满。同时由于事务是有超时时间的,过了超时时间自动回滚,会导致锁的释放,这个超时时间要把控好。

对于单点问题:同上。

并发量大的时候请求量太大:同上。

行锁升级为表锁的问题:Mysql行锁默认需要走索引,如果不走索引会导致锁表,如果可以,在sql中可以强制指定索引。

 

总结:

数据库这种方式看起来简单,实际不会采用的。因为要埋这些坑,要加入更多的代码。使方案变得越来越复杂。

2.基于缓存实现

基于缓存实现,在性能上会比较好,而且缓存一般都是集群部署的,解决了单点问题。 
可以参考本博的另外一篇博文关于基于Redis的分布式锁的实现:

缓存的分布式锁主要通过Redis实现,当然其他的缓存也是可以的。关于缓存有两种实现吧:

  1. 基于SetNX实现:
    setNX是Redis提供的一个原子操作,如果指定key存在,那么setNX失败,如果不存在会进行Set操作并返回成功。我们可以利用这个来实现一个分布式的锁,主要思路就是,set成功表示获取锁,set失败表示获取失败,失败后需要重试。

具体看下代码:

import redis.clients.jedis.Jedis;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Redis分布式锁
 */
public class RedisLockTest {

    private Jedis jedisCli = new Jedis("localhost",6381);

    private int expireTime = 1;

    /**
     * 获取锁
     * @param lockID
     * @return
     */
    public boolean lock(String lockID){
        while(true){
            long returnFlag = jedisCli.setnx(lockID,"1");
            if (returnFlag == 1){
                System.out.println(Thread.currentThread().getName() + " get lock....");
                return true;
            }
            System.out.println(Thread.currentThread().getName() + " is trying lock....");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                return false;
            }
        }
    }

    /**
     * 超时获取锁
     * @param lockID
     * @param timeOuts
     * @return
     */
    public boolean lock(String lockID,long timeOuts){
        long current = System.currentTimeMillis();
        long future = current + timeOuts;
        long timeStep = 500;
        CountDownLatch latch = new CountDownLatch(1);
        while(future > current){
            long returnFlag = jedisCli.setnx(lockID,"1");
            if (returnFlag == 1){
                System.out.println(Thread.currentThread().getName() + " get lock....");
                jedisCli.expire(lockID,expireTime);
                return true;
            }
            System.out.println(Thread.currentThread().getName() + " is trying lock....");
            try {
                latch.await(timeStep, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            current = current + timeStep;
        }
        return false;
    }

    public void unlock(String lockId){
        long flag = jedisCli.del(lockId);
        if (flag>0){
            System.out.println(Thread.currentThread().getName() + " release lock....");
        }else {
            System.out.println(Thread.currentThread().getName() + " release lock fail....");
        }
    }

    /**
     * 线程工厂,命名线程
     */
    public static class MyThreadFactory implements ThreadFactory{
        public static AtomicInteger count = new AtomicInteger();
        @Override
        public Thread newThread(Runnable r) {
            count.getAndIncrement();
            Thread thread = new Thread(r);
            thread.setName("Thread-lock-test "+count);
            return thread;
        }
    }

    public static void main(String args[]){
        final String lockID = "test1";
        Runnable task = () ->{
            RedisLockTest testCli = new RedisLockTest();
            testCli.lock(lockID);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            testCli.unlock(lockID);
        };

        MyThreadFactory factory = new MyThreadFactory();
        ExecutorService services = Executors.newFixedThreadPool(10);
        for (int i = 0;i<3;i++)
            services.execute(factory.newThread(task));
    }

}

看看结果:

pool-1-thread-3 is trying lock....
pool-1-thread-2 get lock....
pool-1-thread-1 is trying lock....
pool-1-thread-3 is trying lock....
pool-1-thread-2 release lock....
pool-1-thread-1 get lock....
pool-1-thread-3 is trying lock....
pool-1-thread-1 release lock....
pool-1-thread-3 get lock....
pool-1-thread-3 release lock....

可以看到,几个线程很好的进行了同步。

这种方式也是有优点和缺点:

优点:实现简单,吞吐量十分客观,对于高并发情况应付自如,自带超时保护,对于网络抖动的情况也可以利用超时删除策略保证不会阻塞所有流程。

缺点:单点问题、没有线程唤醒机制、网络抖动可能会引起锁删除失败。

对单点问题:因为redis一般都是单实例使用,那么对于单点问题,可以做一个主从。当然主从切换的时候也是不可用的,因为主从同步是异步的,可能会并发问题。如果对于主从还是不能保证可靠性的话,可以上Redis集群,对于Redis集群,因为使用了类一致性Hash算法,虽然不能避免节点下线的并发问题(当前的任务没有执行完,其他任务就开始执行),但是能保证Redis是可用的。可用性的问题是出了问题之后的备选方案,如果我们系统天天都出问题还玩毛啊,对于突发情况牺牲一两个请求还是没问题的。

对于线程唤醒机制:分布式锁大多都是这样轮训获取锁的,所以控制住你的重试频率,也不会导致负载特别高的。可能就是吞吐量低点而已。

对于锁删除失败:分布式锁基本都有这个问题,可以对key设置失效时间。这个超时时间需要把控好,过大那么系统吞吐量低,很容易导致超时。如果过小那么会有并发问题,部分耗时时间比较长的任务就要遭殃了。

3.基于Zookeeper实现

当某个客户端获取锁后,突然挂掉。没关系,对应的znode会被自动删除,不会出现锁释放不了的问题。 
是阻塞锁 
可重入 
集群部署

1、实现原理:

基于zookeeper瞬时有序节点实现的分布式锁,其主要逻辑如下(该图来自于IBM网站)。大致思想即为:每个客户端对某个功能加锁时,在zookeeper上的与该功能对应的指定节点的目录下,生成一个唯一的瞬时有序节点。判断是否获取锁的方式很简单,

只需要判断有序节点中序号最小的一个。当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。

2、优点

锁安全性高,zk可持久化

3、缺点

性能开销比较高。因为其需要动态产生、销毁瞬时节点来实现锁功能。

4、实现

可以直接采用zookeeper第三方库curator即可方便地实现分布式锁。以下为基于curator实现的zk分布式锁核心代码:

Java代码  

  1. @Override  
    public boolean tryLock(LockInfo info) {  
        InterProcessMutex mutex = getMutex(info);  
        int tryTimes = info.getTryTimes();  
        long tryInterval = info.getTryInterval();  
        boolean flag = true;// 代表是否需要重试  
        while (flag && --tryTimes >= 0) {  
            try {  
                if (mutex.acquire(info.getWaitLockTime(), TimeUnit.MILLISECONDS)) {  
                    LOGGER.info(LogConstant.DST_LOCK + "acquire lock successfully!");  
                    flag = false;  
                    break;  
                }  
            } catch (Exception e) {  
                LOGGER.error(LogConstant.DST_LOCK + "acquire lock error!", e);  
            } finally {  
                checkAndRetry(flag, tryInterval, tryTimes);  
            }  
        }  
        return !flag;// 最后还需要重试,说明没拿到锁  
    }  
    

    Java代码  

  2. @Override  
    public boolean releaseLock(LockInfo info) {  
        InterProcessMutex mutex = getMutex(info);  
        int tryTimes = info.getTryTimes();  
        long tryInterval = info.getTryInterval();  
        boolean flag = true;// 代表是否需要重试  
        while (flag && --tryTimes >= 0) {  
            try {  
                mutex.release();  
                LOGGER.info(LogConstant.DST_LOCK + "release lock successfully!");  
                flag = false;  
                break;  
            } catch (Exception e) {  
                LOGGER.error(LogConstant.DST_LOCK + "release lock error!", e);  
            } finally {  
                checkAndRetry(flag, tryInterval, tryTimes);  
            }  
        }  
        return !flag;// 最后还需要重试,说明没拿到锁  
    }  
    

    Java代码  

  3. /** 
         * 获取锁。此处需要加同步,concurrentHashmap无法避免此处的同步问题 
         * @param info 锁信息 
         * @return 锁实例 
         */  
        private synchronized InterProcessMutex getMutex(LockInfo info) {  
            InterProcessReadWriteLock lock = null;  
            if (locksCache.get(info.getLock()) != null) {  
                lock = locksCache.get(info.getLock());  
            } else {  
                lock = new InterProcessReadWriteLock(client, BASE_DIR + info.getLock());  
                locksCache.put(info.getLock(), lock);  
            }  
            InterProcessMutex mutex = null;  
            switch (info.getIsolate()) {  
            case READ:  
                mutex = lock.readLock();  
                break;  
            case WRITE:  
                mutex = lock.writeLock();  
                break;  
            default:  
                throw new IllegalArgumentException();  
            }  
            return mutex;  
        }  
    

    Java代码  

  4. /** 
     * 判断是否需要重试 
     * @param flag 是否需要重试标志 
     * @param tryInterval 重试间隔 
     * @param tryTimes 重试次数 
     */  
    private void checkAndRetry(boolean flag, long tryInterval, int tryTimes) {  
        try {  
            if (flag) {  
                Thread.sleep(tryInterval);  
                LOGGER.info(LogConstant.DST_LOCK + "retry getting lock! now retry time left: " + tryTimes);  
            }  
        } catch (InterruptedException e) {  
            LOGGER.error(LogConstant.DST_LOCK + "retry interval thread interruptted!", e);  
        }  
    }  

     

但是生产环境上还是推荐zookeeper。

关于ZK实现的分布式锁的性能,可以参考这方篇文章:

一个基于zookeeper实现的分布式锁的性能测试

 

 

Redis第三讲:分布式锁的三种实现方法 应用开发时,如果需要在同进程内的不同线程并发访问某项资源,可以使用各种互斥锁、读写锁等JVM锁;如果一台主机上的多个进程需要并发访问某项资源,则可以使用进程间同步的原语,例如信号量、管道、共享内存等。JVM 锁只能作用于单个 JVM,可以简单理解为就是单台服务器(容器),**而对于多台服务器之间,JVM 锁则没法解决,就需要使用一种在全局可见并具有互斥性的锁了,这时候就需要引入分布式锁**。它可以在分布式场景中对资源加锁,避免竞争资源引起的逻辑错 阅读详情

相关推荐

Redis分布式锁的三种方式,实现Java高并发编程

总之,选择使用纯Redis、Lua还是Redisson,很大程度上取决于应用程序的具体要求、对Redis和Lua的熟悉程度以及可以接受的抽象级别。

wjianwei666的专栏 772

聊聊分布式锁

动手点关注干货不迷路????为什么我们需要一把分布式锁?为了效率(efficiency),协调各个客户端避免做重复的工作。即使锁偶尔失效了,只是可能把某些操作多做一遍而已,不会产生其它的不良后果。比如重复发送了一封同样的 email(当然这取决于业务应用的容忍度)。为了正确性(correctness)。在任何情况下都不允许锁失效的情况发生,因为一旦发生,就可能意味着数据不一致...

字节跳动技术团队官方博客 4614

java 3种分布式锁实现方法大比拼,你会选哪种?

分布式锁(Distributed Lock)是一种在分布式系统中用于控制多个节点对共享资源进行访问控制的机制。它的目的是确保在分布式环境下,同一时间只有一个节点能够获取到锁,并且其他节点需要等待释放锁后才能获取。通过本文的详细介绍,相信你已经掌握了三种常见的分布式锁实现方法及其应用场景。无论你是选择高性能的 Redis,还是高可靠的 ZooKeeper,亦或是简单的数据库实现,都能根据实际需求选择最适合的方案。希望这篇文章能帮助你在项目中顺利实现分布式锁,提升系统的稳定性和性能!

java专栏 881

分布式锁解决方案总结

基于redis、zookeeper的分布式锁原理及实现

qq_33594592的博客 1737

MAIXCAM无线连接实战:5分钟搞定远程调试与代码烧录(避坑版)

本文提供了一份详细的MAIXCAM无线连接实战指南,帮助嵌入式视觉开发者快速配置远程调试与代码烧录。文章重点介绍了通过MaixHub IDE进行Wi-Fi配置、建立远程终端连接、使用SFTP进行文件传输以及管理自启动应用程序的完整流程,并针对IP地址变动、信号中断等常见问题提供了实用的避坑解决方案,旨在提升开发效率与灵活性。

weixin_29188043的博客 297

深入解析JESD204B Subclass 1中的SYSREF与LMFC同步机制

本文深入解析了JESD204B Subclass 1协议中的核心同步机制,重点阐述了SYSREF信号如何作为系统级参考,对齐所有设备内部的本地多帧时钟(LMFC),从而实现高速多通道数据采集系统中的确定性延迟。文章结合实战经验,详细说明了硬件设计、时序要点及软件配置流程,是解决多ADC芯片同步对齐问题的关键指南。

weixin_29271263的博客 398

从认知到实现,一文读懂实现分布式锁的五种方案。

分布式锁是指在分布式系统中,多个进程或线程之间为了避免冲突而对某个共享资源加锁的机制。分布式锁通常用于保证在分布式系统中,只有一个进程或线程能访问某个共享资源。分布式锁可以分为两种:悲观锁和乐观锁。悲观锁是指在访问共享资源之前,先对共享资源加锁,然后再进行访问。如果其他进程或线程也尝试访问共享资源,那么它们将被阻塞,直到当前进程或线程释放锁。悲观锁保证了共享资源的互斥访问,但可能会导致其他进程或线程长时间等待。乐观锁是指在访问共享资源之前,先对共享资源进行版本控制,然后再进行访问。

m0_50308467的博客 5046

魔兽RPG老地图逆向工程:解密三国列传3.0武将招募限制(附JASS实战)

本文详细解析了《三国列传3.0》魔兽RPG地图的武将招募限制机制,通过逆向工程和JASS脚本修改技术,突破SLK加密防线并解密游戏逻辑。文章提供了从工具准备到代码修改的完整实战指南,帮助玩家解除武将数量限制,重温经典游戏的乐趣。

i1j2k的博客 362

分布式锁的 3 种实现方案

前言 今天跟大家探讨一下分布式锁的设计与实现,希望对大家有帮助,如果有不正确的地方,欢迎指出,一起学习,一起进步。 分布式锁概述 我们的系统都是分布式部署的,日常开发中,秒杀下单、抢购商品等等业务场景,为了防止库存超卖,都需要用到分布式锁。 分布式锁其实就是,控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。 业界流行的分布式锁实现,一般有这 3 种方式: 基于数据库实现的分布式锁

weixin_42073629的博客 6754

学习利用ce修改游戏生命参数

易语言代码 游戏的简陋界面 用ce查找存储这个1000即生命值的内存地址(得先附加到目标程序上,点击左上角那个电脑按钮) 看到有很多值为1000的,但哪个是生命值呢?真相只有一个 我们点击一下打我这个按钮,看下面的ce界面上地址为004A65C0处的值变了,所以这个就是我们要找的生命值 接下来我们对生命值进行修改,修改为99

zsd747289639的博客 5万+

图解B树的原理及操作

本文主要参考:https://blog.youkuaiyun.com/shenchaohao12321/article/details/83243314 1. B树 1.1 B树的定义 B树也称B-树,它是一颗多路平衡查找树。我们描述一颗B树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点,一般用字母m表示阶数。当m取2时,就是我们常见的二叉搜索树。 一颗m阶的B树定义如下: 1)每个结点最多有m-1个关键字。 2)根结点最少可以只有1个关键字。 3)非根结点至少有Math.ceil(m/2)-

jinking01的专栏 3万+

分布式锁学习详解

1. 分布式锁 1.1 什么是分布式锁 在我们进行单机应用开发涉及并发同步的时候,我们往往采用synchronized或者ReentrantLock的方式来解决多线程间的代码同步问题。但是当我们的应用是在分布式集群工作的情况下,那么就需要一种更加高级的锁机制,来处理种跨机器的进程之间的数据同步问题,这就是分布式锁。 分布式锁,是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要

qq_45228323的博客 649

ubuntu-emto-cpa 测试和使用(单核运行)

ubuntu-emto-cpa 测试和使用(单核运行)

qq_45834835的博客 1399

常见的三种实现分布式锁的方法

为了保证在高并发下,一个方法或者属性同一时间只能被同一个线程执行,单机部署下可以用ReentrantLock或者Synchronized进行互斥控制。分布式集群系统中就需要用分布式锁来解决。一般来说实现分布式锁有三种方式。 一、基于数据库实现排他锁 建一张表,设置方法名称字段唯一,在获取锁的时候可以通过两种方法: 1、直接insert,如果失败则已经存在锁了,获取锁失败,如果insert成功则获取锁成功,释放锁直接delete。 2、update,再加一个字段,锁的状态,先查寻,然后update,如

dembo的博客 1050

Linux----Ubuntu系统官网下载iso镜像文件

Linux----Ubuntu系统官网下载iso镜像文件

MQ0522的博客 5万+

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

分布式锁的三种主流实现方法

Redis分布式锁

m0_52710583的博客 1132

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

实现分布式锁的常用的三种方法

1、什么是分布式锁? 在说分布式锁之前,我们先了解一下单体应用锁的特点,单体应用锁是在一个JVM进程内有效,无法跨越JVM、跨进程。而分布式锁就是可以跨越多个JVM、跨越多个进程的锁。 2、目前存在的分布式锁的解决方案 分布式锁都是通过第三方组件来实现的,目前比较流行的分布式解决方案有: 数据库,通过数据库可以实现分布式锁,但是在高并发的场景下对数据库的压力比较大,所以很少使用。常用实现方法有:select ... for update 、将字段的属性设置为unite key。 R...

Champion-Dai 359

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

操作系统锁的实现方法有哪几种_分布式锁的这三种实现90%的人都不知道

一、为什么要使用分布式锁我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,可以使用我们学到的Java多线程的18般武艺进行处理,并且可以完美的运行,毫无Bug!注意这是单机应用,也就是所有的请求都会分配到当前服务器的JVM内部,然后映射为操作系统的线程进行处理!而这个共享变量只是在这个JVM内部的一块内存空间!后来业务发展,需要做集群,一个应用需要部署到几台机器上然后做负载均...

weixin_39717367的博客 196

在 Spring Boot 分布式项目中使用分布式锁的三种实现方法

在分布式系统中,分布式锁是解决并发访问问题的重要手段。

流楚丶格念的博客 1870

三种实现分布式锁方法

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34407348的博客 95

Redis实现分布式锁的三种方案

在进入正题之前要搞清楚两个问题:为什么需要分布式锁?Redis为什么能够实现分布式锁? 假设现在有一个应用部署在了三台机器上,应用的某个资源需要进行加锁控制,如果用关键字synchronized加锁能控制住么?显然是不行的,因为synchronized是线程锁,只能作用在当前的JVM里,获取的锁是各自JVM主内存上的锁资源。就好比一个房间有三个门,不惯是打开哪个门上的锁都能进入这个房间。此时就需要一个统一的入口来提供锁资源,这就是分布式锁。能作为锁还必须满足一个条件,就是操作必须是原子性的,锁本身要能保证线

weixin_45497155的博客 4719
上一篇: 实现分布式锁:Redis SETNX 命令
下一篇: Java中ArrayList和LinkedList区别
大树叶
博客等级 码龄12年 1171粉丝 664原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值