Object的notify,wait,notifyAll
首先须要说明的是声明在Object类中声明的方法是java每一个类都应该具备的特性。由于众所周知,Object是java全部类的鼻祖,那么Object中的这三个方法是干嘛用的呢?一句话总结:用来控制java线程的状态,或者说是用来做线程同步的。
首先了解三个基本概念,
线程同步:多线程并发完毕任务,可能须要线程之间的运行有先后顺序,线程A做任务必须等待线程B完毕后才干够做。
线程相互排斥:多个线程并发运行。仅仅有一个线程可以同一时候运行某任务,一般体如今代码段的运行上。比方说A线程在訪问一个类方法的时候。B线程不应该能同一时候訪问该方法,由于该方法内部訪问了一个线程不安全的容器,如ArrayList,假设同一时候訪问该函数就会造成ArrayList的数据不一致。朴素点说就是管理上的混乱。
java线程的七种状态:new、dead、runnable、running、timed_waiting、waiting、blocked
了解完以上概念之后我们看下jvm怎样实现线程的相互排斥与同步的:
java实现这样的线程之间的同步和线程相互排斥都是通过对象锁来完毕的,也就是众所周知的synchronizedkeyword。通过它可以锁定一个对象(可能是实例对象。也可能是一个Class对象【注:静态锁定】),通过这样的锁定可以轻松的实现多线程的相互排斥,步骤大致如此:
1、线程进入一个注明synchronizedkeyword的代码段之前必须获取声明对象的锁,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