本文共 5145 字,大约阅读时间需要 17 分钟。
1.Object类的wait()、notify()和notifyAll(),必需由同步监视器对象来调用
- wait(),导致当前线程等待,直到其他线程调用该同步监视器的notify()或notifyAll()来唤醒该线程,调用该方法的当前线程会释放对该同步监视器的锁定
- notify(),唤醒在此同步监视器上等待的单个线程,如果所有线程都在该同步监视器上等待,则会选择唤醒其中一个,选择是任意性的,只有当前线程放弃对该同步监视器的锁定(使用wait方法)后,才可以执行被唤醒的线程
- notifyAll(),唤醒在此同步监视器上等待的所有线程
eg:建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC
package jichu;/*建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C要求线程同时运行,交替打印10次ABC思路:Thread A->Thread B->Thread C->Thread A循环为了控制顺序,必须确定唤醒、等待的顺序,每个线程必须同时有两个对象锁,prev前一个;self自身先持有prev,也就是前一个线程要释放自身对象锁,再去申请自身对象锁,两者兼备打印之后先调用self.notify释放自身,唤醒下一个等待线程,再调用prev.wait释放prev对象锁,终止当前线程,等待循环结束后再次被唤醒A先运行,持有CA,后释放AC,唤醒B;B等待A,再申请B,后打印B,再释放BA,唤醒C;C等待B,再申请C,打印后释放CB,唤醒A */public class MyThreadPrinter implements Runnable { private String name; private Object prev; private Object self; private MyThreadPrinter(String name,Object prev,Object self){ this.name=name; this.prev=prev; this.self=self; } public void run(){ int count=10; while(count>0){ synchronized (prev){ synchronized (self){ System.out.print(name); count--;// 调用self.notify()释放自身对象锁,唤醒下一个等待线程 self.notify(); } try{// 调用prev.wait()释放prev锁,终止当前等待线程,等待循环结束后再次被唤醒 prev.wait(); }catch (InterruptedException e){ e.printStackTrace(); } } } } public static void main (String [] args)throws Exception{ Object a=new Object(); Object b=new Object(); Object c=new Object(); MyThreadPrinter pa=new MyThreadPrinter("A",c,a); MyThreadPrinter pb=new MyThreadPrinter("B",a,b); MyThreadPrinter pc=new MyThreadPrinter("C",b,c); new Thread(pa).start(); Thread.sleep(100); new Thread(pb).start(); Thread.sleep(100); new Thread(pc).start(); Thread.sleep(100); }}
2. Condition
如果程序不使用synchronized关键字来保证同步,而是直接用Lock对象来保证同步,则系统中不存在隐式的同步监视器,也就不能使用上述方法
当使用Lock对象来保证同步,java提供一个Condition类来保持协调,使用Condition可以让那些已经得到Lock对象而无法继续执行的线程释放Lock对象,Condition对象也可以唤醒其他处于等待的线程
eg:和Object类的wait()、notify()和notifyAll()一样,当线程使用condition.await()时,要求线程持有相关的重入锁,在condition.await()调用后,这个线程会释放这把锁,signal同理,在signal方法被调用后,系统会从当前Condition对象的等待队列中唤醒一个线程,一旦线程被唤醒,它会重新尝试获得与之绑定的重入锁,一旦成功获取即可执行
package LockTest;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;public class LockCondition implements Runnable { public static ReentrantLock lock=new ReentrantLock();// 通过lock生成一个与之绑定的condition对象 public static Condition condition=lock.newCondition(); public void run(){ try{ lock.lock(); condition.await();//线程在condition对象上进行等待 System.out.println("Thread is going on"); }catch(InterruptedException e){ e.printStackTrace(); }finally{ lock.unlock(); } } public static void main(String[] args) throws InterruptedException { // TODO Auto-generated method stub LockCondition lc=new LockCondition(); Thread t1=new Thread(lc); t1.start(); Thread.sleep(2000);// 主线程main发出通知,告诉等待在condition上的线程可以继续执行了 lock.lock(); condition.signal(); lock.unlock(); }}
这种方式在JDK内部被广泛使用,eg,ArrayBlockingQueue
ArrayBlockingQueue的内部元素都放置在一个对象数组中:
/** The queued items */ final Object[] items;
- offer(),如果当前队列已经满了,立即返回false
- put(),如果队列满了,则会一直等待,直到队列中有空闲位置
- poll(),如果队列为空,直接返回null
- take(),如果队列空,一直等待,直到队列中有可用元素
为了做好等待和通知,ArrayBlockingQueue定义如下字段:
/** Main lock guarding all access */ final ReentrantLock lock; /** Condition for waiting takes */ private final Condition notEmpty; /** Condition for waiting puts */ private final Condition notFull;
当执行take操作,如果队列为空,则让当前线程等待在notEmpty上:
public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == 0) notEmpty.await(); return dequeue(); } finally { lock.unlock(); } }
新元素入队列时,则进行一次notEmpty上的通知:
private void enqueue(E x) { // assert lock.getHoldCount() == 1; // assert items[putIndex] == null; final Object[] items = this.items; items[putIndex] = x; if (++putIndex == items.length) putIndex = 0; count++; notEmpty.signal(); }
同理,对于put,当队列满时,让压入线程等待:
public void put(E e) throws InterruptedException { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == items.length) notFull.await(); enqueue(e); } finally { lock.unlock(); } }
当有元素从队列中被挪走,队列中出现空位,通知等待入队的线程:
private E dequeue() { // assert lock.getHoldCount() == 1; // assert items[takeIndex] != null; final Object[] items = this.items; @SuppressWarnings("unchecked") E x = (E) items[takeIndex]; items[takeIndex] = null; if (++takeIndex == items.length) takeIndex = 0; count--; if (itrs != null) itrs.elementDequeued(); notFull.signal(); return x; }
转载地址:https://blog.csdn.net/Autumn03/article/details/81094281 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!