JAVA线程13 - 新特性:Lock和条件变量

一、Lock

1. 概述

Lock是JDK 1.5以后将同步和锁封装成了对象。Lock是对之前synchronized的替代。 
Lock接口的实现类:互斥锁ReentrantLock 。

2. synchronized与Lock区别 

synchronized对于锁的操作是隐式的;Lock锁对象是显式的。 
synchronized内只有一个锁。Lock可以有多个Conditoin。

3. 语法 

Lock myLock = new ReentrantLock(); 
myLock.lock();//获取锁 
try{ 
    //do something 
}finally{ 
    //释放锁。需要放在finally子句内,否则抛异常后,锁未被释放,其他线程则会永远被阻塞 
    myLock.unlock(); 
}

二、读写锁

1. 概述

为了提高性能,Java提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,在一定程度上提高了程序的执行效率。

读写锁分为读锁和写锁。多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥。

Java中读写锁有个接口java.util.concurrent.locks.ReadWriteLock,也有具体的实现ReentrantReadWriteLock。

在实际开发中,最好在能用读写锁的情况下使用读写锁,而不要用普通锁,以求更好的性能。

2. 使用读写锁完成一个简单的缓存系统

public class SimpleCacheDemo{

    private Map<String, Object> cache = new HashMap<String, Object>();
    private ReadWriteLock rwlock = new ReentrantReadWriteLock();

    public Object getData(String key){
        rwlock.readLock().lock();
        Object obj = null;
        try{
            obj = cache.get(key);
            if(obj == null){
                rwlock.readLock().unlock();
                rolock.wirteLock().lock();
                try{
                    if(obj == null){
                        obj = queryDB(key);
                        if(obj != null){
                            cache.put(key, obj);
                            return obj;
                        }
                    }
                }finally{
                    rwlock.writeLock().unlock();
                }
                rwlock.readLock().lock();
            }
        }finally{
            rwlock.readLock().unlock();
        }
        return obj;
    }

    //从数据库查出值
    public Object queryDB(key){
        //todo
        return null;
    }
}

三、条件变量

条件变量都实现了java.util.concurrent.locks.Condition接口,条件变量的实例化是通过一个Lock对象上调用newCondition()方法来获取的,这样,条件就和一个锁对象绑定起来了。因此,Java中的条件变量只能和锁配合使用,来控制并发程序访问竞争资源的安全。 

条件变量的出现是为了更精细控制线程等待与唤醒,在Java5之前,线程的等待与唤醒依靠的是Object对象的wait()和notify()/notifyAll()方法,这样的处理不够精细。

Condition将Object监视器方法wait()、notify()、notifyAll()分解成截然不同的对象,以便通过这些对象与任意Lock实现组合使用。 对应的方法是:await()、signal() 、signalAll() 。

四、使用JDK1.5后的新特性对多生产者与多消费者示例优化

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/** 
* Java线程:并发协作-生产者消费者模型 
*/ 
public class ProducerConsumer { 
    public static void main(String[] args) { 
        Godown godown = new Godown(30); 
        Consumer c1 = new Consumer(50, godown); 
        Consumer c2 = new Consumer(20, godown); 
        Consumer c3 = new Consumer(30, godown); 
        Producer p1 = new Producer(10, godown); 
        Producer p2 = new Producer(10, godown); 
        Producer p3 = new Producer(50, godown); 
        Producer p4 = new Producer(10, godown); 
        Producer p5 = new Producer(70, godown); 


        c1.start(); 
        c2.start(); 
        c3.start(); 
        p1.start(); 
        p2.start(); 
        p3.start(); 
        p4.start(); 
        p5.start(); 
    } 
} 


/** 
* 仓库 
*/ 
class Godown { 
    public static final int max_size = 100; //最大库存量 
    public int curnum;     //当前库存量 
    
    private final Lock lock = new ReentrantLock();
    private final Condition connProduce = lock.newCondition();
    private final Condition connConsume = lock.newCondition();
    
    public Godown() { 
    } 

    public Godown(int curnum) { 
        this.curnum = curnum; 
    } 

    /** 
     * 生产指定数量的产品 
     * 
     * @param neednum 
     */ 
    public void produce(int neednum) { 
    lock.lock();
    try{
       //测试是否需要生产 
       while (neednum + curnum > max_size) { 
           System.out.println("要生产的产品数量" + neednum + "超过剩余库存量" + (max_size - curnum) + ",暂时不能执行生产任务!"); 
           try { 
               //当前的生产线程等待 
               connProduce.await();
           } catch (InterruptedException e) { 
               e.printStackTrace(); 
           } 
       } 
       //满足生产条件,则进行生产,这里简单的更改当前库存量 
       curnum += neednum; 
       System.out.println("已经生产了" + neednum + "个产品,现仓储量为" + curnum); 
       //唤醒在此对象监视器上等待的所有消费线程 
       connConsume.signalAll();
    }finally{
    lock.unlock();
    }
    } 

    /** 
     * 消费指定数量的产品 
     * 
     * @param neednum 
     */ 
    public void consume(int neednum) { 
    lock.lock();
    try{
       //测试是否可消费 
       while (curnum < neednum) { 
           try { 
               //当前的消费线程等待 
           	connConsume.await();
           } catch (InterruptedException e) { 
               e.printStackTrace(); 
           } 
       } 
       //满足消费条件,则进行消费,这里简单的更改当前库存量 
       curnum -= neednum; 
       System.out.println("已经消费了" + neednum + "个产品,现仓储量为" + curnum); 
       //唤醒在此对象监视器上等待的所有生产线程 
       connProduce.signalAll();
    }finally{
    lock.unlock();
    }
    } 
} 


/** 
* 生产者 
*/ 
class Producer extends Thread { 
    private int neednum;                //生产产品的数量 
    private Godown godown;            //仓库 

    Producer(int neednum, Godown godown) { 
        this.neednum = neednum; 
        this.godown = godown; 
    } 

    public void run() { 
        //生产指定数量的产品 
        godown.produce(neednum); 
    } 
} 


/** 
* 消费者 
*/ 
class Consumer extends Thread { 
    private int neednum;                //生产产品的数量 
    private Godown godown;            //仓库 

    Consumer(int neednum, Godown godown) { 
        this.neednum = neednum; 
        this.godown = godown; 
    } 

    public void run() { 
        //消费指定数量的产品 
        godown.consume(neednum); 
    } 
}

转载于:https://my.oschina.net/hongdengyan/blog/205352

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值