Java锁概念内部如何工作?
“类Java锁概念内部如何工作?,java,multithreading,concurrency,Java,Multithreading,Concurrency,“类对象(及其子类)的每个实例都拥有一个锁,该锁在进入同步方法时获得,并在退出时自动释放” 这是否意味着我们创建的任何对象实例在默认情况下在内部都有一个“锁”(作为字段实现) 我对这个“锁”概念感到困惑,我想知道它实际上在内部做什么 有谁能告诉我一些可以找到更多信息的地方吗?一如既往: 这些方法中最基本的是同步,它是使用监视器实现的。Java中的每个对象都与一个监视器相关联,线程可以锁定或解锁该监视器。一次只有一个线程可以在监视器上持有锁。试图锁定该监视器的任何其他线程都将被阻止,直到它们可以在
对象
(及其子类)的每个实例都拥有一个锁,该锁在进入同步
方法时获得,并在退出时自动释放”
这是否意味着我们创建的任何对象实例在默认情况下在内部都有一个“锁”(作为字段实现)
我对这个“锁”概念感到困惑,我想知道它实际上在内部做什么
有谁能告诉我一些可以找到更多信息的地方吗?一如既往:
这些方法中最基本的是同步,它是使用监视器实现的。Java中的每个对象都与一个监视器相关联,线程可以锁定或解锁该监视器。一次只有一个线程可以在监视器上持有锁。试图锁定该监视器的任何其他线程都将被阻止,直到它们可以在该监视器上获得锁为止。线程t可以多次锁定特定监视器;每次解锁都会反转一次锁定操作的效果
因此,不,lock
不像对象中的字段(您只需查看即可看到)。相反,每个对象
都与一个“监视器”相关联,该监视器被锁定或解锁
我只是想指出一个进一步的参考,其中详细介绍了“Java是如何做到的”,以确保它不会被忽视。这是位于C++代码的注释中,下面是@ SELIG发现的,我鼓励下面的内容的所有投票来回答他的答案。您可以在此处提供的链接中查看完整的源代码
126 // -----------------------------------------------------------------------------
127 // Theory of operations -- Monitors lists, thread residency, etc:
128 //
129 // * A thread acquires ownership of a monitor by successfully
130 // CAS()ing the _owner field from null to non-null.
131 //
132 // * Invariant: A thread appears on at most one monitor list --
133 // cxq, EntryList or WaitSet -- at any one time.
134 //
135 // * Contending threads "push" themselves onto the cxq with CAS
136 // and then spin/park.
137 //
138 // * After a contending thread eventually acquires the lock it must
139 // dequeue itself from either the EntryList or the cxq.
140 //
141 // * The exiting thread identifies and unparks an "heir presumptive"
142 // tentative successor thread on the EntryList. Critically, the
143 // exiting thread doesn't unlink the successor thread from the EntryList.
144 // After having been unparked, the wakee will recontend for ownership of
145 // the monitor. The successor (wakee) will either acquire the lock or
146 // re-park itself.
147 //
148 // Succession is provided for by a policy of competitive handoff.
149 // The exiting thread does _not_ grant or pass ownership to the
150 // successor thread. (This is also referred to as "handoff" succession").
151 // Instead the exiting thread releases ownership and possibly wakes
152 // a successor, so the successor can (re)compete for ownership of the lock.
153 // If the EntryList is empty but the cxq is populated the exiting
154 // thread will drain the cxq into the EntryList. It does so by
155 // by detaching the cxq (installing null with CAS) and folding
156 // the threads from the cxq into the EntryList. The EntryList is
157 // doubly linked, while the cxq is singly linked because of the
158 // CAS-based "push" used to enqueue recently arrived threads (RATs).
159 //
160 // * Concurrency invariants:
161 //
162 // -- only the monitor owner may access or mutate the EntryList.
163 // The mutex property of the monitor itself protects the EntryList
164 // from concurrent interference.
165 // -- Only the monitor owner may detach the cxq.
166 //
167 // * The monitor entry list operations avoid locks, but strictly speaking
168 // they're not lock-free. Enter is lock-free, exit is not.
169 // See http://j2se.east/~dice/PERSIST/040825-LockFreeQueues.html
170 //
171 // * The cxq can have multiple concurrent "pushers" but only one concurrent
172 // detaching thread. This mechanism is immune from the ABA corruption.
173 // More precisely, the CAS-based "push" onto cxq is ABA-oblivious.
174 //
175 // * Taken together, the cxq and the EntryList constitute or form a
176 // single logical queue of threads stalled trying to acquire the lock.
177 // We use two distinct lists to improve the odds of a constant-time
178 // dequeue operation after acquisition (in the ::enter() epilog) and
179 // to reduce heat on the list ends. (c.f. Michael Scott's "2Q" algorithm).
180 // A key desideratum is to minimize queue & monitor metadata manipulation
181 // that occurs while holding the monitor lock -- that is, we want to
182 // minimize monitor lock holds times. Note that even a small amount of
183 // fixed spinning will greatly reduce the # of enqueue-dequeue operations
184 // on EntryList|cxq. That is, spinning relieves contention on the "inner"
185 // locks and monitor metadata.
186 //
187 // Cxq points to the the set of Recently Arrived Threads attempting entry.
188 // Because we push threads onto _cxq with CAS, the RATs must take the form of
189 // a singly-linked LIFO. We drain _cxq into EntryList at unlock-time when
190 // the unlocking thread notices that EntryList is null but _cxq is != null.
191 //
192 // The EntryList is ordered by the prevailing queue discipline and
193 // can be organized in any convenient fashion, such as a doubly-linked list or
194 // a circular doubly-linked list. Critically, we want insert and delete operations
195 // to operate in constant-time. If we need a priority queue then something akin
196 // to Solaris' sleepq would work nicely. Viz.,
197 // http://agg.eng/ws/on10_nightly/source/usr/src/uts/common/os/sleepq.c.
198 // Queue discipline is enforced at ::exit() time, when the unlocking thread
199 // drains the cxq into the EntryList, and orders or reorders the threads on the
200 // EntryList accordingly.
201 //
202 // Barring "lock barging", this mechanism provides fair cyclic ordering,
203 // somewhat similar to an elevator-scan.
204 //
205 // * The monitor synchronization subsystem avoids the use of native
206 // synchronization primitives except for the narrow platform-specific
207 // park-unpark abstraction. See the comments in os_solaris.cpp regarding
208 // the semantics of park-unpark. Put another way, this monitor implementation
209 // depends only on atomic operations and park-unpark. The monitor subsystem
210 // manages all RUNNING->BLOCKED and BLOCKED->READY transitions while the
211 // underlying OS manages the READY<->RUN transitions.
212 //
213 // * Waiting threads reside on the WaitSet list -- wait() puts
214 // the caller onto the WaitSet.
215 //
216 // * notify() or notifyAll() simply transfers threads from the WaitSet to
217 // either the EntryList or cxq. Subsequent exit() operations will
218 // unpark the notifyee. Unparking a notifee in notify() is inefficient -
219 // it's likely the notifyee would simply impale itself on the lock held
220 // by the notifier.
221 //
222 // * An interesting alternative is to encode cxq as (List,LockByte) where
223 // the LockByte is 0 iff the monitor is owned. _owner is simply an auxiliary
224 // variable, like _recursions, in the scheme. The threads or Events that form
225 // the list would have to be aligned in 256-byte addresses. A thread would
226 // try to acquire the lock or enqueue itself with CAS, but exiting threads
227 // could use a 1-0 protocol and simply STB to set the LockByte to 0.
228 // Note that is is *not* word-tearing, but it does presume that full-word
229 // CAS operations are coherent with intermix with STB operations. That's true
230 // on most common processors.
231 //
232 // * See also http://blogs.sun.com/dave
233
234
235 // -----------------------------------------------------------------------------
126//-----------------------------------------------------------------------------
127//操作理论--监视器列表、线程驻留等:
128 //
129//*线程通过以下方式成功获取监视器的所有权:
130//CAS()将_owner字段从null设置为非null。
131 //
132//*不变量:一个线程最多出现在一个监视器列表上--
133//cxq、EntryList或WaitSet——在任何时候。
134 //
135//*争用线程通过CAS将自己“推”到cxq上
136//然后旋转/停车。
137 //
138//*在争用线程最终获得锁后,它必须
139//从EntryList或cxq中退出队列。
140 //
141/*现有线程识别并取消解析“继承人推定”
142//EntryList上的暂定后续线程。关键是
143//退出线程不会从EntryList取消后续线程的链接。
144//被解除连接后,wakee将重新拥有
145//监视器。继任者(wakee)将获得锁或
146//重新停车。
147 //
148//竞争性交接政策规定了继任。
149//退出线程不会将所有权授予或传递给
150//后继线程。(这也称为“移交”继承)。
151//相反,退出的线程会释放所有权并可能唤醒
152//继承人,因此继承人可以(重新)争夺锁的所有权。
153//如果EntryList为空,但cxq已填充,则退出
154//线程将把cxq排入EntryList
155//通过分离cxq(使用CAS安装null)和折叠
156//从cxq到EntryList的线程。EntryList是
157//双链接,而cxq是单链接的,因为
158//基于CAS的“推送”用于将最近到达的线程(RAT)排队。
159 //
160//*并发不变量:
161 //
162/--只有监视器所有者可以访问或修改EntryList。
163//监视器本身的互斥体属性保护EntryList
164//避免并发干扰。
165/--只有监视器所有者可以拆下cxq。
166 //
167/*监视器条目列表操作避免锁定,但严格来说
168//它们不是无锁的。进入是无锁的,退出不是。
169//见http://j2se.east/~dice/PERSIST/040825-LockFreeQueues.html
170 //
171//*cxq可以有多个并发“推送器”,但只能有一个并发“推送器”
172//正在分离线程。此机制不受ABA损坏的影响。
173//更准确地说,基于CAS的对cxq的“推送”是ABA遗忘的。
174 //
175//*综上所述,cxq和EntryList构成或构成
176//试图获取锁时暂停的线程的单个逻辑队列。
177//我们使用两个不同的列表来提高恒定时间的概率
178//采集后的出列操作(在::enter()epilog中)和
179//减少列表中的热量(c.f.Michael Scott的“2Q”算法)。
180//一个关键要求是最小化队列和监视器元数据操作
181//在保持监视器锁时发生的情况——也就是说,我们希望
182//最小化监视器锁定保持时间。请注意,即使是少量的
183//固定旋转将大大减少排队-出列操作的次数
184//在EntryList | cxq上。也就是说,旋转减轻了“内部”的争用
185//锁定和监视元数据。
186 //
187//Cxq指向最近到达的一组试图进入的线程。
188//因为我们用CAS将线程推到cxq上,所以老鼠必须以
189//一个单独链接的后进先出。当
190//解锁线程注意到EntryList为null,但_cxq为!=null。
191 //
192//EntryList按当前的队列规程排序,并且
193//可以以任何方便的方式组织,例如双链接列表或
194//循环双链表。关键是,我们需要插入和删除操作
195//在固定时间内运行。如果我们需要一个优先级队列,那么
196//Solaris的sleepq将很好地工作。即。,
197 // http://agg.eng/ws/on10_nightly/source/usr/src/uts/common/os/sleepq.c.