分布式锁其实很简单,6行代码教你实现redis分布式锁

news2024/11/22 11:17:17

在这里插入图片描述

一、前言

分布式锁是一种用于协调分布式系统中多个节点之间对共享资源进行访问控制的机制。它可以确保在分布式环境下,同一时间只有一个节点能够获取到锁,并且其他节点需要等待释放锁后才能获取。

以下是使用分布式锁的几个常见场景和原因:

  1. 避免资源冲突:当多个节点需要同时对共享资源进行读写操作时,使用分布式锁可以确保同一时间只有一个节点能够执行写操作,避免数据冲突和一致性问题。

  2. 防止重复处理:在某些业务场景中,可能会出现重复处理的问题,例如订单支付、秒杀等。使用分布式锁可以确保同一时间只有一个节点能够处理该任务,避免重复处理和产生脏数据。

  3. 控制资源并发:某些资源的并发操作会导致性能问题,如数据库的并发写操作。使用分布式锁可以限制对资源的并发访问,提高系统的稳定性和性能。

  4. 避免死锁:在分布式环境下,由于网络延迟等原因,可能会发生死锁的情况。使用分布式锁可以避免死锁问题的发生,确保资源的正确释放。

二、使用redisTemplate.opsForValue().setIfAbsent

  1. 获取锁:

    • 客户端通过在Redis中设置一个特定的键,作为锁的标识,并设置过期时间以避免死锁情况。这个操作可以通过Redis的SETNX命令来实现。如果SETNX命令返回1,表示锁获取成功,客户端可以继续执行相应的业务逻辑;如果返回0,表示锁已经被其他客户端持有,客户端需要等待或进行重试操作。
    • 可以通过Redis的SET命令设置锁的过期时间,以防止锁一直被持有而导致死锁。设置过期时间可以保证即使持有锁的客户端发生异常退出,锁也会在过期时间后自动释放。
  2. 释放锁:

    • 客户端完成业务操作后,通过DEL命令删除锁的键,即可释放锁。只有持有锁的客户端才能删除锁,以避免误删其他客户端的锁。

下面是一个示例:

@Component
public class DistributedLock {
    private static final String LOCK_KEY = "my_lock";
    private static final long EXPIRE_TIME = 30000; // 锁的过期时间,单位为毫秒
    private static final long WAIT_TIME = 1000; // 获取锁时的等待时间,单位为毫秒
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    public boolean acquireLock() throws InterruptedException {
        long start = System.currentTimeMillis();
        while (true) {
            Boolean success = redisTemplate.opsForValue().setIfAbsent(LOCK_KEY, "locked", EXPIRE_TIME, TimeUnit.MILLISECONDS);
            if (success != null && success) {
                return true;
            }
            
            long current = System.currentTimeMillis();
            if (current - start > WAIT_TIME) {
                return false;
            }
            
            Thread.sleep(100); // 等待一段时间后进行重试
        }
    }
    
    public void releaseLock() {
        redisTemplate.delete(LOCK_KEY);
    }
}

在上述代码中,acquireLock方法尝试获取分布式锁,如果成功获取,则返回true;如果超过等待时间仍未获取到锁,则返回false。releaseLock方法用于释放锁。

使用setIfAbsent的缺点

使用redisTemplate.opsForValue().setIfAbsent()方法实现分布式锁存在以下缺点:

  1. 可靠性问题:使用redisTemplate.opsForValue().setIfAbsent()方法实现分布式锁时,需要手动编写代码来处理锁的获取和释放,容易出现人为的错误,如忘记释放锁、锁的过期时间设置不正确等。而Redisson框架提供了更加可靠的分布式锁实现,内部封装了各种功能的锁,并提供了易于使用的API,能够确保锁的可靠性和正确性。

  2. 功能限制:redisTemplate.opsForValue().setIfAbsent()方法只能实现简单的锁功能,无法支持更复杂的功能,如可重入锁、公平锁、红锁和读写锁等。而Redisson框架提供了丰富的分布式锁实现方式,可以根据实际需求选择适用的锁类型。

  3. 性能问题:redisTemplate.opsForValue().setIfAbsent()方法实现分布式锁时,每次都需要与Redis服务器进行通信,可能会造成较高的网络开销和延迟。而Redisson框架通过内部的优化和封装,能够提供更高效的分布式锁实现,减少与Redis服务器的通信次数和网络开销。

  4. 可拓展性问题:使用redisTemplate.opsForValue().setIfAbsent()方法实现分布式锁时,随着业务的发展和变化,可能需要添加更多的功能和特性,而手动编写的代码可能无法满足新的需求。而Redisson框架提供了丰富的锁实现,同时也支持自定义锁的扩展,能够更好地适应业务的变化和拓展。

三、使用redisson实现分布式锁

通过Redisson框架可以方便地实现分布式锁。Redisson是一个基于Redis的分布式Java对象和服务框架,提供了丰富的分布式锁的实现方式。

要使用Redisson实现分布式锁,需要完成以下步骤:

  1. 引入Redisson依赖:在项目的pom.xml文件中添加Redisson的依赖。
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.14.0</version>
</dependency>
  1. 创建RedissonClient对象:在Spring Boot中,可以通过Redisson的Spring支持来创建RedissonClient对象。
@Configuration
public class RedissonConfig {
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://localhost:6379");
        return Redisson.create(config);
    }
}

在上述代码中,创建了一个RedissonClient对象,并配置了连接Redis的地址。

  1. 实现分布式锁:
    • 使用RedissonClient对象获取RLock对象,RLock是Redisson提供的分布式锁接口。
    • 通过RLock对象的lock方法来获取锁,并在获取锁成功后执行业务逻辑。
    • 通过RLock对象的unlock方法来释放锁。

下面是一个示例:

@Service
public class DistributedLockService {
    @Autowired
    private RedissonClient redissonClient;
    
    public void executeWithLock() {
        RLock lock = redissonClient.getLock("my_lock");
        try {
            lock.lock();
            // 执行业务逻辑...
        } finally {
            lock.unlock();
        }
    }
}

在上述代码中,executeWithLock方法通过redissonClient获取了一个名为"my_lock"的锁,并通过lock方法获取锁。在获取锁成功后,可以执行业务逻辑。最后,通过unlock方法释放锁。

Redisson还提供了其他一些功能强大的分布式锁实现方式,如可重入锁、公平锁、红锁、读写锁等。这些锁的实现方式更加灵活和强大,可以根据实际需求进行选择和使用。

使用Redisson实现分布式锁时,需要确保Redis服务器的可用性和稳定性,以避免单点故障导致的锁失效或锁的不稳定情况。此外,还需要根据具体的应用场景和需求,合理设置锁的过期时间,避免锁的长时间占用。

1. 可重入锁(Reentrant Lock):

可重入锁是指同一个线程可以多次获得同一个锁,而不会发生死锁。Redisson的可重入锁实现是基于Redis的分布式锁的一种特例。

@Service
public class ReentrantLockService {
    @Autowired
    private RedissonClient redissonClient;
    
    public void executeWithReentrantLock() {
        RLock lock = redissonClient.getLock("my_lock");
        try {
            lock.lock();
            // 执行业务逻辑...
            executeWithReentrantLock();
        } finally {
            lock.unlock();
        }
    }
}

在上述代码中,使用redissonClient获取了一个名为"my_lock"的可重入锁,并通过lock方法获取锁。在获取锁成功后,可以执行业务逻辑,包括递归调用executeWithReentrantLock方法。最后,通过unlock方法释放锁。

2. 公平锁(Fair Lock):

公平锁是指按照线程请求锁的顺序来分配锁。Redisson的公平锁实现可以保证多个线程按照先后顺序获取锁。

@Service
public class FairLockService {
    @Autowired
    private RedissonClient redissonClient;
    
    public void executeWithFairLock() {
        RLock lock = redissonClient.getFairLock("my_lock");
        try {
            lock.lock();
            // 执行业务逻辑...
        } finally {
            lock.unlock();
        }
    }
}

在上述代码中,使用redissonClient获取了一个名为"my_lock"的公平锁,并通过lock方法获取锁。在获取锁成功后,可以执行业务逻辑。最后,通过unlock方法释放锁。

3. 红锁(Red Lock):

红锁是指在多个Redis节点上获取锁,以提高分布式系统的可靠性和容错性。Redisson的红锁实现是基于Redis的分布式锁的一种优化方式。

@Service
public class RedLockService {
    @Autowired
    private RedissonClient redissonClient;
    
    public void executeWithRedLock() {
        RLock lock1 = redissonClient.getLock("lock1");
        RLock lock2 = redissonClient.getLock("lock2");
        RLock lock3 = redissonClient.getLock("lock3");
        
        RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
        try {
            redLock.lock();
            // 执行业务逻辑...
        } finally {
            redLock.unlock();
        }
    }
}

在上述代码中,使用redissonClient分别获取了名为"lock1"、"lock2"和"lock3"的锁,并通过RedissonRedLock将这些锁组合成红锁。在获取红锁成功后,可以执行业务逻辑。最后,通过unlock方法释放红锁。

4. 读写锁(ReadWrite Lock):

读写锁是指在多线程环境下,对于读操作可以并行进行,对于写操作必须互斥进行。Redisson的读写锁实现提供了读锁和写锁两种操作。

@Service
public class ReadWriteLockService {
    @Autowired
    private RedissonClient redissonClient;
    
    public void readWithReadWriteLock() {
        RReadWriteLock rwLock = redissonClient.getReadWriteLock("my_lock");
        RLock readLock = rwLock.readLock();
        try {
            readLock.lock();
            // 执行读操作...
        } finally {
            readLock.unlock();
        }
    }
    
    public void writeWithReadWriteLock() {
        RReadWriteLock rwLock = redissonClient.getReadWriteLock("my_lock");
        RLock writeLock = rwLock.writeLock();
        try {
            writeLock.lock();
            // 执行写操作...
        } finally {
            writeLock.unlock();
        }
    }
}

在上述代码中,使用redissonClient获取了一个名为"my_lock"的读写锁,并通过readLock方法获取读锁,通过writeLock方法获取写锁。在获取锁成功后,可以执行相应的读操作或写操作。最后,通过unlock方法释放锁。

四、遇到redis单点故障怎么办

当使用Redisson实现分布式锁时,如果遇到Redis服务器的单点故障,可以采取以下解决方案:

  1. Redis Sentinel(哨兵模式):Redis Sentinel是Redis官方提供的高可用性解决方案,它通过监控Redis主节点和从节点的状态,实现自动故障转移和故障恢复。在使用Redisson时,可以配置Redis Sentinel来实现高可用性的Redis集群,在主节点故障时,Redis Sentinel会自动将从节点切换为主节点,从而保证分布式锁的可用性。

  2. Redis Cluster(集群模式):Redis Cluster是Redis官方提供的分布式解决方案,通过将数据分散到多个节点上进行存储和访问,实现高可用性和横向扩展。使用Redisson时,可以配置Redis Cluster来搭建分布式锁的集群,当某个节点出现故障时,其他节点仍然可以正常工作,确保分布式锁的可用性。

  3. 使用RedLock算法:RedLock算法是由Redis官方提出的一种多实例锁机制,通过在多个独立的Redis实例之间获取锁,确保锁的可靠性。在使用Redisson时,可以使用RedLock算法来实现分布式锁,通过协调多个Redis实例之间的锁获取和释放,即使部分实例发生故障,仍然可以保证锁的可用性。

  4. 引入其他高可用的中间件:除了Redis本身的高可用性解决方案,也可以考虑引入其他高可用的中间件,如ZooKeeper、etcd等。在使用Redisson时,可以将这些中间件作为分布式锁的协调中心,用于进行锁的获取和释放操作,以保证分布式锁的可用性。

以上解决方案都需要在配置和部署时做相应的工作,如正确配置Redis Sentinel或Redis Cluster、合理设计Redis实例的数量和分布、选择合适的RedLock算法实现等。同时,还需要在代码实现中考虑异常处理和重试机制,以应对可能出现的故障和异常情况。

1、如何实现哨兵模式

哨兵模式是一种用于提供Redis高可用性的解决方案。在哨兵模式下,多个Redis Sentinel进程监控着一个Redis主节点和其对应的从节点,当主节点发生故障时,哨兵会自动进行故障转移,将一个从节点升级为新的主节点,并将其他从节点重新配置为新的主节点的从节点。

下面是哨兵模式的实现步骤:

  1. 配置Redis Sentinel:在Redis Sentinel的配置文件中,需要指定监控的主节点和从节点,并配置哨兵的运行参数。可以通过配置文件或命令行参数来指定。示例配置文件如下:
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000
  1. 启动Redis Sentinel:按照配置启动Redis Sentinel进程,可以运行多个哨兵进程以提高可用性。每个哨兵进程会周期性地监控主节点和从节点的状态,并进行故障检测和故障转移。

  2. 配置Redis客户端:在Redis客户端中,需要配置哨兵模式下的连接参数。通常,需要指定哨兵的地址和端口,以及要连接的Redis实例的名称。示例代码如下(使用Java Redisson框架):

RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
    .master("mymaster")
    .sentinel("127.0.0.1", 26379)
    .sentinel("127.0.0.1", 26380)
    .sentinel("127.0.0.1", 26381);

RedissonClient redisson = Redisson.create(sentinelConfig);
  1. 处理故障转移:当主节点发生故障时,Redis Sentinel会自动进行故障转移。它会选取一个从节点升级为新的主节点,并将其他从节点重新配置为该新主节点的从节点。在故障转移期间,客户端可能会遇到一段时间的不可用,需要处理相关异常或者等待故障转移完成。

哨兵模式下的高可用性是通过监控和自动故障转移来实现的,但在故障转移期间,可能会存在一段时间的不可用,因此需要在应用程序中进行适当的异常处理和重试机制。另外,哨兵模式适用于小规模的Redis集群,对于大规模集群和高并发场景,可以考虑使用Redis Cluster或其他方案来提供更高可用性和性能。

2、如何实现集群模式

Redis Cluster是Redis官方提供的一种分布式解决方案,用于实现Redis的高可用性和横向扩展。Redis Cluster将数据分散存储在多个节点上,并通过使用Gossip协议进行节点之间的通信和数据同步,从而实现了分布式的数据存储和访问。

下面是Redis Cluster的实现步骤:

  1. 配置Redis Cluster:创建一个Redis Cluster所需的Redis实例数量,并按照一定规则将槽位(slot)分配给这些实例。Redis Cluster将数据分为16384个槽位,每个实例负责一部分槽位。可以使用redis-trib.rb工具来进行槽位分配和配置。示例命令如下:
redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002
  1. 启动Redis Cluster节点:按照配置启动Redis Cluster各个节点,每个节点需指定一个端口号,以及相应的槽位分配信息。示例配置文件如下:
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 127.0.0.1
cluster-announce-port 7000
cluster-announce-bus-port 7000
appendonly yes
  1. 配置Redis客户端:在Redis客户端中,需要配置Redis Cluster的连接参数。通常,需要指定多个节点的地址和端口,以便客户端可以与集群中的任何一个节点进行通信。示例代码如下(使用Java Redisson框架):
RedisClusterNodes clusterNodes = new RedisClusterNodes.Builder()
    .addNodeAddress("127.0.0.1:7000")
    .addNodeAddress("127.0.0.1:7001")
    .addNodeAddress("127.0.0.1:7002")
    .build();

RedissonClusterClient redisson = Redisson.createCluster(clusterNodes);
  1. 数据分片和故障转移:当客户端向Redis Cluster发送命令时,Redis Cluster会根据槽位的分配规则将命令路由到相应的节点进行处理。同时,Redis Cluster会自动进行故障检测和故障转移。当一个节点故障或离线时,Redis Cluster会将该节点的槽位重新分配给其他节点,以保证数据的可用性。

Redis Cluster要求至少有3个主节点,并且每个主节点都有至少一个从节点进行数据备份。这样可以保证在单个节点故障时,数据仍然可用,并且Redis Cluster可以自动进行故障转移。同时,为了保证Redis Cluster的高可用性和性能,还需要合理配置集群中的节点数量、网络拓扑等参数。

总之,Redis Cluster通过将数据分布存储和自动故障转移,实现了Redis的高可用性和横向扩展。在使用Redis Cluster时,需要正确配置和启动节点,以及合理设计和管理集群的数据分片和故障转移机制,以提供稳定和可靠的分布式数据存储服务。

3、如何使用RedLock算法
RedLock算法是一种用于在分布式系统中实现分布式锁的算法,它由Redis官方提出。RedLock算法通过在多个独立Redis实例上加锁,以保证分布式环境下的互斥性。

以下是使用RedLock算法的详细步骤:

  1. 配置Redis实例:在多个独立的Redis实例上,需要配置相同的密码,并确保这些实例之间可以相互通信。

  2. 获取锁:当需要获取锁时,客户端应该在多个Redis实例上分别执行以下步骤:

    a. 生成一个唯一的锁标识符(lock identifier)。

    b. 尝试在每个Redis实例上使用SET命令来设置一个锁键(lock key),并设置过期时间。

    c. 统计成功设置锁键的实例数量。

    d. 如果成功设置锁键的实例数量超过半数(majority),则表示锁获取成功;否则,表示锁获取失败。

  3. 释放锁:当不再需要锁时,客户端应该在每个Redis实例上执行以下步骤:

    a. 检查当前锁键是否和之前生成的锁标识符匹配。

    b. 如果匹配,则通过执行DEL命令来删除锁键。

  4. 容错处理:在使用RedLock算法时,需要处理网络延迟、节点故障以及竞争条件等可能引发的问题。可以通过设置合理的锁超时时间、重试机制和容错策略来提高算法的可靠性。

需要注意的是,RedLock算法并不是绝对可靠的,它无法解决网络分区(split-brain)等特殊情况下的一致性问题。因此,在使用RedLock算法时,需要根据具体应用场景和需求来评估其可用性和适用性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1148020.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ChatGPT 驱动软件开发:AI 在软件研发全流程中的革新与实践

目录 内容简介作者简介专家推荐读者对象目录直播预告 计算机技术的发展和互联网的普及&#xff0c;使信息处理和传输变得更加高效&#xff0c;极大地改变了金融、商业、教育、娱乐等领域的运作方式。数据分析、人工智能和云计算等新兴技术&#xff0c;也在不断地影响和改变着各…

misc学习(4)Traffic(流量分析)-

感悟&#xff1a;回想起自己学的计算机网络和网络协议分析&#xff0c;有所感悟&#xff1a;计算机网络好比将一群人区分开来&#xff08;局域网&#xff09;&#xff0c;为了能够使得不同部分的人能够沟通&#xff08;wireshark中的数据包&#xff09;&#xff0c;就设置了网络…

高级深入--day43

通过Fiddler进行手机抓包 通过Fiddler抓包工具&#xff0c;可以抓取手机的网络通信&#xff0c;但前提是手机和电脑处于同一局域网内&#xff08;WI-FI或热点&#xff09;&#xff0c;然后进行以下设置&#xff1a; 用Fiddler对Android应用进行抓包 打开Fiddler设置 在Conne…

DIANA算法c++实现

第一步对具有最大直径的簇中每个点计算平均相异度找出最大的点放入splinter group&#xff0c;其余放在放入splinter group 第二步 在old party里找出到splinter group中点的最近距离 < 到old party中点的最近距离的点&#xff0c;并将该点加入splinter group 重复第二步的…

共用体开发案例

有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码性别、职业、班级。教师的数据包括:姓名、号码、性别、职业、职务。要求用同一个表格来处理。 #include <stdio.h>struct Person {char name[32];int age;char zhiYe;char addr[32];union {int class;…

Ubuntu系统编译调试QGIS源码保姆级教程

在之前的文章中&#xff0c;我详细介绍了怎么在Windows下编译QGIS源码&#xff0c;也得到了不错的反馈。但是不足的是Windows下只能编译QGIS的Release模式和RelWithDebInfo模式&#xff0c;想要分析源码&#xff0c;“断点调试”肯定是少不了的&#xff0c;但是这两种模式虽然也…

论文写作框架示例:论软件系统建模方法及其应用

标题 前言题目要求写作框架(1)摘要(300~330字)(2)正文(2000~2500字,2200字左右为宜)(3)收尾(200字左右)前言 本章内容参考了51cto的薛老师的《软考论文高分特训与范文10篇》的内容,是帮助初学者打开写作思路的工具,而不是必须要遵循的模式。建议软考人多读多看…

集丰照明|灯光布局没做好,几百万装修也拯救不了

俗话说拍照要看灯光的位置&#xff0c;我们在装修的时候也要注重灯光的摆放&#xff0c;不然可能你花了很多钱的装修&#xff0c;结果就会因为灯光的布局不合理&#xff0c;导致效果大打折扣。 装修也是一样&#xff0c;忽略掉灯光&#xff0c;可能就会发生花了几十万&#xff…

08. 按键输入

08. 按键输入 按键原理图代码编写GPIO驱动代码按键驱动代码主函数 加上清除BSS段&#xff0c;代码不运行 按键原理图 按键KEY0连接到了UART1_CTS上。默认情况下&#xff0c;KEY0为高&#xff0c;当按下KEY0后&#xff0c;UART1_CTS为低电平 代码编写 在bsp下创建一个key和一个…

减少磁盘读/写中延迟时间的方法(交替编号,错位命名)

目录 1.延迟时间的优化空间2.交替编号3.磁盘地址结构的设计1.若物理地址结构是&#xff08;盘面号&#xff0c;柱面号&#xff0c;扇区号&#xff09;2.若物理地址结构是&#xff08;柱面号&#xff0c;盘面号&#xff0c;扇区号) 4.错位命名 关于磁盘延迟时间的概念请看博主的…

AQS 关于锁与队列的关系

前面的文章中又介绍的在争用激烈的场景下&#xff0c;使用基于CAS自旋实现的轻量级锁有两个大的问题: (1)CAS恶性空自旋会浪费大量的CPU资源。 (2)在SMP架构的CPU上会导致“总线风暴”。 解决CAS恶性空白旋的有效方式之一是以空间换时间&#xff0c;较为常见的方案有两…

leetCode 229. 多数元素 II + k值摩尔投票法 + 进阶 + 优化空间

229. 多数元素 II - 力扣&#xff08;LeetCode&#xff09; 给定一个大小为 n 的整数数组&#xff0c;找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。 进阶&#xff1a;尝试设计时间复杂度为 O(n)、空间复杂度为 O(1)的算法解决此问题。 &#xff08;1&#xff09;哈希表 class …

“RG-SMP 安全管理平台” 优化

全局添加ICO图标 管理强制重定向https <!-- \SMP\jboss\server\default\deploy\smp.war\common common_js.jsp --> <link rel"shortcut icon" href"data:image/x-icon;base64,AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAAAAAAAAAA…

通俗易懂的GPT原理简介

GPT是自然语言处理领域中最受欢迎的模型之一。GPT全称为Generative Pre-trained Transformer&#xff0c;是一种基于Transformer模型的预训练语言模型。在接下来的文章中&#xff0c;我们将详细介绍GPT的原理。 一、前置知识 在了解GPT原理之前&#xff0c;需要了解以下一些基…

【2023年冬季】华为OD统一考试(B卷)题库清单(已收录345题),又快又全的 B 卷题库大整理

目录 专栏导读华为OD机试算法题太多了&#xff0c;知识点繁杂&#xff0c;如何刷题更有效率呢&#xff1f; 一、逻辑分析二、数据结构1、线性表① 数组② 双指针 2、map与list3、队列4、滑动窗口5、二叉树6、并查集7、栈 三、算法1、基础算法① 贪心算法② 二分查找③ 分治递归…

【Linux】-进程控制(深度理解写时拷贝、exit函数、return的含义以及makefile编译多个程序)-进程创建、进程终止、进程等待、进程程序替换

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

基于SSM的品牌手机销售信息系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

dash--项目的前端展示简单基础

1.前置工作 创建虚拟环境&#xff1a; sudo apt-get install python3-venv # 安装 python3 -m venv venv # 在本目录下创建venv虚拟环境&#xff08;也是一个文件夹。如果用不到这个虚拟环境以后就rm -rf venv&#xff09; source venv/bin/activate # 激活虚拟环境临时使用清华…

精通Nginx(01)-产品概览

Nginx是当今使用最广泛的 Web 服务器之一。2021年W3Techs 宣布&#xff0c; Nginx市场份额超越 Apache HTTP Server 成为全球最常用的 Web 服务器&#xff0c;最新Web Server排名如图所示&#xff08;实际超过4亿站点实用Nginx&#xff09;。 Nginx由Igor Sysoev在2001年创建&…

一年一度表白代码(动态表白)

效果图 # coding:utf-8 import sys import os import random import pygame from pygame.locals import *WIDTH, HEIGHT = 640, 480 BACKGROUND =