/**
* 建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC。
* 这个问题用Object的wait(),notify()就可以很方便的解决。
*
* 主要的思想就是,为了控制执行的顺序,必须要先持有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。
*
* wait导致当前的线程等待,直到其他线程调用此对象的 notify() 要领或 notifyAll() 要领。当前的线程必须拥有此对象监视器。该线程揭晓对此监视器的一切权并等待,直到其他线程议决调用 notify 要领,或 notifyAll 要领告诉在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得 对监视器的一切权后才能继续执行.
notify唤醒在此对象监视器上等待的单个线程。假如一切线程都在此对象上等待,则会挑选唤醒其中一个线程。直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。此要领只应由作为此对象监视器的一切者的线程来调用.
"当前的线程必须拥有此对象监视器"与"此要领只应由作为此对象监视器的一切者的线程来调用"表明 wait要领与notify要领必须在同步块内执行,即synchronized(obj之内).
调用对像wait要领后,当火线程释放对像锁,进入等待形状 .直到其他线程(也只好是其他线程)议决 notify 要领,或 notifyAll.该线程重新获得 对像锁.
继续执行,记得线程必须重新获得 对像锁才能继续执行.由于 synchronized代码块内没有锁是寸步无法走的.
notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。两者的最大区别在于:
notifyAll使所有原来在该对象上等待被notify的线程统统退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争。
notify则文明得多他只是选择一个wait状态线程进行通知,并使它获得该对象上的锁,但不惊动其他同样在等待被该对象notify的线程们,当第一个线程运行完毕以后释放对象上的锁此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,继续处在wait状态,直到这个对象发出一个notify或notifyAll,它们等待的是被notify或notifyAll,而不是锁。
*/
package t;
public class MyThreadPrinter2 implements Runnable {
private String name;
private Object prev;
private Object self;
private MyThreadPrinter2(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 10;
while (count > 0) {
//同步块,加锁
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count--;
/* try{
Thread.sleep(1);
}
catch (InterruptedException e){
e.printStackTrace();
}*/
self.notify(); //唤醒在此对象监视器上等待的单个线程(即等待给self加锁的线程)。假如多个线程都在此对象上等待,则会挑选唤醒其中一个线程。
}//self解锁,被唤醒的线程此时可以给self加锁了。
try {
prev.wait(); //该线程暂时释放prev的锁,等待再次获得prev的锁,然后执行下面的语句。此时prev还需要被唤醒
} 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();
MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);
new Thread(pa).start();//c a加锁,a输出'A',a唤醒pb,a解锁(synchronized (a){}同步块结束),c.wait()->该线程pa等待(c暂时解锁,直至其它线程执行c.notify()之后,该线程pa才能继续执行(即被唤醒)---可以理解为等待c的通知)
Thread.sleep(10);//在单线程下,Thread.sleep(10000)让你的线程“睡眠”10000ms,也就是不工作,因为是单线程,所以要等到过了10000ms之后,该子线程继续工作。
//多线程下,睡眠的线程main先不工作,让其余的子线程先工作,等过了10000ms之后,它再重新回到线程的等待队伍中,开始工作。
//main睡眠10ms结束后,执行下面的语句,即new Thread(pb).start();----pb线程启动后,main再睡眠10ms,接着启动pc。这样不让pb和pc相邻启动,避免pc和pb竞争(因为开始时pc和pb都符合条件)
new Thread(pb).start();//a b加锁,b输出'B',b唤醒pc,b解锁,a.wait()->该线程pb等待(c暂时解锁,直至其它线程执行a.notify()之后,该线程pb才能继续执行)
Thread.sleep(10);
new Thread(pc).start();//b c加锁,c输出'C',c唤醒pa,c解锁,b.wait()->该线程pc等待(b暂时解锁,直至其它线程执行b.notify()之后,该线程pc才能继续执行)
Thread.sleep(10);
}
}
* 建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC。
* 这个问题用Object的wait(),notify()就可以很方便的解决。
*
* 主要的思想就是,为了控制执行的顺序,必须要先持有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。
*
* wait导致当前的线程等待,直到其他线程调用此对象的 notify() 要领或 notifyAll() 要领。当前的线程必须拥有此对象监视器。该线程揭晓对此监视器的一切权并等待,直到其他线程议决调用 notify 要领,或 notifyAll 要领告诉在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得 对监视器的一切权后才能继续执行.
notify唤醒在此对象监视器上等待的单个线程。假如一切线程都在此对象上等待,则会挑选唤醒其中一个线程。直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。此要领只应由作为此对象监视器的一切者的线程来调用.
"当前的线程必须拥有此对象监视器"与"此要领只应由作为此对象监视器的一切者的线程来调用"表明 wait要领与notify要领必须在同步块内执行,即synchronized(obj之内).
调用对像wait要领后,当火线程释放对像锁,进入等待形状 .直到其他线程(也只好是其他线程)议决 notify 要领,或 notifyAll.该线程重新获得 对像锁.
继续执行,记得线程必须重新获得 对像锁才能继续执行.由于 synchronized代码块内没有锁是寸步无法走的.
notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。两者的最大区别在于:
notifyAll使所有原来在该对象上等待被notify的线程统统退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争。
notify则文明得多他只是选择一个wait状态线程进行通知,并使它获得该对象上的锁,但不惊动其他同样在等待被该对象notify的线程们,当第一个线程运行完毕以后释放对象上的锁此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,继续处在wait状态,直到这个对象发出一个notify或notifyAll,它们等待的是被notify或notifyAll,而不是锁。
*/
package t;
public class MyThreadPrinter2 implements Runnable {
private String name;
private Object prev;
private Object self;
private MyThreadPrinter2(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 10;
while (count > 0) {
//同步块,加锁
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count--;
/* try{
Thread.sleep(1);
}
catch (InterruptedException e){
e.printStackTrace();
}*/
self.notify(); //唤醒在此对象监视器上等待的单个线程(即等待给self加锁的线程)。假如多个线程都在此对象上等待,则会挑选唤醒其中一个线程。
}//self解锁,被唤醒的线程此时可以给self加锁了。
try {
prev.wait(); //该线程暂时释放prev的锁,等待再次获得prev的锁,然后执行下面的语句。此时prev还需要被唤醒
} 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();
MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);
new Thread(pa).start();//c a加锁,a输出'A',a唤醒pb,a解锁(synchronized (a){}同步块结束),c.wait()->该线程pa等待(c暂时解锁,直至其它线程执行c.notify()之后,该线程pa才能继续执行(即被唤醒)---可以理解为等待c的通知)
Thread.sleep(10);//在单线程下,Thread.sleep(10000)让你的线程“睡眠”10000ms,也就是不工作,因为是单线程,所以要等到过了10000ms之后,该子线程继续工作。
//多线程下,睡眠的线程main先不工作,让其余的子线程先工作,等过了10000ms之后,它再重新回到线程的等待队伍中,开始工作。
//main睡眠10ms结束后,执行下面的语句,即new Thread(pb).start();----pb线程启动后,main再睡眠10ms,接着启动pc。这样不让pb和pc相邻启动,避免pc和pb竞争(因为开始时pc和pb都符合条件)
new Thread(pb).start();//a b加锁,b输出'B',b唤醒pc,b解锁,a.wait()->该线程pb等待(c暂时解锁,直至其它线程执行a.notify()之后,该线程pb才能继续执行)
Thread.sleep(10);
new Thread(pc).start();//b c加锁,c输出'C',c唤醒pa,c解锁,b.wait()->该线程pc等待(b暂时解锁,直至其它线程执行b.notify()之后,该线程pc才能继续执行)
Thread.sleep(10);
}
}