多线程之 Lock接口
- 在synchronized的同步机制下,一个锁对象只能对应一组监视器方法,若要提供多组监视器,则必须有多个锁对象,这种情况下锁会相互嵌套,容易引发死锁问题,利用同步实现多生产多消费问题时,只能利用notifyAll()唤醒全部等待中的线程,无法实现本方只唤醒对方,这降低了程序的效率。
- 在jdk1.5之后提供了Lock锁机制,位于java.util.concurrent.locks包下,Lock中将Object类中的监视器方法(wait(),notify(),notifyAll())单独封装成为了Condition对象中的(await(),signal(),signalAll()),并且一个锁对象可以创建多个监视器对象(通过调用Lock的newCondition方法),ReentrantLock类是实现了Lock接口的子类,通过该子类对象为Lock接口实例化,创建锁对象。
Lock锁机制的规范使用格式:
将锁对象的释放置于finally块中,是以防在执行释放锁语句之前程序出现异常,导致无法释放锁资源。
//实例化锁对象
Lock lock = new ReentrantLock();
//实例化监视器
Condition condition = lock.newCondition();
lock.lock(); // 上锁
try {
... // 处理代码,在这之间根据需要有监视器方法的调用
} finally {
lock.unlock(); // 释放锁
}
应用实例:多生产多消费
package thread类测试.lock锁机制;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 多线程应用情景2:
* 多生产多消费者问题
* 问题1:生产了商品没有被消费,同一个商品被消费多次
* 产生原因:被唤醒的线程没有判断标记,造成问题1的产生。
* 解决:只要让被唤醒的线程必须判断标记就可以了,即将if判断标记的方式更改为while判断标记的方式。**记住:多生产多消费,必须使用while判断标记。**
* 问题2:加入while判断后,出现了死锁。
* 产生原因:生产方唤醒了线程池中等待的生产方的线程,即本方唤醒了本方。
* 解决:希望本方要唤醒对方,利用Lock来实例化不同的监视器,从而实现本方唤醒对方。
* 此种解决方案解决了问题2,并且解决了synchronized唤醒全部等待中线程遗留的效率问题。
*
* 2018年9月30日下午4:03:21
*/
public class ProducerConsumer4 {
public static void main(String[] args) {
//1、创建资源对象
Resource r = new Resource();
//2、创建线程任务
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
//3、创建线程
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
//4、启动线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
//1、描述资源。属性:商品名称和编号;行为:对商品名称赋值,获取商品
class Resource{
private String name;
private int count = 1;
Lock lock = new ReentrantLock();
//实例化生产者监视器
Condition produce = lock.newCondition();
//实例化消费者监视器
Condition consume = lock.newCondition();
//定义标记,false表示没有商品,true表示有商品待消费
private boolean flag = false;
public String getName() { //供消费者调用
lock.lock();
try {
while(!flag) {
try {
consume.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread()+".....消费了..."+this.name);
//消费了商品,修改标记
flag = false;
//唤醒生产者,只需要唤醒其中一个生产者即可,提高了程序的效率
produce.signal();
} finally {
lock.unlock();
}
return name;
}
public void setName(String name) { //供生产者调用
lock.lock();
try {
while(flag) {
try {
produce.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name + this.count;
this.count++;
System.out.println(Thread.currentThread()+"...生产了..."+this.name);
//生产了商品,修改标记
flag = true;
//唤醒消费者,只需要唤醒其中一个消费者即可,提高了程序的效率
consume.signal();
}finally {
lock.unlock();
}
}
}
//2、描述生产者
class Producer implements Runnable {
private Resource r;
//生产者一初始化就要有资源,需要将资源传递到构造方法中
public Producer(Resource r) {
this.r = r;
}
@Override
public void run() {
for(int i=0;i<50;i++) {
r.setName("面包");
}
}
}
//3、描述消费者
class Consumer implements Runnable {
private Resource r;
//消费者一初始化就要有资源,需要将资源传递到构造方法中
public Consumer(Resource r) {
this.r = r;
}
@Override
public void run() {
for(int i=0;i<50;i++) {
r.getName();
}
}
}