理论
被调用wait方法时,线程必须持有被调用对象的锁,当调用wait方法之后,线程会释放锁
在调用 thread 的 sleep 是不会释放锁的
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
synchronized(obj) {
obj.wait();
}
调用wait方法之后,要退出这种状态的只有一下四种情况的一种发生
1.当有一个线程调用了 当前锁对象的 notify 并且碰巧唤醒的是这个线程(这个地方解释下为啥是碰巧:因为这个锁对象可能是很多个线程使用的锁,每当一个线程调用了这个锁对象的wait方法,就是把这个线程放入这个锁对象的等待队列中,所以说这个队列中可能会有很多等待的线程,然后notify只能唤醒一个)
2.当有一个线程调用了 当前锁对象的 notifyAll 方法
3.当前线程异常退出
4.等待时间超时(假如设置了等待时间)
总结
1.当调用wait方法前,首先需要保证调用wait方法的线程持有了对象的锁
2.当调用wait方法后,调用wait方法的线程会释放锁,进入等待状态
3.当这个线程进入等待状态,他就可以等待其他线程调用这个锁的 notify 或者 notifyAll 唤醒
4.一但被唤醒,他就会就会和其他线程公平的竞争锁
5.调用wait方法 应该放在synchronized代码块或者synchronized方法中
6.当调用对象的notify方法,他会随机唤醒一个该对象的等待队列中的一个线程,这个线程后续会与其他线程公平竞争这个锁
7.当调用对象的notify方法,他会唤醒该对象的等待队列中的所有线程,这些线程后续会与其他线程公平竞争这个锁
8.在某一个时刻之后有一个线程持有锁
代码示例
目标
编写一个多线程程序,实现这样一个目标:
1.存在一个对象,该对象有一个int类型的成员变量counter,该成员变量的初始值为0。
2.创建两个线程,其中一个线程对该对象的成员变量counter增1,另一个线程对该对象的成员变量减1。
3.输出该对象成员变量counter每次变化后的值。
4.最终输出的结果应为:1010101010101......
实现
public class MyObject {
private int counter;
public synchronized void inc() {
while(counter != 0) {
try {
wait();
} catch (Exception e) {
}
}
counter++;
System.out.println(counter);
notifyAll();
}
public synchronized void dec() {
while(counter == 0) {
try {
wait();
} catch (Exception e) {
}
}
counter--;
System.out.println(counter);
notifyAll();
}
}
public class IncThread extends Thread {
private MyObject myObject;
public IncThread(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
for(int a=0; a<30; a++) {
try {
Thread.sleep((long) Math.random() * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myObject.inc();
}
}
}
public class DecThread extends Thread {
private MyObject myObject;
public DecThread(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
for(int a=0; a<30; a++) {
try {
Thread.sleep((long) Math.random() * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myObject.dec();
}
}
}
public class Client {
public static void main(String[] args) {
MyObject myObject = new MyObject();
Thread incthread = new IncThread(myObject);
Thread decThread = new DecThread(myObject);
Thread incthread1 = new IncThread(myObject);
Thread decThread1 = new DecThread(myObject);
incthread.start();
decThread.start();
incthread1.start();
decThread1.start();
}
}