资源锁
在Java中,如果一个资源被上了锁,则其他线程必须要等待该资源被释放后才能使用。如下:
String student = "test";
Runnable runnable = () -> {
synchronized (student) {
System.out.println(Thread.currentThread().getName() + "start");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "end");
}
};
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
Thread thread3 = new Thread(runnable);
thread1.start();
thread2.start();
thread3.start();
该代码的输出结果为:
Thread-0start
Thread-0end
Thread-1start
Thread-1end
Thread-2start
Thread-2end
因为线程0把student对象给占用了,所以其他线程在占用该对象之前,必须等待该对象的释放。
wait的作用
wait可以阻塞自己,然后暂时释放自己对资源的占用,等待其他对象调用notify来唤醒他,然后再次对资源进行占用。例如
String student = "test";
Runnable runnable = () -> {
synchronized (student) {
System.out.println(Thread.currentThread().getName() + "start");
try {
student.wait(); // 暂时释放资源
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "end");
}
};
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
Thread thread3 = new Thread(runnable);
thread1.start();
thread2.start();
thread3.start();
输出结果为:
Thread-0start
Thread-1start
Thread-2start
// 阻塞在这个地方
0线程在输出start后,调用了wait方法暂时释放了student的资源锁。所以线程1就可以进入sychronized代码。但是由于没有线程调用notify,所以代码最终阻塞了。
notify
如果调用了notify,则会唤醒一个wait中的资源。网上有说是随机,但是经测试,应该是唤醒最先被阻塞的资源。
String student = "test";
Runnable runnable = () -> {
synchronized (student) {
System.out.println(Thread.currentThread().getName() + "start");
try {
student.wait(); // 暂时释放资源
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "end");
}
};
Runnable notifyR = () -> {
synchronized (student) {
student.notify(); // 唤醒一个结果
}
};
Thread thread1 = new Thread(runnable);
Thread.sleep(1000);
Thread thread2 = new Thread(runnable);
Thread.sleep(1000);
Thread thread3 = new Thread(runnable);
thread1.start();
thread2.start();
thread3.start();
Thread notifyT = new Thread(notifyR);
Thread.sleep(1000);
notifyT.start();
输出结果为:
Thread-0start
Thread-1start
Thread-2start
Thread-0end
//在这里阻塞了
因为只调用了一次notify,所以只唤醒了一个wait,1和2线程还在等待notify来唤醒他们
notifyAll
唤醒所有wait资源。优先唤醒最后阻塞的线程。比如,阻塞顺序为 A-B-C,则唤醒时的顺序为 C-B-A。且C线程释放资源后,B线程才会被唤醒。
String student = "test";
Runnable runnable = () -> {
synchronized (student) {
System.out.println(Thread.currentThread().getName() + "start");
try {
student.wait(); // 暂时释放资源
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "end");
}
};
Runnable notifyR = () -> {
synchronized (student) {
student.notifyAll(); // 唤醒所有wait
}
};
Thread thread1 = new Thread(runnable);
Thread.sleep(1000);
Thread thread2 = new Thread(runnable);
Thread.sleep(1000);
Thread thread3 = new Thread(runnable);
thread1.start();
thread2.start();
thread3.start();
Thread notifyT = new Thread(notifyR);
Thread.sleep(1000);
notifyT.start();
输出结果为:
Thread-0start
Thread-1start
Thread-2start
Thread-2end
Thread-1end
Thread-0end
// 到这里程序结束,没有阻塞
因为这次调用的notifyAll,所有的wait都会被唤醒(不是同时唤醒,而是先等前面的线程把资源锁释放后,才会被唤醒)。所以不会有wait被无限阻塞。