在lab6中,我们需要使用java的同步机制,主要是synchronized方法和synchronized代码块。为了更好地理解这里的原理,我查询了一些关于synchronized的实现机制。
在Java中,对象被创建在堆中,其中的结构不止包含对象的数据,还包括对象头和对齐。
其中对象头中就存储着关于对象锁的信息,存储着一个指向“锁”的指针。这个指针指向一个monitor对象,Java中,每个对象实例都会有一个monitor,它可以与对象一起创建、销毁;亦或者当线程试图获取对象锁时自动生成。
monitor中大致有这样几个域:
owner
EntryList
WaitSet
ContentionList
OnDeck
每个monitor同时只能有一个owner,这就是获得锁的当前线程。
当它(Owner)调用unlock之后,如果发现 EntryList为空,则从ContentionList中移动线程到EntryList,EntryList与ContentionList实际上都是等待队列,只不过为了提高效率,增加吞吐率才使用了这样的机制。Owner线程在unlock时会从ContentionList中迁移线程到EntryList,并会指定EntryList中的某个线程(一般为Head)为Ready(OnDeck)线程。Owner线程并不是把锁传递给OnDeck线程,只是把竞争锁的权利交给OnDeck,OnDeck线程需要重新竞争锁。这样做虽然牺牲了一定的公平性,但极大的提高了整体吞吐量。OnDeck线程获得锁后即变为owner线程,无法获得锁则会依然留在EntryList中,而且为队头(公平性)。
如果owner线程调用了wait而被阻塞,它就会被转移到WaitSet队列;直到被notify/notifyAll唤醒,再次转移到EntryList。
java的线程机制是依赖系统线程机制的。这就是说,如果要切换线程,就会进行系统调用,我们知道这会消耗大量的系统资源,因为这使得CPU在用户态和内核态来回切换。
如果对于那些需要同步的简单的代码块,获取锁挂起操作消耗的时间比用户代码执行的时间还要长。
synchronized会导致争用不到锁的线程进入阻塞状态,所以说它是java语言中一个重量级的同步操纵,被称为重量级锁。
有参考:
https://www.cnblogs.com/lykm02/p/4516777.html