最简单的东西,每每包含了最复杂的实现,由于须要为上层的存在提供一个稳定的基础,Object做为java中全部对象的基类,其存在的价值不言而喻,其中wait和notify方法的实现多线程协做提供了保证。html
public class WaitTestDemo { public static void main(String[] args) { Message msg = new Message("process it"); Waiter waiter = new Waiter(msg); new Thread(waiter,"waiterThread").start(); Waiter waiter1 = new Waiter(msg); new Thread(waiter1, "waiter1Thread").start(); Notifier notifier = new Notifier(msg); new Thread(notifier, "notifierThread").start(); System.out.println("All the threads are started"); } public static class Message { private String msg; public Message(String str){ this.msg=str; } public String getMsg() { return msg; } public void setMsg(String str) { this.msg=str; } } public static class Waiter implements Runnable{ private Message msg; public Waiter(Message m){ this.msg=m; } @Override public void run() { String name = Thread.currentThread().getName(); synchronized (msg) { try{ System.out.println(name+" waiting to get notified at time:"+System.currentTimeMillis()); msg.wait(); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println(name+" waiter thread got notified at time:"+System.currentTimeMillis()); //process the message now System.out.println(name+" processed: "+msg.getMsg()); } } } public static class Notifier implements Runnable { private Message msg; public Notifier(Message msg) { this.msg = msg; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name+" started"); try { Thread.sleep(1000); synchronized (msg) { msg.setMsg(name+" Notifier work done"); msg.notify(); msg.notify(); //msg.notifyAll(); } } catch (InterruptedException e) { e.printStackTrace(); } } } }
Output:java
All the threads are started waiterThread waiting to get notified at time:1572344152693 waiter1Thread waiting to get notified at time:1572344152693 notifierThread started waiterThread waiter thread got notified at time:1572344153705 waiterThread processed: notifierThread Notifier work done waiter1Thread waiter thread got notified at time:1572344153706 waiter1Thread processed: notifierThread Notifier work done
也可使用notifyAll,输出为:node
All the threads are started waiterThread waiting to get notified at time:1572344222162 waiter1Thread waiting to get notified at time:1572344222162 notifierThread started waiter1Thread waiter thread got notified at time:1572344223175 waiter1Thread processed: notifierThread Notifier work done waiterThread waiter thread got notified at time:1572344223177 waiterThread processed: notifierThread Notifier work done
发现最后唤醒的顺序颠倒了编程
执行完notify方法,并不会立马唤醒等待线程,在notify方法后面加一段sleep代码就能够看到效果,若是线程执行完notify方法以后sleep 5s,在这段时间内,线程waiterThread1依旧持有monitor,线程waiterThread只能继续等待;数组
在Java中,synchronized
有两种使用形式,同步方法和同步代码块。代码以下:多线程
public class SynchronizedTest { public synchronized void doSth(){ System.out.println("Hello World"); } public void doSth1(){ synchronized (SynchronizedTest.class){ System.out.println("Hello World"); } } }
咱们先来使用Javap来反编译以上代码,结果以下(部分无用信息过滤掉了):并发
public synchronized void doSth(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello World 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return public void doSth1(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: ldc #5 // class com/hollis/SynchronizedTest 2: dup 3: astore_1 4: monitorenter 5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 8: ldc #3 // String Hello World 10: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 13: aload_1 14: monitorexit 15: goto 23 18: astore_2 19: aload_1 20: monitorexit 21: aload_2 22: athrow 23: return
反编译后,咱们能够看到Java编译器为咱们生成的字节码。在对于doSth
和doSth1
的处理上稍有不一样。也就是说。JVM对于同步方法和同步代码块的处理方式不一样。oracle
对于同步方法,JVM采用ACC_SYNCHRONIZED
标记符来实现同步。 对于同步代码块。JVM采用monitorenter
、monitorexit
两个指令来实现同步。jvm
关于这部份内容,在JVM规范中也能够找到相关的描述。ide
方法级的同步是隐式的。同步方法的常量池中会有一个ACC_SYNCHRONIZED
标志。当某个线程要访问某个方法的时候,会检查是否有ACC_SYNCHRONIZED
,若是有设置,则须要先得到监视器锁,而后开始执行方法,方法执行以后再释放监视器锁。这时若是其余线程来请求执行方法,会由于没法得到监视器锁而被阻断住。值得注意的是,若是在方法执行过程当中,发生了异常,而且方法内部并无处理该异常,那么在异常被抛到方法外面以前监视器锁会被自动释放。
同步代码块使用monitorenter
和monitorexit
两个指令实现。 The Java® Virtual Machine Specification 中有关于这两个指令的介绍:
大体内容以下: 能够把执行monitorenter
指令理解为加锁,执行monitorexit
理解为释放锁。 每一个对象维护着一个记录着被锁次数的计数器。未被锁定的对象的该计数器为0,当一个线程得到锁(执行monitorenter
)后,该计数器自增变为 1 ,当同一个线程再次得到该对象的锁的时候,计数器再次自增。当同一个线程释放锁(执行monitorexit
指令)的时候,计数器再自减。当计数器为0的时候。锁将被释放,其余线程即可以得到锁。
同步方法经过
ACC_SYNCHRONIZED
关键字隐式的对方法进行加锁。当线程要执行的方法被标注上ACC_SYNCHRONIZED
时,须要先得到锁才能执行该方法。同步代码块经过
monitorenter
和monitorexit
执行来进行加锁。当线程执行到monitorenter
的时候要先得到所锁,才能执行后面的方法。当线程执行到monitorexit
的时候则要释放锁。每一个对象自身维护这一个被加锁次数的计数器,当计数器数字为0时表示能够被任意线程得到锁。当计数器不为0时,只有得到锁的线程才能再次得到锁。便可重入锁。
每一个对象分为三块区域:对象头、实例数据和对齐填充。
级锁定、重量级锁定、GC标记、可偏向)下对象的存储内容以下表所示。
Synchronized一般被称为重量级锁,可是1.6以后对其进行优化,新增了轻量级锁和偏向锁,这里重点说下重量级锁,随后对Synchronized的优化简单介绍下。
从对象头的存储内容能够看出锁的状态都保存在对象头中,Synchronized也不例外,当其从轻量级锁膨胀为重量级锁时,锁标识位为10,其中指针指向的是monitor对象(也称为管程或监视器锁)的起始地址。
关于Synchronized的实如今java对象头里较为简单,只是改变一下标识位,并将指针指向monitor对象的起始地址,其实现的重点是monitor对象。
在HotSpot虚拟机中,monitor采用ObjectMonitor实现。
一般所说的对象的内置锁,是对象头Mark Word中的重量级锁指针指向的monitor对象,该对象是在HotSpot底层C++语言编写的(openjdk里面看),简单看一下代码:
//结构体以下 ObjectMonitor::ObjectMonitor() { _header = NULL; _count = 0; _waiters = 0, _recursions = 0; //线程的重入次数 _object = NULL; _owner = NULL; //标识拥有该monitor的线程 _WaitSet = NULL; //等待线程组成的双向循环链表,_WaitSet是第一个节点 _WaitSetLock = 0 ; _Responsible = NULL ; _succ = NULL ; _cxq = NULL ; //多线程竞争锁进入时的单向链表 FreeNext = NULL ; _EntryList = NULL ; //_owner从该双向循环链表中唤醒线程结点,_EntryList是第一个节点 _SpinFreq = 0 ; _SpinClock = 0 ; OwnerIsThread = 0 ; }
ObjectMonitor队列之间的关系转换能够用下图表示:
既然提到了_waitSet和_EntryList(_cxq队列后面会说),那就看一下底层的wait和notify方法
//1.调用ObjectSynchronizer::wait方法 void ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) { /*省略 */ //2.得到Object的monitor对象(即内置锁) ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj()); DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), THREAD, millis); //3.调用monitor的wait方法 monitor->wait(millis, true, THREAD); /*省略*/ } //4.在wait方法中调用addWaiter方法 inline void ObjectMonitor::AddWaiter(ObjectWaiter* node) { /*省略*/ if (_WaitSet == NULL) { //_WaitSet为null,就初始化_waitSet _WaitSet = node; node->_prev = node; node->_next = node; } else { //不然就尾插 ObjectWaiter* head = _WaitSet ; ObjectWaiter* tail = head->_prev; assert(tail->_next == head, "invariant check"); tail->_next = node; head->_prev = node; node->_next = head; node->_prev = tail; } } //5.而后在ObjectMonitor::exit释放锁,接着 thread_ParkEvent->park 也就是wait
总结:经过object得到内置锁(objectMonitor),经过内置锁将Thread封装成OjectWaiter对象,而后addWaiter将它插入以_waitSet为首结点的等待线程链表中去,最后释放锁。
//1.调用ObjectSynchronizer::notify方法 void ObjectSynchronizer::notify(Handle obj, TRAPS) { /*省略*/ //2.调用ObjectSynchronizer::inflate方法 ObjectSynchronizer::inflate(THREAD, obj())->notify(THREAD); } //3.经过inflate方法获得ObjectMonitor对象 ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) { /*省略*/ if (mark->has_monitor()) { ObjectMonitor * inf = mark->monitor() ; assert (inf->header()->is_neutral(), "invariant"); assert (inf->object() == object, "invariant") ; assert (ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is inva;lid"); return inf } /*省略*/ } //4.调用ObjectMonitor的notify方法 void ObjectMonitor::notify(TRAPS) { /*省略*/ //5.调用DequeueWaiter方法移出_waiterSet第一个结点 ObjectWaiter * iterator = DequeueWaiter() ; //6.后面省略是将上面DequeueWaiter尾插入_EntrySet的操做 /**省略*/ }
总结:经过object得到内置锁(objectMonitor),调用内置锁的notify方法,经过_waitset结点移出等待链表中的首结点,将它置于_EntrySet中去,等待获取锁。注意:notifyAll根据policy不一样可能移入_EntryList或者_cxq队列中,此处不详谈。