【Java基础教程】(四十三)多线程篇 · 下:深入剖析Java多线程编程:同步、死锁及经典案例——生产者与消费者,探究sleep()与wait()的差异
发布人:shili8
发布时间:2025-02-13 21:40
阅读次数:0
**Java基础教程**(四十三) 多线程篇 · 下**深入剖析Java多线程编程:同步、死锁及经典案例——生产者与消费者**
在前面的文章中,我们已经学习了Java中的基本线程概念和相关方法。然而,实际开发中,往往需要处理复杂的线程逻辑,这就涉及到了同步和死锁的问题。在本文中,我们将深入剖析这些问题,并通过经典案例——生产者与消费者来进行演示。
###1. 同步在多线程环境下,共享资源可能会被多个线程同时访问,这就需要一种机制来保证数据的一致性。这种机制就是同步。
####1.1 synchronized关键字Java提供了`synchronized`关键字来实现同步。这个关键字可以应用于方法、类或块上,用于锁定资源以防止多线程同时访问。
javapublic class SynchronizedExample { private int count =0; public void increment() { synchronized (this) { // 锁定当前对象 count++; } } public int getCount() { synchronized (this) { // 锁定当前对象 return count; } } }
在上面的例子中,`increment()`和`getCount()`方法都使用了`synchronized`关键字来锁定资源。这样就保证了数据的一致性。
####1.2 ReentrantLock除了`synchronized`关键字之外,Java还提供了`ReentrantLock`类来实现同步。这个类比`synchronized`更灵活和高效。
javaimport java.util.concurrent.locks.ReentrantLock; public class ReentrantLockExample { private int count =0; private final ReentrantLock lock = new ReentrantLock(); public void increment() { lock.lock(); // 锁定资源 try { count++; } finally { lock.unlock(); // 解锁资源 } } public int getCount() { lock.lock(); // 锁定资源 try { return count; } finally { lock.unlock(); // 解锁资源 } } }
在上面的例子中,`increment()`和`getCount()`方法都使用了`ReentrantLock`类来实现同步。
###2. 死锁死锁是指两个或多个线程在竞争共享资源时,造成了循环等待现象,从而导致所有线程都无法继续执行下去。
####2.1 死锁的条件要发生死锁,必须满足以下四个条件:
* **互斥性**: 多个线程同时访问同一资源。
* **占有和等待**: 线程占有一个资源,同时等待另一个资源。
* **不剥夺**: 线程占有资源后,不会被其他线程剥夺。
* **循环等待**: 多个线程之间形成了循环等待现象。
####2.2 死锁的避免要避免死锁,必须破坏上述四个条件之一。最简单的方法是使用`synchronized`关键字或`ReentrantLock`类来实现同步,这样就可以保证资源的一致性。
###3. 经典案例——生产者与消费者生产者与消费者是一个经典的多线程案例,涉及到生产者和消费者的交互逻辑。这个案例可以用来演示多线程编程中的同步和死锁问题。
####3.1 生产者与消费者类
javaimport java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ProducerConsumerExample { private final Lock lock = new ReentrantLock(); private final Condition notFull = lock.newCondition(); private final Condition notEmpty = lock.newCondition(); private int count =0; private boolean isFull = false; public void produce() { lock.lock(); // 锁定资源 try { while (isFull) { // 等待缓冲区不满 notFull.await(); // 等待缓冲区不满 } count++; isFull = true; System.out.println("生产者生产了一个产品,当前数量为:" + count); notEmpty.signal(); // 通知消费者 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.unlock(); // 解锁资源 } } public void consume() { lock.lock(); // 锁定资源 try { while (!isFull) { // 等待缓冲区满了 notEmpty.await(); // 等待缓冲区满了 } count--; isFull = false; System.out.println("消费者消费了一个产品,当前数量为:" + count); notFull.signal(); // 通知生产者 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.unlock(); // 解锁资源 } } }
在上面的例子中,`produce()`和`consume()`方法都使用了`synchronized`关键字来实现同步。这个案例演示了生产者与消费者的交互逻辑,以及如何避免死锁。
###4. sleep()与wait()
`sleep()`和`wait()`都是用于暂停线程执行的方法,但是它们有不同的作用和使用场景。
####4.1 sleep()
`sleep()`方法是用来暂停当前线程的执行时间,直到指定的毫秒数。这个方法可以用来实现睡眠效果。
javapublic class SleepExample { public void sleep() { try { Thread.sleep(1000); // 暂停1秒 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }
在上面的例子中,`sleep()`方法使用了`Thread.sleep()`方法来暂停线程的执行时间。
####4.2 wait()
`wait()`方法是用来暂停当前线程的执行,并等待其他线程通知。这个方法可以用来实现等待效果。
javapublic class WaitExample { private final Lock lock = new ReentrantLock(); private boolean isReady = false; public void wait() { lock.lock(); // 锁定资源 try { while (!isReady) { // 等待准备好 lock.wait(); // 等待准备好 } System.out.println("准备好了"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.unlock(); // 解锁资源 } } public void notify() { lock.lock(); // 锁定资源 try { isReady = true; lock.notifyAll(); // 通知等待线程 } finally { lock.unlock(); // 解锁资源 } } }
在上面的例子中,`wait()`和`notify()`方法都使用了`synchronized`关键字来实现同步。这个案例演示了如何使用`wait()`和`notify()`方法来实现等待效果。
###5. 总结本文深入剖析了Java多线程编程中的同步、死锁及经典案例——生产者与消费者。通过上述内容,我们可以看出,多线程编程中需要考虑到资源的共享和互斥性,以及如何避免死锁的问题。同时,我们也学习到了如何使用`synchronized`关键字和`ReentrantLock`类来实现同步,以及如何使用`sleep()`和`wait()`方法来暂停线程的执行。
###6. 参考* [Java多线程编程]( />* [Java并发API]( />* [生产者与消费者问题](