您的位置:首页 > 编程语言 > Java开发

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

2014-11-23 01:09 267 查看
一. 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();
}


当线程 A 获得了 obj 锁后,若发现条件 condition为
false 则调用 wait()阻塞自己 。 在另一线程 B 中,如果 B 更改了某些条件,使得 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();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐