并发编程12-Condition

30 篇文章 0 订阅

1.概述

1.1 简介

java condition使用及分析

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

  • Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作(wait和notify处理不够精细),相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,阻塞队列实际上是使用了Condition来模拟线程间协作。

  • Condition是一个多线程间协调通信的工具类,使得某个,或者某些线程一起等待某个条件(Condition),只有当该条件具备( signal 或者 signalAll方法被带调用)时 ,这些等待线程才会被唤醒,从而重新争夺锁。

  • Condition是个接口,基本的方法就是await()和signal()方法;

    • Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()
      调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用
    • Conditon中的await()对应Object的wait();
    • Condition中的signal()对应Object的notify();
    • Condition中的signalAll()对应Object的notifyAll()。

2.示例

2.1 自己通过信号来实现按顺序循环执行方法

自己实现通过信号方式来:按照 执行完a(),然后执行b(),然后执行c() 的顺序执行.


//自己实现通过信号方式来:按照 执行完first(),然后执行second(),然后执行third() 的顺序执行.
public class SignalThread {

    private int mySignal;

    public synchronized void first() {
        while (mySignal != 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("first");
        mySignal++;
        notifyAll();
    }

    public synchronized void second() {
        while (mySignal != 1) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("second");
        mySignal++;
        notifyAll();
    }

    public synchronized void third() {
        while (mySignal != 2) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("third");
        mySignal = 0;
        notifyAll();
    }

    public static void main(String[] args) {

        SignalThread d = new SignalThread();
        A a = new A(d);
        B b = new B(d);
        C c = new C(d);

        new Thread(a).start();
        new Thread(b).start();
        new Thread(c).start();

    }
}

class A implements Runnable {

    private SignalThread signalThread;

    public A(SignalThread signalThread) {
        this.signalThread = signalThread;
    }

    @Override
    public void run() {
        while (true) {
            signalThread.first();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

class B implements Runnable {

    private SignalThread signalThread;

    public B(SignalThread signalThread) {
        this.signalThread = signalThread;
    }

    @Override
    public void run() {
        while (true) {
            signalThread.second();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

class C implements Runnable {

    private SignalThread signalThread;

    public C(SignalThread signalThread) {
        this.signalThread = signalThread;
    }

    @Override
    public void run() {
        while (true) {
            signalThread.third();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

2.2 Condition按顺序循环执行方法

通过Condition来实现多个方法按照顺序执行.


public class ConditionThread {
	

	Lock lock = new ReentrantLock();
	Condition firstCondition = lock.newCondition();
	Condition secondCondition = lock.newCondition();
	Condition thirdCondition = lock.newCondition();
	
	
	public void first() {
		lock.lock();
			try {
				firstCondition.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		System.out.println("firstCondition");
		secondCondition.signal();
		lock.unlock();
	}
	
	public  void second() {
		lock.lock();
			try {
				secondCondition.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		System.out.println("secondCondition");
		thirdCondition.signal();
		lock.unlock();
	}
	
	public  void third() {
		lock.lock();
			try {
				thirdCondition.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		System.out.println("thirdCondition");
		firstCondition.signal();
		lock.unlock();
	}
	
	public static void main(String[] args) {
		
		ConditionThread conditionThread = new ConditionThread();
		A a = new A(conditionThread);
		B b = new B(conditionThread);
		C c = new C(conditionThread);
		
		new Thread(a).start();
		new Thread(b).start();
		new Thread(c).start();
		
	}
}

class A implements Runnable {
	
	private ConditionThread conditionThread;
	
	public A(ConditionThread conditionThread) {
		this.conditionThread = conditionThread;
	}

	@Override
	public void run() {
		while(true) {
			conditionThread.first();
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
}
class B implements Runnable {
	
	private ConditionThread conditionThread;
	
	public B(ConditionThread conditionThread) {
		this.conditionThread = conditionThread;
	}
	
	@Override
	public void run() {
		while(true) {
			conditionThread.second();
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
}
class C implements Runnable {
	
	private ConditionThread conditionThread;
	
	public C(ConditionThread conditionThread) {
		this.conditionThread = conditionThread;
	}
	
	@Override
	public void run() {
		while(true) {
			conditionThread.third();
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
}

3. 源码分析

JDK-Condition位置

Condition实现类

Condition对象图

  • Condition接口包含了多种await方式和两个通知方法,由于wait是Object方法,所以新取名叫await.

  • ConditionObject实现了Condition接口,是AbstractQueuedSynchronizer的内部类

  • Reentrantlock的newCondition方法返回与某个lock实例相关的Condition对象

  • 我们看到condition实现是依赖于AQS,而AQS是个抽象类。里面定义了同步器的基本框架,实现了基本的结构功能。只留有状态条件的维护由具体同步器根据具体场景来定制,如常见的 ReentrantLock 、 RetrantReadWriteLock和CountDownLatch 等

下面查看部分源码:

  • Condition
public interface Condition {
    void await() throws InterruptedException;
    void signal();
    void signalAll();
....
}
  • ReentrantLock
public class ReentrantLock implements Lock, java.io.Serializable {
    
    private final Sync sync;

    public Condition newCondition() {
            return sync.newCondition();
    }
    
    abstract static class Sync extends AbstractQueuedSynchronizer {
      final ConditionObject newCondition() {
            return new ConditionObject();
        }
    }    
}
  • AbstractQueuedSynchronizer中内部类ConditionObject
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    
    public class ConditionObject implements Condition, java.io.Serializable {
    
        private static final long serialVersionUID = 1173984872572414699L;
        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;
        
        public ConditionObject() { }
        
        //一.await方法
        public final void await() throws InterruptedException {
         //1.0 如果线程已经被中断了,则不用等待了
          if (Thread.interrupted())
                throw new InterruptedException();
            //1.1.将当前线程包装下后,添加到Condition自己维护的一个链表(等待队列)中。 [每个线程单独的]
            Node node = addConditionWaiter();   
            //1.2. 释放当前线程占有的锁,调用await前,当前线程是占有锁(lock.lock())的,需要释放
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            /*
            1.3 释放完毕后,遍历AQS的队列,看当前节点是否在同步队列中,刚添加的时候在等待对列中,所以isOnSyncQueue为false.不在说明它还没有竞争锁的资格,所以继续将自己沉睡。singal的时候会加入到同步队列中.
            */
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            //1.4 isOnSyncQueue=true(同步队列)被唤醒后,重新开始正式竞争锁,同样,如果竞争不到还是会将自己沉睡,等待唤醒重新开始竞争。尝试唤醒,唤醒后interruptMode = 1
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            //1.5 唤醒之后,如果当前创建的节点next不为空则清空后面的.    
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            //1.6 如果中断    
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }
        
        //1.1 将Condition添加到队列维度(单向链表Node)
         private Node addConditionWaiter() {
            Node t = lastWaiter;    //创建一个t等于最后一个. 
            // If lastWaiter is cancelled, clean out.
            //确保t为lastWaiter
            if (t != null && t.waitStatus != Node.CONDITION) {
            //把之前的节点清空.
                unlinkCancelledWaiters();
                t = lastWaiter;
            }
            //创建Node.CONDITION类型的节点
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            //如果last(最后一个)为空,则说明为空
            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            //新增加的刚才这个节点为最后一个节点
            lastWaiter = node;  
            return node;
        }
        
        //1.2 释放锁,前面的lock.lock产生的锁
        final int fullyRelease(Node node) {
            boolean failed = true;
            try {
                int savedState = getState();
                if (release(savedState)) {
                    failed = false;
                    return savedState;
                } else {
                    throw new IllegalMonitorStateException();
                }
            } finally {
                if (failed)
                    node.waitStatus = Node.CANCELLED;
            }
        }
        
        //二.signal唤醒
         public final void signal() {
            //2.1 如果不是独占锁
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            //firstWaiter为condition自己维护的一个链表的头结点,取出第一个节点后开始唤醒操作 
            Node first = firstWaiter;
            //2.2 唤醒第1个节点
            if (first != null)
                doSignal(first);
        }
        
        //2.2 唤醒第2个节点
         private void doSignal(Node first) {
            do {
                //2.2.1 将firstWaiter指针指到当前的next,指到了则不为空,则不执行(lastWaiter = null;)
                //如果指到则说明为最后一个了.
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                //2.2.2 first准备取走了,将first的next指为空
                first.nextWaiter = null;
                //2.2.3 将该节点放到同步队列中,如果成功则do while结束
                //如果放到同步队列失败,则再继续下一个节点,递归进行,一直保证有叫醒的节点放到同步队列中
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }
        
        //2.2.3 将该节点放到同步队列中
        final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
         //设置为0,开始竞争资源
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
         //enq轮循尝试,添加放到同步队列中
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }
    }
}
  • Condition都有等待队列和同步队列.里面有firstwaiter(指向first)和lastwaiter(指向last).
  • 每个Condition都是有自己的链表队列(Node).是单向链表,通过Node来保存.如果值await()则往尾部追加节点,如果叫醒则将头部节点移除放到同步队列中.
  • 如果唤醒,则唤醒队列中的第1个.

4.wait,notify和Condition的区别

  • notify是随机叫醒线程.Condition是指定线程.
  • Condition一个线程中可以有多个队列(每个Condition一个队列),而wait和notify只能实现一个队列.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值