Java 线程同步互斥 wait、notify、notifyall

一. Java 线程概述

一个线程在其生命周期内总是处于某种特定的状态,线程的状态可定为五种,如下:

1.    创建: 当一个线程对象被声明并创建后,它处于“创建”状态;

2.    就绪:线程对象调用 start() 方法后,将进入“就绪”状态,处于“就绪”状态的线程不是立即执行,而是进入就绪队列,等待CPU;

3.    运行:当就绪队列中具有最高优先级的就绪线程被调度并获得CPU时,便进入“运行”状态,执行 run() 方法,run 方法中定义了线程的操作和功能;

4.    非运行:处于“运行”状态的线程可能因为某些原因 (例如人为挂起)进入“非运行”状态,让出CPU并临时中止自己的执行;

5.    停止:线程完成了它的全部工作或调用 stop() 方法强制中止线程,线程就进入“停止”状态。

                                                     



wait

sleep

归属类

属于Object类(java.lang.Object

属于Thread类(java.lang.Thread),静态方法

释放锁

释放了锁,其它线程同步块或方法

没有释放锁,不出让系统资源(如cpu)

中断唤醒

wait一般不会加时间限制,而是判断是否满足符合条件;如果符合条件,则

notify/notifyall唤醒

sleep(milliseconds)后自动唤醒,如果时间不到可用

interrupt()强制中断

适用范围

同步方法或同步块使用(synchronized)

任何地方都可使用(main、thread线程)

捕获异常

必须捕获异常(try/catch)

不需要捕获异常


二. Wait、notify、notifyAll

  在 Java 中,wait/notify可以实现线程的之间的同步互斥。wait 使线程进入阻塞状态而 notify 则用于唤醒被阻塞的线程,使线程能够继续执行。如

synchronized(object) {
   while(!condition) {
       object.wait();
   }
   object.doSomething();
} 

当线程 获得了 obj 锁后,若发现条件 condition false 则调用 wait()阻塞自己 。 在另一线程 中,如果 更改了某些条件,使得 condition变成 true 唤醒线程A: 

synchronized(object) {
   condition = true;
   object.notify();
}

注意:

(1)调用一个对象(object)的 wait(), notify() 方法前,必须先获得该 object 的锁,所以这两个方法必须写在 synchronized(object) {...} 代码块内。

(2)调用 object.wait() 后,线程 A 就释放了 object 的锁,否则线程 B 无法获得 object 锁,也就无法在synchronized(object) {...} 代码块中唤醒 A。 

(3)当 object.wait() 方法返回后,线程 A 需要再次获得 object 的锁,才能继续执行。 

(4)如果 A1,A2,A3 都调用了 object.wait(),则 B 调用 object.notify() 只能唤醒 A1,A2,A3 中的一个(具体哪一个由JVM决定)。 

(5)object.notifyAll() 会全部唤醒 A1,A2,A3,但是要继续执行 object.wait() 的下一条语句,必须获得 object 锁。因此,A1,A2,A3 只有一个有机会获得锁继续执行。其余的需要等待 object 的锁再次被释放后才能继续执行。 

(6)当 B 调用 object.notify/notifyAll 的时候,B 正持有 object 锁。因此 A1,A2,A3 虽被唤醒了,但是仍无法获得 object 锁。直到 B 退出 synchronized 块,释放 object 锁后,A1,A2,A3 才有机会获得锁继续执行。


三. wait 和 notify 的区别 

wait 与 notify 是 java 同步机制中重要的组成部分。结合与synchronized关键字使用,可以组建很多优秀的多线程模型。 

synchronized(this){} 等价于 public synchronized void method(){.....} 

同步分为类级别和对象级别的同步,两者分别对应类锁和对象锁。 

每个类只有一个类锁,如果 static 方法被 synchronized 修饰,则在这个方法被执行前必须获得类锁; 

对象锁也是类似的。(static synchronized 是类级别的,非 static 的 synchronized 和 synchronized 块都是对象级别的,即作用在同一 new 出来的对象上) 

  调用一个 Object 的 wait 与 notify/notifyAll 的时候,必须保证调用代码对该Object是同步的,也就是说必须在作用等同于 synchronized(obj){......} 的内部才能够去调用 obj 的 wait 与 notify/notifyAll 方法,否则就会报错:java.lang.IllegalMonitorStateException:current thread not owner 

  在调用wait的时候,线程自动释放其占有的对象锁,同时不会去申请对象锁。当线程被唤醒的时候,它才再次申请获取对象锁。notify 与 notifyAll 没有太大的区别,notify 仅唤醒一个线程并允许它去获得锁,notifyAll是唤醒所有等待这个对象的线程并允许它们去获得对象锁。

调用 notifyall 时,虽然对每个 wait 的对象都调用一次 notify,但是这个还是有顺序的,每个对象都保存这一个等待对象链中,调用的顺序就是这个链的顺序。其实启动等待对象链中各个线程的也是一个线程,在具体应用的时候,要注意了。


四.例子

1. 生产者与消费者的实现,生产者与消费者的概念我就不多说了,直接给出 demo 源码吧

// 主类,测试类
public class ProducerConsumer {
	
	public static void main(String[] args) {
		ShareDataQueue queue = new ShareDataQueue();
		new Producer(queue);
		new Consumer(queue);
	}
}

// 生产者类
class Producer implements Runnable {
	
	ShareDataQueue mQueue = null;

	public Producer(ShareDataQueue queue) {
		this.mQueue = queue;
		(new Thread(this, "Producer")).start();
	}

	@Override
	public void run() {
		int counter = 0;
		while (counter < 10) {
			mQueue.put(counter++);
		}
	}
}

// 消费者类
class Consumer implements Runnable {
	
	ShareDataQueue mQueue = null;

	public Consumer(ShareDataQueue queue) {
		this.mQueue = queue;
		(new Thread(this, "Consumer")).start();
	}

	@Override
	public void run() {
		while (mQueue.get() < 10) {
		}
	}
}

// 生产者和消费者的共享实例,可当成一个仓库,生产者往里面存它所生产的// 产品,消费者从里面取出产品
class ShareDataQueue {
	
	int mCounter;
	boolean valueFlag = false;

	// 消费者获取产品的方法
	public synchronized int get() {
		
		if (!valueFlag) { // if valueSet == false,wait else try to got value
			try {
				wait();
				System.out.println("--------- get() method waiting");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		System.out.println("--------- Get counter : " + mCounter);
		valueFlag = false;
		notify();

		return mCounter;
	}

	// 生产者存产品的方法
	public synchronized void put(int counter) {
		
		if (valueFlag) { // valueFlag == true, means there has a value wait to get, else put a value
			try {
				wait();
				System.out.println("+++++++++ put() method waiting");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		this.mCounter = counter;
		System.out.println("+++++++++ Put counter : " + counter);
		valueFlag = true;
		notify();
	}
}

2. 简单的线程同步,运行时会有两个线程,一个是该程序的主线程,另一个是在主线程中显示启动的 SelfWakeupThread 线程,SelfWakeupThread 类中定义了可以唤醒自己的方法notifySelf(),该方法给别的线程唤醒 SelfWakeupThread 线程的机会 。

(1)主类

public class SelfMain {

	public static void main(String[] args) {
		
		SelfWakeupThread thread = new SelfWakeupThread();
		thread.start();
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		thread.notifySelf();
	}
}

(2)SelfWakeupThread 类

public class SelfWakeupThread extends Thread{
	
	@Override
	public void run() {
		
		synchronized(this) {
			try {
				long curTime_1 = System.currentTimeMillis();
				System.out.println("SelfWakeupThread, before waiting...");
				this.wait(); // wait the mNotifyThread object, until mNotifyThread notify me
				long curTime_2 = System.currentTimeMillis(); 
				System.out.println("SelfWakeupThread, timediff = " + (curTime_2 - curTime_1));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}			
		}
	}
	
	public void notifySelf() {
		synchronized (this) {			
			this.notifyAll(); // wake up all the object which is waiting this
		}
	}
}

3. 同步互斥 demo

(1)代表锁的类

/**
 * single instance
 */
public class SingleLock {
	
	private static SingleLock mLock;	
	private SingleLock() {

	}
	
	public static SingleLock getSingleLock() {
		
		if(null == mLock) {
			mLock = new SingleLock();
		}
		return mLock;
	}
}

(2)执行到特定代码时,等待对象锁

public class WaitThread extends Thread{
	
	private SingleLock mLock;	
	public WaitThread(SingleLock lock) {
		mLock = lock;
	}
	
	@Override
	public void run() {
		
		long curTime_1 = System.currentTimeMillis();
		System.out.println("--- WaitThread, before wait()");
		
		synchronized(mLock) {
			try {
				mLock.wait(); // wait the mLock object, until mLock notify me
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		long curTime_2 = System.currentTimeMillis();
		System.out.println("--- WaitThread, after wait(), timediff = " + (curTime_2 - curTime_1));
	}
}

(3)执行完特定操作后,唤醒等待对象锁的其它所有线程

public class NotifyThread extends Thread{
	
	private SingleLock mLock;	
	public NotifyThread(SingleLock lock) {
		mLock = lock;
	}

	@Override
	public void run() {
		long curTime_1 = System.currentTimeMillis();
		
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
				
		synchronized (mLock) {			
			mLock.notifyAll(); // wake up all the object which is waiting this
		}
		
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		long curTime_2 = System.currentTimeMillis();
		System.out.println("+++ NotifyThread, timediff = " + (curTime_2 - curTime_1));
	}
}

(4)主类,测试类

public class MainRunTwoThread {
	
	public static void main(String[] args) {

		NotifyThread notifyThread = new NotifyThread(SingleLock.getSingleLock());
		notifyThread.start();

		WaitThread waitThread = new WaitThread(SingleLock.getSingleLock());
		waitThread.start();
	}
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Storm-Shadow

你的鼓励将是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值