标签:object wait notify notifyall
首先需要说明的是声明在Object类中声明的方法是java每个类都应该具备的特性,因为众所周知,Object是java所有类的鼻祖,那么Object中的这三个方法是干嘛用的呢?一句话总结:用来控制java线程的状态,或者说是用来做线程同步的。
首先了解三个基本概念,
线程同步:多线程并发完成任务,可能需要线程之间的执行有先后顺序,线程A做任务必须等待线程B完成后才可以做。
线程互斥:多个线程并发执行,只有一个线程能够同时执行某任务,一般体现在代码段的执行上,比如说A线程在访问一个类方法的时候,B线程不应该能同时访问该方法,因为该方法内部访问了一个线程不安全的容器,如ArrayList,如果同时访问该函数就会造成ArrayList的数据不一致,朴素点说就是管理上的混乱。
java线程的七种状态:new、dead、runnable、running、timed_waiting、waiting、blocked
了解完以上概念之后我们看下jvm如何实现线程的互斥与同步的:
java实现这种线程之间的同步和线程互斥都是通过对象锁来完成的,也就是众所周知的synchronized关键字,通过它能够锁定一个对象(可能是实例对象,也可能是一个Class对象【注:静态锁定】),通过这种锁定可以轻松的实现多线程的互斥,步骤大致如此:
1、线程进入一个注明synchronized关键字的代码段之前必须获取声明对象的锁,java的每个对象内部都有一个内置锁,jvm负责修改该锁被哪个线程占用的信息
2、若该对象的锁未被占用,则当前线程获取该对象锁,jvm修改信息,该线程可以继续执行synchronized中的代码段,此时线程处于runnable或running状态(区别在于是否获得了CPU时间)
3、若该对象锁已经被其他线程占用,则java虚拟机暂停当前请求线程的运行,使其处于blocked状态,当占用锁的线程运行完成后java虚拟机的线程调度器会负责将正在等待该锁的线程由blocked池置入到runnable池中,并将对象锁赋予给它,被唤起的线程此时处于runnabel状态,等待获取CPU时间后就可以继续运行了(running)。
现在问题来了?通过上面的分析我们发现通过synchronized只能实现了线程间的互斥,那么多线的的同步问题怎么解决呢,通过上面的分析我们发现,一个线程的停与起都不受自己的主动控制,而是通过锁机制被java虚拟机被动的调度。这明显实现不了线程同步的机制,所以jvm引入了wait、notify的机制来解决此问题。
首先需要声明的是wait和notify的设计依据是对象锁,所以在执行wait和notify方法的时候,必须首先获取对象锁。以下是线程同步的基本步骤:
(1)假设线程A正在运行处于running状态,它想停止运行,等待线程B执行完一段同步代码后它再回来接着运行,显然这种停止要带有某种标记,以通知线程B执行完某段代码后可以把自己唤起,那么首先线程A要获取一个对象锁L(任意一个对象的内置锁都可以)。
(2)获取L后,线程A在想停止运行的地方执行wait,wait的设计正是为了解决这个问题,其实wait的核心只是做了两件事:1、释放当前线程已经获取的对象锁L;2、jvm将本线程由running置于wating或者timed_waiting状态,其实对象锁L就是(1)中所说的标记,wait释放对象锁其实告诉了jvm它处于waiting状态后在等待什么(其实还是该对象锁L)
(3)A通过wait释放了对象锁后,因为B因为在同步代码块之前也要获取该对象锁,所以在A释放对象锁之前它处于blocked状态,A释放了对象锁之后它由blocked状态改为了runnable状态,获取CPU时间后改为了running状态,并获取了对象锁L,进入了同步代码块。
(4)而notify的作用在于通知jvm将等待同一个对象锁的线程至于blocked状态,仅此而已,再无其它,千万不要以为notify本身会释放该对象锁的功能,no它做的只是修改waiting线程的状态,线程B在执行完notify后线程A的状态改为了blocked。
(5)线程B继续运行,直到所有的同步代码块执行完成,此时jvm释放了B对对象锁L的占用,jvm会从blocked状态的所有线程中选择等待该对象锁的线程A并修改其状态为runnable(概率性选择)。
(6)线程A获取CPU时间,继续wait之后的代码运行
下面举例说明以上过程:
首先我随意写了一个Mutex对象,用于互斥对象
package com.wanyonghui.test.notify; public class Mutex { }
然后定义了两个线程,一个对应上面的线程A,一个对应上面的线程B
package com.wanyonghui.test.notify; /* * 该线程先打出一句话“我是线程WaitThread begin”,然后等待NotifyTread线程完成任务后才打印另一句话“我是线程WaitThread end” */ public class WaitThread extends Thread{ private Mutex mutex; public WaitThread(Mutex mutex){ this.mutex = mutex; } public void run(){ try { System.out.println("我是线程WaitThread begin"); synchronized(mutex){ mutex.wait(); } System.out.println("我是线程WatiThread end"); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.wanyonghui.test.notify; /* * 该线程先睡觉3秒钟,然后打出一句话“我是线程NotifyThread processing”,最后唤醒另一个线程” */ public class NotifyThread extends Thread{ private Mutex mutex; public NotifyThread(Mutex mutex){ this.mutex = mutex; } public void run(){ try { Thread.sleep(3000); synchronized(mutex){ System.out.println("我是线程NotifyThread processing"); mutex.notify(); Thread.sleep(3000); } } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.wanyonghui.test.notify; public class TestNotify { public static void main(String[] args) { Mutex mutex = new Mutex(); NotifyThread wt = new NotifyThread(mutex); WaitThread nt = new WaitThread(mutex); wt.start(); nt.start(); } }
我是线程WaitThread begin(3秒后显示下一行)
我是线程NotifyThread processing(3秒后显示下一行)
我是线程WatiThread end
标签:object wait notify notifyall
原文地址:http://blog.csdn.net/wanzaixiaoxinjiayou/article/details/46399391