package cn.shenzhen.feixun; public class PrintABC extends Thread{ private String name; private Object prev; private Object self; public PrintABC(String name,Object prev,Object self){ this.name=name; this.prev=prev; this.self=self; } /** * ,为了控制执行的顺序,必须要先持有prev锁, * 也就是前一个线程要释放自身对象锁,再去申请自身对象锁,两者兼备时打印字母, * 之后首先调用self.notify()释放自身对象锁,唤醒下一个等待线程, * 再调用prev.wait()释放prev对象锁,终止当前线程,等待循环结束后再次被唤醒。 * 程序运行的主要过程就是A线程最先运行,持有C,A对象锁,后释放A,C锁,唤醒B。 * 线程B等待A锁,再申请B锁,后打印B,再释放B,A锁,唤醒C,线程C等待B锁,再申请C锁, * 后打印C,再释放C,B锁,唤醒A…… */ public void run(){ int count=0; while(count<10){ // 先获取 prev锁 如此问题中先将对象C锁住 synchronized (prev) { //然后获取自身的锁如此问题中将对象A锁住 synchronized (self) { System.out.print(name+""); count++; self.notify();//此问题中一共有三个对象ABC此时将self唤醒,是其他线程来竞争self } try { prev.wait(); /** * 注意的是notify()调用后,并不是马上就释放对象锁, * 而是在相应的synchronized(){}语句块执行结束,自动释放锁, * JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。 */ } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws InterruptedException { Object a=new Object(); Object b=new Object(); Object c=new Object(); PrintABC printA=new PrintABC("A", c, a);//第一个线程先将AC对象锁住,A执行完了之后释放锁 PrintABC printB=new PrintABC("B", a, b); PrintABC printC=new PrintABC("C", b, c); /** * 为了避免JVM启动ThreadA、ThreadB、ThreadC三个线程顺序的不确定性。 * 需要让A,B,C三个线程以确定的顺序启动,中间加一段sleep确保前一个线程已启动。 */ printA.start(); /** * sleep()方法导致了当前线程暂停执行指定的时间, * 让出cpu该其他线程,但是他的监控状态依然保持者, * 当指定的时间到了又会自动恢复运行状态。 */ printA.sleep(10); printB.start(); printB.sleep(10); printC.start(); printC.sleep(10); } }