尚硅谷 答疑 redis 第96课 springboot整合redis之RedisTemplate-上集 补充

二==---=---SW0000P=二
spring.swagger2.enabled=true
#在springboot2.6.X结合swagger2.9.X会提示documentationPluginsBootstrapper空指针异常#原因是在springboot2.6.X中将SpringMVC默认路径匹配策略从AntPathMatcher更改为PathPatternParser,#号致出错,解决办法是atching-strategy切换回之ant pathmatcherspring.mvc.pathmatch.matching-strategy=ant path matcher
这一段讲了个啥


这段文字主要讲了在Spring Boot 2.6.X版本结合Swagger 2.9.X版本时可能会遇到一个空指针异常的问题。具体来说:
问题描述:
在Spring Boot 2.6.X版本中,Spring MVC默认的路径匹配策略从AntPathMatcher更改为了PathPatternParser。
这种更改会导致在使用Swagger 2.9.X时出现documentationPluginsBootstrapper空指针异常。
解决办法:
将Spring MVC的路径匹配策略切换回AntPathMatcher。
具体操作是在配置文件中添加一行配置:spring.mvc.pathmatch.matching-strategy=ant_path_matcher。
通过这种方式,可以解决由于路径匹配策略更改而导致的Swagger与Spring Boot不兼容的问题。

===============
为什么要用ThreadLocalRandom

在多线程环境中使用 `ThreadLocalRandom` 而不是 `java.util.Random` 具有以下几个优势:

1. **性能优势**:
   - `ThreadLocalRandom` 是专门为多线程环境设计的。它避免了在多个线程之间共享 `Random` 实例时所需的同步开销,从而提高了性能。
   - 每个线程都有自己的 `ThreadLocalRandom` 实例,这意味着不同线程之间不会相互干扰,减少了竞争。

2. **简化代码**:
   - 使用 `ThreadLocalRandom` 可以避免显式地创建和管理 `Random` 实例。
   - 代码简洁,直接调用 `ThreadLocalRandom.current()` 即可获取当前线程的 `ThreadLocalRandom` 实例。

3. **减少资源争用**:
   - 在高并发环境中,多个线程共享一个 `Random` 实例会导致竞争,进而影响性能。`ThreadLocalRandom` 通过为每个线程提供独立的实例,避免了这种资源争用。

具体到你的代码:
```java
int keyId = ThreadLocalRandom.current().nextInt(1000) + 1;
```
这行代码生成了一个在 1 到 1000 之间的随机整数。使用 `ThreadLocalRandom` 确保了在多线程环境中生成随机数时的高效和安全。

总结:
`ThreadLocalRandom` 是在多线程环境中生成随机数的最佳选择,因为它提供了更高的性能和更低的资源争用。

===========
多线程就用Random怎么了  具体说

在多线程环境中使用 `java.util.Random` 而不是 `ThreadLocalRandom` 可能会导致以下几个问题:

### 1. **竞争条件和性能瓶颈**:
`Random` 类的实例在多线程环境中是共享的。如果多个线程同时访问同一个 `Random` 实例,会导致竞争条件和同步问题。为了防止这些问题,`Random` 的一些方法是同步的,这就会导致性能瓶颈。例如:
```java
public synchronized int nextInt(int bound) {
    // Implementation
}
```
同步方法会导致线程在访问这些方法时被阻塞,从而降低并发性能。

### 2. **线程安全问题**:
如果在多线程环境中不使用同步机制而直接共享 `Random` 实例,可能会导致生成的随机数不正确或重复。这是因为 `Random` 类内部的状态在没有同步保护的情况下可能会被多个线程同时修改,导致不可预测的行为。

### 3. **资源争用**:
多个线程共享一个 `Random` 实例会导致资源争用,影响性能。这种争用会导致线程切换和上下文切换的开销,进一步降低系统的整体性能。

### 使用 `ThreadLocalRandom` 的好处:
1. **无锁设计**:
   `ThreadLocalRandom` 是无锁的,每个线程都有自己的实例,不需要同步机制,因此避免了锁竞争和同步开销。
   
2. **高效性**:
   因为每个线程都有自己的 `ThreadLocalRandom` 实例,生成随机数的操作是本地的,不会受到其他线程的影响,从而提高了性能。

3. **简洁性**:
   使用 `ThreadLocalRandom` 可以避免显式地创建和管理 `Random` 实例,代码更加简洁。例如:
   ```java
   int keyId = ThreadLocalRandom.current().nextInt(1000) + 1;
   ```

### 示例对比:
#### 使用 `Random` 的多线程示例:
```java
Random random = new Random();
Runnable task = () -> {
    int keyId = random.nextInt(1000) + 1;
    System.out.println(keyId);
};

for (int i = 0; i < 10; i++) {
    new Thread(task).start();
}
```
在这个示例中,多个线程共享一个 `Random` 实例,可能会导致性能瓶颈和竞争条件。

#### 使用 `ThreadLocalRandom` 的多线程示例:
```java
Runnable task = () -> {
    int keyId = ThreadLocalRandom.current().nextInt(1000) + 1;
    System.out.println(keyId);
};

for (int i = 0; i < 10; i++) {
    new Thread(task).start();
}
```
在这个示例中,每个线程都有自己的 `ThreadLocalRandom` 实例,没有竞争条件和同步开销,性能更高。

### 结论:
在多线程环境中使用 `ThreadLocalRandom` 而不是 `Random` 可以显著提高性能,避免竞争条件和同步问题。因此,在需要生成随机数的多线程应用中,推荐使用 `ThreadLocalRandom`。在多线程环境中使用 `java.util.Random` 而不是 `ThreadLocalRandom` 可能会导致以下几个问题:

### 1. **竞争条件和性能瓶颈**:
`Random` 类的实例在多线程环境中是共享的。如果多个线程同时访问同一个 `Random` 实例,会导致竞争条件和同步问题。为了防止这些问题,`Random` 的一些方法是同步的,这就会导致性能瓶颈。例如:
```java
public synchronized int nextInt(int bound) {
    // Implementation
}
```
同步方法会导致线程在访问这些方法时被阻塞,从而降低并发性能。

### 2. **线程安全问题**:
如果在多线程环境中不使用同步机制而直接共享 `Random` 实例,可能会导致生成的随机数不正确或重复。这是因为 `Random` 类内部的状态在没有同步保护的情况下可能会被多个线程同时修改,导致不可预测的行为。

### 3. **资源争用**:
多个线程共享一个 `Random` 实例会导致资源争用,影响性能。这种争用会导致线程切换和上下文切换的开销,进一步降低系统的整体性能。

### 使用 `ThreadLocalRandom` 的好处:
1. **无锁设计**:
   `ThreadLocalRandom` 是无锁的,每个线程都有自己的实例,不需要同步机制,因此避免了锁竞争和同步开销。
   
2. **高效性**:
   因为每个线程都有自己的 `ThreadLocalRandom` 实例,生成随机数的操作是本地的,不会受到其他线程的影响,从而提高了性能。

3. **简洁性**:
   使用 `ThreadLocalRandom` 可以避免显式地创建和管理 `Random` 实例,代码更加简洁。例如:
   ```java
   int keyId = ThreadLocalRandom.current().nextInt(1000) + 1;
   ```

### 示例对比:
#### 使用 `Random` 的多线程示例:
```java
Random random = new Random();
Runnable task = () -> {
    int keyId = random.nextInt(1000) + 1;
    System.out.println(keyId);
};

for (int i = 0; i < 10; i++) {
    new Thread(task).start();
}
```
在这个示例中,多个线程共享一个 `Random` 实例,可能会导致性能瓶颈和竞争条件。

#### 使用 `ThreadLocalRandom` 的多线程示例:
```java
Runnable task = () -> {
    int keyId = ThreadLocalRandom.current().nextInt(1000) + 1;
    System.out.println(keyId);
};

for (int i = 0; i < 10; i++) {
    new Thread(task).start();
}
```
在这个示例中,每个线程都有自己的 `ThreadLocalRandom` 实例,没有竞争条件和同步开销,性能更高。

### 结论:
在多线程环境中使用 `ThreadLocalRandom` 而不是 `Random` 可以显著提高性能,避免竞争条件和同步问题。因此,在需要生成随机数的多线程应用中,推荐使用 `ThreadLocalRandom`。


===============
int keyId = ThreadLocalRandom.current().nextInt( bound: 1000)+1;string serialNo =uuID.randomuuID().tostring();
string key = ORDER KEY+keyId;String value ="京东订单"+serialNo;
redisTemplate.opsForValue().set(key,value);
log.info("***key:{}",key);log.info("***value:{}",value);
——————————————————
opsForValue干什么的


`opsForValue` 是 Spring Data Redis 中 `RedisTemplate` 提供的一个方法,用于操作 Redis 中的字符串类型数据。具体来说,`opsForValue` 返回一个 `ValueOperations` 对象,该对象提供了一系列方法来操作 Redis 中的字符串类型数据。

以下是 `opsForValue` 的一些常用方法:

1. **set(K key, V value)**:
   - 设置指定键的值。如果键已经存在,则覆盖其旧值。
   ```java
   redisTemplate.opsForValue().set("key", "value");
   ```

2. **get(Object key)**:
   - 获取指定键的值。
   ```java
   String value = redisTemplate.opsForValue().get("key");
   ```

3. **increment(K key, long delta)**:
   - 将键的整数值增加指定的增量。
   ```java
   redisTemplate.opsForValue().increment("key", 1);
   ```

4. **decrement(K key, long delta)**:
   - 将键的整数值减少指定的减量。
   ```java
   redisTemplate.opsForValue().decrement("key", 1);
   ```

5. **setIfAbsent(K key, V value)**:
   - 如果键不存在,则设置键的值。
   ```java
   redisTemplate.opsForValue().setIfAbsent("key", "value");
   ```

6. **setIfPresent(K key, V value)**:
   - 如果键存在,则设置键的值。
   ```java
   redisTemplate.opsForValue().setIfPresent("key", "value");
   ```

7. **getAndSet(K key, V value)**:
   - 获取键的旧值并设置新值。
   ```java
   String oldValue = redisTemplate.opsForValue().getAndSet("key", "newValue");
   ```

### 代码示例解析
```java
int keyId = ThreadLocalRandom.current().nextInt(1000) + 1;
String serialNo = UUID.randomUUID().toString();
String key = "ORDER_KEY" + keyId;
String value = "京东订单" + serialNo;

redisTemplate.opsForValue().set(key, value);

log.info("***key:{}", key);
log.info("***value:{}", value);
```

1. **生成随机数 `keyId`**:
   ```java
   int keyId = ThreadLocalRandom.current().nextInt(1000) + 1;
   ```
   生成一个 1 到 1000 之间的随机整数。

2. **生成 UUID `serialNo`**:
   ```java
   String serialNo = UUID.randomUUID().toString();
   ```
   生成一个唯一的 UUID 字符串。

3. **构建 Redis 键 `key`**:
   ```java
   String key = "ORDER_KEY" + keyId;
   ```
   构建一个键,格式为 "ORDER_KEY" 加上随机数。

4. **构建 Redis 值 `value`**:
   ```java
   String value = "京东订单" + serialNo;
   ```
   构建一个值,格式为 "京东订单" 加上 UUID。

5. **将键值对存储到 Redis**:
   ```java
   redisTemplate.opsForValue().set(key, value);
   ```
   使用 `opsForValue().set` 方法将键值对存储到 Redis 中。

6. **日志输出**:
   ```java
   log.info("***key:{}", key);
   log.info("***value:{}", value);
   ```
   输出存储的键和值到日志中。

### 总结
`opsForValue` 是用于操作 Redis 中字符串类型数据的接口,提供了丰富的方法来进行各种操作。在上述代码中,使用 `opsForValue().set` 方法将生成的订单键值对存储到 Redis 中。


=================
加@Slf4j干嘛

`@Slf4j` 是 Lombok 提供的一个注解,用于自动生成日志记录器(Logger)对象。使用这个注解可以简化日志记录器的声明,避免手动创建日志记录器对象。

### 使用 `@Slf4j` 的好处

1. **简化代码**:
   - 无需手动声明和初始化日志记录器对象,减少了样板代码。
   - 例如,使用 `@Slf4j` 之前,通常需要如下代码来创建日志记录器:
     ```java
     import org.slf4j.Logger;
     import org.slf4j.LoggerFactory;

     public class MyClass {
         private static final Logger log = LoggerFactory.getLogger(MyClass.class);

         public void myMethod() {
             log.info("This is a log message");
         }
     }
     ```
   - 使用 `@Slf4j` 之后,只需在类上加上注解,Lombok 会自动生成日志记录器:
     ```java
     import lombok.extern.slf4j.Slf4j;

     @Slf4j
     public class MyClass {
         public void myMethod() {
             log.info("This is a log message");
         }
     }
     ```

2. **减少错误**:
   - 手动创建日志记录器时,容易出错,比如拼写错误或使用错误的类名。使用 `@Slf4j` 可以避免这些错误。

3. **统一日志记录器**:
   - Lombok 提供了多种日志注解(如 `@Log`, `@Log4j`, `@Log4j2`, `@Slf4j` 等),可以根据项目需求统一使用某种日志框架,提高代码一致性。

### 示例代码
结合你之前的代码示例,使用 `@Slf4j` 后的完整代码如下:

```java
import lombok.extern.slf4j.Slf4j;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class OrderService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public void createOrder() {
        int keyId = ThreadLocalRandom.current().nextInt(1000) + 1;
        String serialNo = UUID.randomUUID().toString();
        String key = "ORDER_KEY" + keyId;
        String value = "京东订单" + serialNo;

        redisTemplate.opsForValue().set(key, value);

        log.info("***key:{}", key);
        log.info("***value:{}", value);
    }
}
```

### 解释
1. **`@Slf4j` 注解**:
   - 自动生成一个名为 `log` 的 `Logger` 对象,无需手动创建。

2. **日志记录**:
   - 使用 `log.info` 方法输出日志信息,记录生成的键和值。

3. **其他部分**:
   - 使用 Spring 的 `RedisTemplate` 操作 Redis。
   - 使用 `ThreadLocalRandom` 生成随机数。
   - 使用 `UUID` 生成唯一标识符。

### 总结
`@Slf4j` 注解是 Lombok 提供的工具,用于自动生成日志记录器对象,简化代码,减少错误,提高代码一致性。在需要记录日志的类上使用 `@Slf4j` 注解,可以方便地进行日志记录。

  • 15
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值