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);
}
}