java中lock在买票_JAVA并发(12)— Lock实现生产者消费者

无论是synchronize还是Lock,均可以将其看做为Monitor模型。

1. 条件变量&生产者消费者模型

在Monitor管程中,存在条件变量。

条件变量是管程内的一种数据结构,且只有在管程中才能访问它,它对管程内的所有过程是全局的。只能通过两个原子性操作来操纵它:

c.wait():将调用线(进)程移入条件变量c所持有的队列中,并释放管程,直到另一个线(进)程在条件变量c上调用signal()方法释放线程。

c.signal():会释放条件变量c队列上持有的阻塞线(进)程。

而在synchronized关键字中,在MonitorObject内置了一个条件变量。于是我们在同步块中会使用wait()或notify()方法后,会将同步块中的线程均放入到MonitorObject的Wait Set中,并进行阻塞。Wait Set中存在的是由于不同条件被阻塞的线程。会导致我们唤醒方法时,只能使用notifyAll()唤醒所有的线程。

public class SyncDemo {

private List list = new ArrayList<>(10);

public void producer() {

synchronized (this) {

while (list.size() == 10) {

try {

//若队列满了,将线程存入WaitSet中,等待被唤醒

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

list.add("产品");

this.notifyAll();

}

}

public void consumer() {

synchronized (this) {

while (list.size() == 0) {

try {

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

list.remove(0);

//唤醒生产者

this.notifyAll();

}

}

在Lock中,用户可以定义多个条件变量,即Condition,若未满足条件,则会存储到不同的条件变量所维护的队列中,以便可以唤醒不同条件队列的线程。

条件变量则允许线程由于一些未达到的条件而阻塞,此处的“条件”可以由用户来定义,在访问该条件时需要加锁(互斥量),如果条件没达到,线程将阻塞在该条件上。

public class Demo {

//共享变量

private List list = new ArrayList<>(10);

//互斥量

private ReentrantLock lock = new ReentrantLock();

//条件变量[不为空]。若不符合条件,则加入到条件变量到Condition queue中

private Condition notEmptyCondition = lock.newCondition();

//条件变量[不为满]。若不符合条件,则加入到条件变量到Condition queue中

private Condition notFullCondition = lock.newCondition();

/**

* 生产者模型

*/

public void producer() {

try {

lock.lock();

//若list满了

while (list.size() == 10) {

try {

//线程进入,则将线程放入到不为full的条件变量中

//禁止再次生产。

notFullCondition.await();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

list.add("元素");

//唤醒生产者

notEmptyCondition.signal();

} finally {

lock.unlock();

}

}

/**

* 消费者模型

*/

public void consumer() {

try {

lock.lock();

//若list为空

while (list.size() == 0) {

try {

//将该线程放入到条件变量中,该条件变量为不为空

//禁止再次消费

notEmptyCondition.await();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

list.remove(0);

notFullCondition.signal();

} finally {

lock.unlock();

}

}

}

2. ArrayBlockingQueue阻塞队列

阻塞队列ArrayBlockingQueue实际上也是使用生产者-消费者模型来实现的。

public class BlockingQueueDemo {

private static BlockingQueue blockingQueue = new ArrayBlockingQueue<>(1);

public static void main(String[] args) throws InterruptedException {

/**

* 增加元素

*/

//如果队列满了,则抛出异常。

blockingQueue.add("元素");

//如果队列满了,则返回false。

blockingQueue.offer("元素");

//如果队列满了,则线程阻塞。

blockingQueue.put("元素2");

//如果队列满了,等待一段时间后依旧满,则返回false。

blockingQueue.offer("元素2",1,TimeUnit.MICROSECONDS);

/**

* 移除元素

*/

//如果队列为空,则线程阻塞。

blockingQueue.take();

//如果队列为空,则抛出异常。

blockingQueue.remove();

//如果队列为空,阻塞一段时间后依旧为null,则返回null。

blockingQueue.poll(1, TimeUnit.MILLISECONDS);

//如果队列为空,则返回null。

blockingQueue.poll();

}

}

生产者:当count为items.length数量时,即队列已满,不满足notFull的条件变量,所以线程会进入notFull所维护的条件队列中阻塞。

public void put(E e) throws InterruptedException {

checkNotNull(e);

final ReentrantLock lock = this.lock;

lock.lockInterruptibly();

try {

while (count == items.length)

notFull.await();

enqueue(e);

} finally {

lock.unlock();

}

}

消费者:当items.length数量为0时。那么不满足notEmpty条件变量。该线程会加入到notEmpty所维护的条件队列中。

public E take() throws InterruptedException {

final ReentrantLock lock = this.lock;

lock.lockInterruptibly();

try {

while (count == 0)

notEmpty.await();

return dequeue();

} finally {

lock.unlock();

}

}

推荐阅读

相关阅读

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaLock是一种更高级别的线程同步机制,它比传统的synchronized关键字更加灵活,性能也更好。JavaLock要求显式地获取锁和释放锁,而synchronized则会自动获取和释放锁。下面介绍一下Lock的使用及其常见的使用场景。 ### Lock的使用 JavaLock接口定义了一组方法,用于获取锁、释放锁以及其他一些与锁相关的操作。Lock的常用实现类有ReentrantLock、ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock等。 下面是一个简单的使用ReentrantLock的示例: ```java import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockDemo { private Lock lock = new ReentrantLock(); public void method() { lock.lock(); // 获取锁 try { // 这里是需要同步的代码块 } finally { lock.unlock(); // 释放锁 } } } ``` 在上面的示例,我们使用了ReentrantLock实现锁的功能。在需要同步的代码块前调用lock()方法获取锁,在同步代码块执行完后调用unlock()方法释放锁。 ### Lock的使用场景 Lock的使用场景与synchronized类似,都是在多线程环境下对共享资源进行同步。但是,由于Lock的灵活性更强,所以它的使用场景比synchronized更加广泛。 下面是一些常见的Lock的使用场景: - 高并发情况下的线程同步:在高并发情况下,使用Lock可以提供更好的性能,因为它的实现比synchronized更加高效。 - 读写分离的情况下的线程同步:在读写分离的情况下,使用ReentrantReadWriteLock可以实现读写锁,使得读操作可以并发执行,而写操作需要独占锁,保证数据的一致性。 - 死锁避免:在使用synchronized时,如果由于某些原因没有及时释放锁,就可能导致死锁。而使用Lock时,可以在获取锁的时候设置超时时间,避免死锁的发生。 总之,LockJava一种强大的线程同步机制,使用时需要注意锁的获取和释放,以及异常处理等问题,但它的灵活性和性能优势使得它成为Java并发编程不可或缺的一部分。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值