多线程学习笔记(六)之锁对象Lock

接口Lock

首先我们回顾下同步代码块,例如:

Object obj = new Object();
void show(){
    synchronized(obj){
        code...
    }
}

同步代码块对锁的操作是隐式的,执行完同步代码块中的内容,自动释放锁。而Lock将锁封装成为了对象,即把对锁操作的隐式操作换成了显示操作。可以将如上代码改写:

Lock lock = new ReentrantLock();
void show(){

    lock.lock();//获取锁
    code...
    lock.unlock();//释放锁

}

jdk1.5之后将同步和锁封装成了对象,并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。
上述代码中存在隐患,例如code部门产生异常throw Exception的话,会导致后面代码无法执行,即没有释放锁,因此需要将释放锁的操作放在finally块中。

Lock lock = new ReentrantLock();
void show(){

    lock.lock();//获取锁
    try{
        code...//access the resource protected by this lock
    }finally{
        lock.unlock();//释放锁
    }   

}

Condition newCondition():返回绑定到此Lock实例的新Condition实例

接口Condition

public interface Condition ,Condition将Object监视器方法(wait,notify和notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待set(wait-set)。其中,Lock替代了synchronized方法和语句的使用,Condition替代了Object监视器方法的使用。

条件(也称为条件队列或条件变量)为线程提供一个含义,以便在某个状态条件现在可能为true的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式释放相关的锁,并挂起当前线程,就像Object.wait做的那样。

Condition实例实质上被绑定到一个锁上。要为特定Lock实例获得Condition实例,请使用其newCondition()方法。

具体是什么意思呢,个人用自己的理解白话解释下:

正常的传统synchronized块方式,每个锁对象obj对应有一套*wait,notify,notifyAll方法(因为不同锁对象的wait和notify是不起作用的),而Condition对wait,notify,notifyAll方法进行了封装,多个Condition对象可以同时所属于同一个Lock锁对象*

常用方法:

  • await():造成当前线程在接收到信号或被中断之前一直处于等待状态
  • await(long time,TimeUnit unit):造成当前线程在接收到信号、被中断或者到达指定等待时间之前一直处于等待状态
  • signal():唤醒一个等待线程
  • signalAll():唤醒所有等待线程

传统锁与Condition使用简单对比

传统方式:

class Object{
    wait();
    notify();
    notifyAll();
}
class Demo extends Object{
}
Demo d = new Demo();
synchronized(d){
    d.wait;
}

Condition方式:

interface Condition{
    await();
    signal();
    signalAll();
}
Lock lock = new ReentrantLock();
Condition c1 = lock.newCondition();//根据锁对象lock获取一组监视器对象
Condition c2 = lock.newCondition();//第二组监视器

由上可以发现Lock的目的是为了替代同步,Condition的目的是为了替代Object中的方法

Lock方式实现学习笔记(五)中的需求

class ResourceDemo{
    private String name;
    private int count;
    private boolean flag = false;
    //生产者
    //创建一个锁对象
    Lock lock = new ReentrantLock();
    //通过已有的锁获取该锁上的监视器对象
    Condition con = lock.newCondition();
    public void set(String name){
        lock.lock();
        try{
            while (flag){
                try{
                    con.await();
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.name = name + count;
            count++;
            System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
            flag = true;
            //notifyAll();
            con.signalAll();
        }finally {
            lock.unlock();
        }
    }
    //消费者
    public  void out(){
        lock.lock();//获取锁
        try{
            while (!flag){
                try{
                    con.await();
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"......消费者......"+this.name);
            flag = false;
            //notifyAll();
            con.signalAll();
        }finally {
            lock.unlock();
        }
    }
}

//生产者
class Producer implements Runnable{
    private ResourceDemo r;
    Producer(ResourceDemo r){
        this.r = r;
    }
    public void run(){
        while (true){
            r.set("烤鸭");
        }
    }
}
//消费者
class Consumer implements Runnable{
    private ResourceDemo r;
    Consumer(ResourceDemo r){
        this.r = r;
    }
    public void run(){
        while (true){
            r.out();
        }
    }
}



public class ProducerConsumerDemo {
    public static void main(String[] args){
        ResourceDemo r = new ResourceDemo();
        Producer pro = new Producer(r);
        Consumer con = new Consumer(r);

        //四个线程,两个负责生产,两个负责消费
        Thread t0 = new Thread(pro);
        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(con);
        Thread t3 = new Thread(con);

        t0.start();
        t1.start();
        t2.start();
        t3.start();
    }
}

注意点:

  • 使用while而不是if
  • 使用signalAll()而不是signal()

具体原因解释可以参见之前博客:http://blog.csdn.net/megustas_jjc/article/details/71107387

Lock优势

使用两个监视器,一组监视生产者,一组监视消费者,而之前的传统方法要想实现,需要两个锁,因为传统方式一把锁上只有一组监视器,而Lock的方式,一把锁上可以绑定多组监视器。因此可以实现“唤醒对方线程”,解决了之前传统方法notifyAll方式的效率低的问题。

最终Lock方式的实现代码

class ResourceDemo{
    private String name;
    private int count;
    private boolean flag = false;
    //生产者
    //创建一个锁对象
    Lock lock = new ReentrantLock();
    //通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者
    Condition producer_con = lock.newCondition();
    Condition consumer_con = lock.newCondition();
    public void set(String name){
        lock.lock();
        try{
            while (flag){
                try{
                    producer_con.await();
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.name = name + count;
            count++;
            System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
            flag = true;
            //notifyAll();
            consumer_con.signalAll();
        }finally {
            lock.unlock();
        }
    }
    //消费者
    public  void out(){
        lock.lock();//获取锁
        try{
            while (!flag){
                try{
                    consumer_con.await();
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"......消费者......"+this.name);
            flag = false;
            //notifyAll();
            producer_con.signalAll();
        }finally {
            lock.unlock();
        }
    }
}

//生产者
class Producer implements Runnable{
    private ResourceDemo r;
    Producer(ResourceDemo r){
        this.r = r;
    }
    public void run(){
        while (true){
            r.set("烤鸭");
        }
    }
}
//消费者
class Consumer implements Runnable{
    private ResourceDemo r;
    Consumer(ResourceDemo r){
        this.r = r;
    }
    public void run(){
        while (true){
            r.out();
        }
    }
}



public class ProducerConsumerDemo {
    public static void main(String[] args){
        ResourceDemo r = new ResourceDemo();
        Producer pro = new Producer(r);
        Consumer con = new Consumer(r);

        //四个线程,两个负责生产,两个负责消费
        Thread t0 = new Thread(pro);
        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(con);
        Thread t3 = new Thread(con);

        t0.start();
        t1.start();
        t2.start();
        t3.start();
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值