Object的锁使用

本文探讨了如何使用Object的锁机制实现生产者消费者问题,以及多消费者多生产者的情况。分析了使用Condition时可能出现的问题,如线程并发导致的数组越界,并提出了解决方案。此外,文章还比较了Synchronized与Lock的区别,指出Lock能实现不同代码块的同步,解决了Synchronized在处理多个对象锁时可能导致的死锁问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇中用Condition实现了生产者和消费者,其中用到了Condition的await和singal,这个实现和Object的锁特别像,如果用Object的锁机制实现生产者和消费者,如下:

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by lizhiqiang on 2016/12/23.
 */
public class testObjectLock2 {
    public static void main(String[] args){
        final BoundedBuffer buffer = new BoundedBuffer();
        ExecutorService service = Executors.newFixedThreadPool(2);

        service.execute(new Runnable() {
            @Override
            public void run() {
                while(true){
                    try {
                        Thread.currentThread().sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    buffer.put(1);
                }
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    System.out.println(new Date() + ":" + buffer.take());
                }
            }
        });
        service.shutdown();
    }
    public static class BoundedBuffer{
        Object notEmpty = new Object();
        Object notFull = new Object();
        final Object[] items = new Object[3];
        int count;
        public BoundedBuffer(){
            count=0;
        }
        public void put(Object x){
            synchronized (notFull){
                if(count==2){
                    try {
                        notFull.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                synchronized (notEmpty){
                    ++count;
                    items[count] = x;
                    notEmpty.notify();
                }
            }
        }
        public Object take(){
            Object result;
            synchronized (notEmpty){
                if(count==0) {
                    try {
                        notEmpty.wait();

                    } catch (InterruptedException e) {
                    }
                }
                synchronized (notFull){
                    result = items[count--];
                    notFull.notify();
                }
                return result;
            }
        }
    }
}
执行结果:

Fri Dec 23 11:07:20 CST 2016:1
Fri Dec 23 11:07:21 CST 2016:1
Fri Dec 23 11:07:22 CST 2016:1
Fri Dec 23 11:07:23 CST 2016:1
Fri Dec 23 11:07:24 CST 2016:1
Fri Dec 23 11:07:25 CST 2016:1
Fri Dec 23 11:07:26 CST 2016:1

发现效果和上一篇一样。


下面开始讲两个问题:

1.用Condition或者Object怎么实现多消费者、多生产者?

2.Condition和Object到底有什么不同?


1.用Condition或者Object怎么实现多消费者、多生产者?

Condition实现:

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by lizhiqiang on 2016/12/23.
 */
public class testCondition3 {
    public static void main(String[] args){
        final BoundedBuffer buffer = new BoundedBuffer();
        ExecutorService service = Executors.newFixedThreadPool(4);
        service.execute(new Runnable() {
            @Override
            public void run() {
                while(true){
                    buffer.put(1);
                }
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    System.out.println("消费者"+Thread.currentThread().getName()+":"+new Date() + ":" + buffer.take());
                }
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    System.out.println("消费者"+Thread.currentThread().getName()+":"+new Date() + ":" + buffer.take());
                }
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    System.out.println("消费者"+Thread.currentThread().getName()+":"+new Date() + ":" + buffer.take());
                }
            }
        });
        service.shutdown();
    }
    public static class BoundedBuffer{
        final Lock lock = new ReentrantLock();

        Condition notEmpty = lock.newCondition();
        Condition notFull = lock.newCondition();
        final Object[] items = new Object[3];
        int count;
        public BoundedBuffer(){
            count=0;
        }
        public void put(Object x){
            lock.lock();
            if(count==2){
                try {
                    notFull.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            ++count;
            items[count] = x;
            notEmpty.signal();
            lock.unlock();
        }
        public Object take(){
            lock.lock();
            Object result;
            if(count==0) {
                try {
                    notEmpty.await();
                } catch (InterruptedException e) {
                }
            }
            result = items[count--];
            notFull.signal();
            lock.unlock();
            return result;
        }
    }
}
Object实现:

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by lizhiqiang on 2016/12/23.
 */
public class testObjectLock3 {
    public static void main(String[] args){
        final BoundedBuffer buffer = new BoundedBuffer();
        ExecutorService service = Executors.newFixedThreadPool(4);

        service.execute(new Runnable() {
            @Override
            public void run() {
                while(true){
                    buffer.put(1);
                }
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    System.out.println("消费者"+Thread.currentThread().getName()+":"+new Date() + ":" + buffer.take());
                }
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    System.out.println("消费者"+Thread.currentThread().getName()+":"+new Date() + ":" + buffer.take());
                }
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    System.out.println("消费者"+Thread.currentThread().getName()+":"+new Date() + ":" + buffer.take());
                }
            }
        });
        service.shutdown();
    }
    public static class BoundedBuffer{
        Object notEmpty = new Object();
        Object notFull = new Object();
        final Object[] items = new Object[3];
        int count;
        public BoundedBuffer(){
            count=0;
        }
        public void put(Object x){
            synchronized (notFull){
                if(count==2){
                    try {
                        notFull.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                synchronized (notEmpty){
                    ++count;
                    items[count] = x;
                    notEmpty.notify();
                }
            }
        }
        public Object take(){
            Object result;
            synchronized (notEmpty){
                if(count==0) {
                    try {
                        notEmpty.wait();

                    } catch (InterruptedException e) {
                    }
                }
                synchronized (notFull){
                    result = items[count--];
                    notFull.notify();
                }
                return result;
            }
        }
    }
}

以上两个实现方式,均实现了一个生产者多个消费者的问题,但是执行会报错:数组越界

为什么?

原因其实出在singal和notify后,对应的线程不是立即执行,而是cpu从等待池中拿到对应的线程,然后再进行上下文切换,才开始执行对应的线程。比如生产者生产一个产品,通知消费者A,此时,生产者开始生产第二个产品,通知消费者B,消费者A和B同时执行,而A执行比较快,一下消费了两个产品,导致B一个产品也拿不到,造成数组越界。

解决方法:

让生产者等一会再通知另一个消费者,让消费者有充足的的时间执行,并再次进入等待。但是这种思路导致生产者效率低下,因为同一时间它只是让一个消费者在执行。

在生产者代码中假如等待时间:

service.execute(new Runnable() {
    @Override
    public void run() {
        while(true){
            try {
                Thread.currentThread().sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            buffer.put(1);
        }
    }
});
另一个思路:

让消费者take一次之后休眠一会,这个比较符合实际的场景。


多生产者多消费者的场景呢?
加等待时间已经不能解决这类数组越界的问题了,只能用try catch将数组越界给吃掉了。

下面一起讨论下第二个内容:

Synchronize和lock的区别是什么呢,有了Synchronize为什么还需要有lock?

首先,synchronize无法解决多个对象锁出现的死锁问题。

比如我们上面的事例中,

public void put(Object x){
    synchronized (notFull){
        if(count==2){
            try {
                System.out.println("产品库满了");
                notFull.wait();
                System.out.println("可以添加产品了,当前有"+count+"个");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("生产者"+Thread.currentThread().getName());
        synchronized (notEmpty){
            System.out.println("要添加产品,当前有"+count+"个");
            ++count;
            items[count] = x;
            System.out.println("添加完产品,当前有"+count+"个");
            notEmpty.notify();
        }
    }
}
public Object take(){
    Object result;
    synchronized (notEmpty){
        if(count==0) {
            try {
                System.out.println("没有产品了");
                notEmpty.wait();
                System.out.println("有产品了");
            } catch (InterruptedException e) {
            }
        }
        System.out.println("消费者"+Thread.currentThread().getName());
        synchronized (notFull){
            System.out.println("要获取产品,当前有"+count+"个");
            result = items[count--];
            System.out.println("取走产品,当前有"+count+"个");
            notFull.notify();
        }
        return result;
    }
}
以上两个方法,一个是生产者调用的,一个是消费者调用的,如果生产者和消费者是一对多的关系,那么极为可能出现死锁。

检查死锁的方法就是将所有的步骤的执行时间都可以看成无限长(主要是和cpu切换上下文以及时间片分配有关)。因为notify方法实现的是将锁交给任意一个,比如:消费者A拿到notEmpty,它在等待notFull锁,而生产者拿到notFull,这时候生产者释放notEmpty锁,将锁交给消费者B,生产者自身开始等消费者A释放notEmpty锁,这样就进入了死锁状态了。(一个生产者和一个消费者一样会出现死锁:只是可能性不是很高)

但是如果改为Lock实现:

public static class BoundedBuffer{
    final Lock lock = new ReentrantLock();

    Condition notEmpty = lock.newCondition();
    Condition notFull = lock.newCondition();
    final Object[] items = new Object[3];
    int count;
    public BoundedBuffer(){
        count=0;
    }
    public void put(Object x){
        lock.lock();
        if(count==2){
            try {
                System.out.println("产品库满了");
                notFull.await();
                System.out.println("可以添加产品了,当前有" + count + "个");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("要添加产品,当前有"+count+"个");
        ++count;
        items[count] = x;
        notEmpty.signal();
        System.out.println("添加完产品,当前有" + count + "个");
        lock.unlock();
    }
    public Object take(){
        lock.lock();
        Object result;
        if(count==0) {
            try {
                System.out.println("没有产品了");
                notEmpty.await();
                System.out.println("有产品了");
            } catch (InterruptedException e) {
            }
        }
        System.out.println("要获取产品,当前有"+count+"个");
        result = items[count--];
        notFull.signal();
        System.out.println("取走产品,当前有" + count + "个");
        lock.unlock();
        return result;
    }
}
就不会出现死锁的问题,因为Lock锁可以有多个condition,两个线程不同方法进入lock代码块是同步的。

这也就是为什么又出现Lock,或者Lock为什么更高级的地方!Lock可以实现不同代码块的同步执行!(而Synchronize做不到,它只是可以做到对象的使用同步)

如果只是一个对象进行加锁,那么无所谓用Lock还是Synchronize,但是如果是多个对象,那么只能用Lock。

<think>好的,我现在需要帮助用户了解如何在Spring Boot项目中使用Redisson实现分布式。根据用户提供的引用内容,我需要整理出步骤和示例代码。首先,用户提到需要教程、示例代码和配置方法,所以我应该从这几个方面入手。 首先,配置Redisson的依赖。引用[1]和[2]都提到了添加Redisson依赖,所以第一步应该是Maven依赖的配置。用户可能用的是Maven,所以需要给出pom.xml中的依赖项,比如redisson-spring-boot-starter的坐标。 接下来是配置文件。需要设置Redis的连接信息,比如地址、密码等。引用[1]和[2]提到在application.yml中添加配置,所以这里需要给出具体的配置示例,包括redis的host、port、password和timeout。 然后是代码实现部分。引用[2]和[3]提到使用注解和切面来实现分布式。用户可能需要一个注解@DistributedLock,以及对应的切面处理类。这部分需要详细说明如何定义注解,如何通过切面在方法执行前后加和解。同时,要结合Redisson的RLock接口,使用tryLock方法,并处理的获取和释放。 示例代码部分,引用[1]和[4]提到了使用try-finally块来确保的释放,所以需要展示一个Service类中使用的例子。可能还需要展示如何在Controller中调用这个Service方法,以体现实际应用。 另外,需要注意异常处理和的释放,避免死。例如,在切面中捕获异常,并在finally块中释放。还要考虑的等待时间和租约时间,引用[4]提到了SetNX,但Redisson的tryLock方法更灵活,可以设置这些参数。 用户可能还会关心Redisson的自动配置,引用[2]提到Spring Boot集成Redisson的便捷性,所以需要说明配置完成后Redisson会自动注入,无需额外配置。 最后,生成相关问题。根据之前的引用内容,可能的问题包括性能分析、优化方法、与其他的比较,以及异常处理等。 需要确保所有步骤清晰,代码示例正确,并且符合中文回答的要求。同时,引用的标识要正确添加,比如引用[^1]、[^2]等。</think>### Spring Boot中使用Redisson实现分布式的步骤 #### 1. 添加Redisson依赖 在`pom.xml`中添加以下依赖: ```xml <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.17.7</version> </dependency> ``` 此依赖会自动配置Redisson客户端。 #### 2. 配置Redis连接 在`application.yml`中配置Redis信息: ```yaml spring: redis: host: 127.0.0.1 port: 6379 password: your_password timeout: 3000 ``` 若需自定义配置,可创建`RedissonConfig`类[^2][^4]。 #### 3. 定义分布式注解 创建注解`@DistributedLock`: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface DistributedLock { String key(); // 的键名 long waitTime() default 30; // 等待的最长时间(秒) long leaseTime() default 10; // 的持有时间(秒) } ``` #### 4. 实现切面逻辑 编写切面类处理加逻辑: ```java @Aspect @Component public class DistributedLockAspect { @Autowired private RedissonClient redissonClient; @Around("@annotation(distributedLock)") public Object lock(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable { RLock lock = redissonClient.getLock(distributedLock.key()); try { boolean acquired = lock.tryLock(distributedLock.waitTime(), distributedLock.leaseTime(), TimeUnit.SECONDS); if (acquired) { return joinPoint.proceed(); } else { throw new RuntimeException("获取失败"); } } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } } ``` 此切面会在目标方法执行前尝试获取,执行后自动释放[^3]。 #### 5. 使用示例 在Service层使用注解: ```java @Service public class OrderService { @DistributedLock(key = "order_lock:#{orderId}", waitTime = 20, leaseTime = 15) public void createOrder(String orderId) { // 业务逻辑(如库存扣减) } } ``` 在Controller中调用: ```java @RestController public class OrderController { @Autowired private OrderService orderService; @PostMapping("/order") public String createOrder(@RequestParam String orderId) { orderService.createOrder(orderId); return "操作成功"; } } ``` ### 关键特性说明 1. **可重入**:Redisson的RLock支持同一线程多次加 2. **看门狗机制**:若未显式设置`leaseTime`,会自动续期防止业务未完成时过期 3. **公平支持**:可通过`redissonClient.getFairLock()`实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值