在单点的程序中我们可以使用synchronized 和 lock来实现,但是在集群环境下。因为程序所在空间不同。已经不能使用这两个来实现锁了。
1. 如何在集群架构中像所有服务共享一个锁。
使用redis分布式锁来实现。 setnx。
将 key 的值设为 value ,当且仅当 key 不存在。若给定的 key 已经存在,返回 0。 不存在 设置值并返回1

springboot 1.5.10.RELEASE

1. 项目中增加redis依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2. application.yml 增加redis配置信息

#redis配置数据
 redis:
    hostName: 123.206.20.217
    port: 6379
    database: 0
    password:
    pool:
      maxTotal: 1000
      maxWaitMillis: 1000
      minEvictableIdleTimeMillis: 300000
      numTestsPerEvictionRun: 1024
      timeBetweenEvictionRunsMillis: 30000
      testOnBorrow: true
      testWhileIdle: true
    timeout: 5000

3. 创建redis配置实例

package com.admin.system.config.redis;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
public class RedisConfig {

    @Bean(name = "jedisPoolConfig")
    @ConfigurationProperties(prefix = "redis.pool")
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig config = new JedisPoolConfig();
        return config;
    }

    @Bean(name = "jedisConnectionFactory")
    @ConfigurationProperties(prefix = "redis")
    public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig jedisPoolConfig) {
        JedisConnectionFactory factory = new JedisConnectionFactory(jedisPoolConfig);
        return factory;
    }


    @Bean(name = "redisTemplate" )
    public RedisTemplate<?, ?> getRedisTemplate(JedisConnectionFactory jedisConnectionFactory) {
        RedisTemplate<?, ?> redisTemplate = new StringRedisTemplate(jedisConnectionFactory);
        /**
         * key序列化类型
         */
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        /**
         * value序列化类型
         */
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return redisTemplate;
    }
}

4. 示例

package com.admin.system.modular.sys.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class RedisImpl {
    @Autowired
    public RedisTemplate redisTemplate;

    @Scheduled(cron = "* * * * * ?")
    public void test() {
        // 模仿多服务
        System.out.println("======================================================");
        new Thread(() -> redisLock("线程1")).start();
        new Thread(() -> redisLock("线程2")).start();
        new Thread(() -> redisLock("线程3")).start();
        new Thread(() -> redisLock("线程4")).start();
    }


    public void redisLock(String threadName) {
        boolean bol = getLock("aa");
        if (bol) {
            try {
                // 执行业务
                System.out.println(threadName + "执行业务");
            } finally {
                unlock("aa");
            }

        } else {
            // 并发执行了
            System.out.println("有服务器正在执行此次无需再次执行");
        }

    }



    // 获得锁 ture 获得锁 false 没有获得锁
    public Boolean getLock(String key) {
        // 创建事物 防止命令执行一半
        redisTemplate.multi();
        redisTemplate.execute(
                (RedisCallback<Boolean>) conn -> {
                    try {
                        conn.setNX(redisTemplate.getStringSerializer().serialize(key),
                                redisTemplate.getStringSerializer().serialize(key));
                    } finally {
                        // 关闭释放链接
                        conn.close();
                    }
                    return null;
                });
        // 防止死锁。 10秒后删除key
        redisTemplate.expire(key, 10, TimeUnit.SECONDS);
        List<Object> list = redisTemplate.exec();
        return (Boolean) list.get(0);
    }
    
    // 释放锁
    private void unlock(String key) {
        redisTemplate.delete(key);
    }
}

输出

======================================================
有服务器正在执行此次无需再次执行
有服务器正在执行此次无需再次执行
线程4执行业务
有服务器正在执行此次无需再次执行
======================================================
有服务器正在执行此次无需再次执行
线程2执行业务
有服务器正在执行此次无需再次执行
有服务器正在执行此次无需再次执行
======================================================
有服务器正在执行此次无需再次执行
线程3执行业务
有服务器正在执行此次无需再次执行
有服务器正在执行此次无需再次执行
======================================================
有服务器正在执行此次无需再次执行
线程2执行业务
有服务器正在执行此次无需再次执行
有服务器正在执行此次无需再次执行
======================================================
线程4执行业务
有服务器正在执行此次无需再次执行
有服务器正在执行此次无需再次执行
有服务器正在执行此次无需再次执行

springboot 2.2.9.RELEASE

yml redis配置

spring:
  application:
    # 服务名
    name: advanced
  #redis配置数据
  redis:
    host: 101.37.152.195
    port: 6379
    database: 10
    password:
    timeout: 5000
    jedis:
      pool:
        max-idle: 10
        max-wait:
        min-idle: 0

server:
  # 服务端口
  port: 9001

RedisLock

package com.advanced.config.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @author 刘志强
 * @date 2020/8/10 11:36
 * redis分布式锁
 */
@Component
public class RedisLock {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 获得锁 ture 获得锁 false 没有获得锁
     *
     * @param key
     * @param time 过期时间
     * @return
     */
    public Boolean getLock(String key, Long time) {
        return stringRedisTemplate.opsForValue().setIfAbsent(key, key, time, TimeUnit.SECONDS);
    }

    /**
     * 释放锁
     *
     * @param key
     */
    public void unLock(String key) {
        stringRedisTemplate.delete(key);
    }

}

使用

@GetMapping("fbs")
    @ResponseBody
    public String fbs() throws InterruptedException {
        boolean bol = redisLock.getLock("a", 10L);
        if (bol) {
            System.out.println("获得锁");
            Thread.sleep(5000L);
            redisLock.unLock("a");
            return "获得锁";
        } else {
            System.out.println("获取锁失败");
            Thread.sleep(1000);
            return fbs();
        }
    }

Redisson官方文档 - 8. 分布式锁和同步器