java并发-线程通讯之wait,notify

233 阅读3分钟

理论

被调用wait方法时,线程必须持有被调用对象的锁,当调用wait方法之后,线程会释放锁
在调用 thread 的 sleep 是不会释放锁的
public static void main(String[] args) throws InterruptedException {
      Object obj = new Object();
      //报错  java.lang.IllegalMonitorStateException
      //调用一个对象的 wait() ,  当前对象一定要是锁  
      //obj.wait();

      //编译为字节码之后  , 执行到 synchronized 关键字是对应的 monitorenter
      //当推出synchronized 代码块的时候对应的指令是monitorexit(不管是正常推出还是异常退出)
      //obj.wait() 这个地方也都会对应 一个 monitorexit 指令
      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() {
		//if(counter != 0) {
		while(counter != 0) {
			try {
				wait();
			} catch (Exception e) {
			}
		}
		
		counter++;
		System.out.println(counter);
        
		//notify();
		notifyAll();
		
	}
	
	public synchronized void dec() {
		//if(counter == 0) {
		while(counter == 0) {
			try {
				wait();
			} catch (Exception e) {
			}
		}
		counter--;
		System.out.println(counter);
        
        //notify();
		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) {
				// TODO Auto-generated catch block
				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) {
				// TODO Auto-generated catch block
				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();
		
	}
	

}