synchronized:
包括synchronized方法和synchronized块。
synchronized方法使用this作为默认的“同步监视器”,而synchronized块则需自行指定。
wait、notify、notifyAll必须在同步方法或块中,否则会抛出异常
Object.wait:
要执行obj.wait,当前线程必须拥有obj的监视器(monitor)
执行后会导致当前线程进入等待,直到其它线程调用obj.notify或obj.notifyAll
执行后当前线程会放弃在obj上的所有同步请求(synchronization claims)
注意只是使当前线程处于等待,但对同步方法/块的占有却释放了,所以其它线程便可以随后访问这个同步方法/块了
Object.notify,Object.notifyAll:
要执行obj.notify、obj.notifyAll,当前线程必须拥有obj的监视器(monitor)
obj.notify会随机唤醒一个等待在obj上的线程,而obj.notifyAll却会唤醒所有等待在obj上的线程
被唤醒的线程不能马上执行,直到当前线程放弃obj的同步锁(The awakened threads will not be able to proceed until the current thread relinquishes the lock on this object. ),也就是当前线程离开了同步方法/块后
实战一下吧:创建三个线程(a线程打印字符a,b线程打印字符b,c线程打印字符c),使三个线程协作打印出abcabcabc。
class Printer extends Thread {
private String chr;
private Object pre, curr;
public Printer(Object pre, Object curr, String chr) {
super();
this.pre = pre;
this.curr = curr;
this.chr = chr;
}
@Override
public void run() {
for(int i = 0; i < 3; i++) {
synchronized(pre) {
synchronized(curr) {
System.out.print(chr);
curr.notify();
}
try {
pre.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.print("\n"+chr+"end");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
Object
a = new Object(),
b = new Object(),
c = new Object();
Printer
printerA = new Printer(c, a, "a"),
printerB = new Printer(a, b, "b"),
printerC = new Printer(b, c, "c");
printerA.start();
Thread.sleep(2000);
printerB.start();
Thread.sleep(2000);
printerC.start();
}
}
这个例子你可能看到过,是的,我也是网上发现的。但是你发现没,最终结果是:
abcabc
aend
bend(未出现)
cend(未出现)
而且程序并没有退出!为什么?
为简化问题,我们不妨把for循环去掉,原理是一样的
@Override
public void run() {
synchronized(pre) {
synchronized(curr) {
System.out.print(chr);
curr.notify();
}
try {
pre.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("\n"+chr+"end");
}
a线程执行后打印“a”,唤醒等待在a上的线程(此时其实并没有其它线程在a上等待)并在pre.wait处等待
b线程执行后打印“b”,唤醒等待在b上的线程(此时其实并没有其它线程在b上等待)并在pre.wait处等待
c线程执行后打印“c”,唤醒等待在c上的线程并在pre.wait处等待。请注意,等待在c上的线程被唤醒了,因此a线程继续往下执行,打印出“aend”后a线程结束。但问题是b,c线程呢?他们并没有被唤醒,因此它们始终在pre.wait处等待着!
因此我们必须为b,c设置一个出口:当i=2(其实是循环次数-1即3-1=2)的时候不执行pre.wait
class Printer extends Thread {
private String chr;
private Object pre, curr;
public Printer(Object pre, Object curr, String chr) {
super();
this.pre = pre;
this.curr = curr;
this.chr = chr;
}
@Override
public void run() {
for(int i = 0; i < 3; i++) {
synchronized(pre) {
synchronized(curr) {
System.out.print(chr);
curr.notify();
}
if(i < 2) {
try {
pre.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
System.out.print("\n"+chr+"end");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
Object
a = new Object(),
b = new Object(),
c = new Object();
Printer
printerA = new Printer(c, a, "a"),
printerB = new Printer(a, b, "b"),
printerC = new Printer(b, c, "c");
printerA.start();
Thread.sleep(2000);
printerB.start();
Thread.sleep(2000);
printerC.start();
}
}
当i=2时跳过pre.wait,程序继续执行。之后i=3,由于此时i已经不满足for循环的要求,所以循环结束,直接执行System.out.print("\n"+chr+"end"),之后线程退出。