接口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();
}
}