深入浅出synchronized关键字
对于synchronized,大家也并不陌生了,
synchronized的使用
先写个例子
package UseSync;
public class SyncDemo implements Runnable {
static int wangzy = 0;
public static void main(String[] args) {
// 用两个线程去执行
new Thread(new SyncDemo(), "t1").start();
new Thread(new SyncDemo(), "t2").start();
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 5; i++) {
wangzy++;
System.out.println(Thread.currentThread().getName() +" "+wangzy);
try {
Thread.currentThread().sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
我们想让两个线程,t1先执行,执行完成之后再执行t2线程,但是执行结果打印如下。
t1 1
t2 2
t2 3
t1 4
t2 5
t1 6
t2 7
t1 8
t2 9
t1 10
这样的话线程的执行顺序得不到保证,那么最终的结果,对我们来说也是不确定的。
我们尝试使用synchronized代码块测试一下能不能实现我们的需求
package UseSync;
public class SyncDemo implements Runnable {
static int wangzy = 0;
Object lock = new Object();
public static void main(String[] args) {
// 用两个线程去执行
new Thread(new SyncDemo(), "t1").start();
new Thread(new SyncDemo(), "t2").start();
}
@Override
public void run() {
synchronized (lock) {
// TODO Auto-generated method stub
for (int i = 0; i < 5; i++) {
wangzy++;
System.out.println(Thread.currentThread().getName() + " " + wangzy);
try {
Thread.currentThread().sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
结果如下:
t1 1
t2 2
t2 3
t1 4
t2 5
t1 6
t2 7
t1 8
t1 10
t2 10
我勒个去,我们不是已经使用synchronized关键字了吗,为什么还是不能同步执行?
这时候我们发现在main方法中,我们使用new SyncDemo()创建了一个对象,对于这两个线程来看的话,两个对象,不存在同步竞争,自然我们的需求不能实现了。那么我们要怎样去修改呢?
我们简单修改一下:
package UseSync;
public class SyncDemo implements Runnable {
static int wangzy = 0;
Object lock = new Object();
public SyncDemo(Object obj) {
lock = obj;
}
public static void main(String[] args) {
// 用两个线程去执行
Object obj = new Object();
new Thread(new SyncDemo(obj), "t1").start();
new Thread(new SyncDemo(obj), "t2").start();
}
@Override
public void run() {
synchronized (lock) {
// TODO Auto-generated method stub
for (int i = 0; i < 5; i++) {
wangzy++;
System.out.println(Thread.currentThread().getName() + " " + wangzy);
try {
Thread.currentThread().sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
我们把同一对象传到SyncDemo类中,也就是这两个线程的Lock是一个对象,那么我们的锁一定是加成功的,执行结果如下,是符合我们预期的
t1 1
t1 2
t1 3
t1 4
t1 5
t2 6
t2 7
t2 8
t2 9
t2 10
我们也可以 synchronized (SyncDemo.class)
这样的加锁方式来实现对线程的同步控制,效果与第二种一致
关于synchronized的用法不做过多阐述,大家可以百度一下。
下面我们使用javap 来看一下对应的汇编代码
javap -verbose D:\eclipse2018-workspace\MultiThreadPro\bin\UseSync\SyncDemo.class
指令如下
Classfile /D:/eclipse2018-workspace/MultiThreadPro/bin/UseSync/SyncDemo.class
Last modified 2018-8-19; size 1733 bytes
MD5 checksum c983dc820edd155cb2204fc316f5b600
Compiled from "SyncDemo.java"
public class UseSync.SyncDemo implements java.lang.Runnable
SourceFile: "SyncDemo.java"
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class #2 // UseSync/SyncDemo
#2 = Utf8 UseSync/SyncDemo
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Class #6 // java/lang/Runnable
#6 = Utf8 java/lang/Runnable
#7 = Utf8 wangzy
#8 = Utf8 I
#9 = Utf8 lock
#10 = Utf8 Ljava/lang/Object;
#11 = Utf8 <clinit>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Fieldref #1.#15 // UseSync/SyncDemo.wangzy:I
#15 = NameAndType #7:#8 // wangzy:I
#16 = Utf8 LineNumberTable
#17 = Utf8 LocalVariableTable
#18 = Utf8 <init>
#19 = Utf8 (Ljava/lang/Object;)V
#20 = Methodref #3.#21 // java/lang/Object."<init>":()V
#21 = NameAndType #18:#12 // "<init>":()V
#22 = Fieldref #1.#23 // UseSync/SyncDemo.lock:Ljava/lang/Object;
#23 = NameAndType #9:#10 // lock:Ljava/lang/Object;
#24 = Utf8 this
#25 = Utf8 LUseSync/SyncDemo;
#26 = Utf8 obj
#27 = Utf8 main
#28 = Utf8 ([Ljava/lang/String;)V
#29 = Class #30 // java/lang/Thread
#30 = Utf8 java/lang/Thread
#31 = Methodref #1.#32 // UseSync/SyncDemo."<init>":(Ljava/lang/Object;)V
#32 = NameAndType #18:#19 // "<init>":(Ljava/lang/Object;)V
#33 = String #34 // t1
#34 = Utf8 t1
#35 = Methodref #29.#36 // java/lang/Thread."<init>":(Ljava/lang/Runnable;Ljava/lang/String;)V
#36 = NameAndType #18:#37 // "<init>":(Ljava/lang/Runnable;Ljava/lang/String;)V
#37 = Utf8 (Ljava/lang/Runnable;Ljava/lang/String;)V
#38 = Methodref #29.#39 // java/lang/Thread.start:()V
#39 = NameAndType #40:#12 // start:()V
#40 = Utf8 start
#41 = String #42 // t2
#42 = Utf8 t2
#43 = Utf8 args
#44 = Utf8 [Ljava/lang/String;
#45 = Utf8 run
#46 = Fieldref #47.#49 // java/lang/System.out:Ljava/io/PrintStream;
#47 = Class #48 // java/lang/System
#48 = Utf8 java/lang/System
#49 = NameAndType #50:#51 // out:Ljava/io/PrintStream;
#50 = Utf8 out
#51 = Utf8 Ljava/io/PrintStream;
#52 = Class #53 // java/lang/StringBuilder
#53 = Utf8 java/lang/StringBuilder
#54 = Methodref #29.#55 // java/lang/Thread.currentThread:()Ljava/lang/Thread;
#55 = NameAndType #56:#57 // currentThread:()Ljava/lang/Thread;
#56 = Utf8 currentThread
#57 = Utf8 ()Ljava/lang/Thread;
#58 = Methodref #29.#59 // java/lang/Thread.getName:()Ljava/lang/String;
#59 = NameAndType #60:#61 // getName:()Ljava/lang/String;
#60 = Utf8 getName
#61 = Utf8 ()Ljava/lang/String;
#62 = Methodref #63.#65 // java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
#63 = Class #64 // java/lang/String
#64 = Utf8 java/lang/String
#65 = NameAndType #66:#67 // valueOf:(Ljava/lang/Object;)Ljava/lang/String;
#66 = Utf8 valueOf
#67 = Utf8 (Ljava/lang/Object;)Ljava/lang/String;
#68 = Methodref #52.#69 // java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
#69 = NameAndType #18:#70 // "<init>":(Ljava/lang/String;)V
#70 = Utf8 (Ljava/lang/String;)V
#71 = String #72 //
#72 = Utf8
#73 = Methodref #52.#74 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#74 = NameAndType #75:#76 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#75 = Utf8 append
#76 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#77 = Methodref #52.#78 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#78 = NameAndType #75:#79 // append:(I)Ljava/lang/StringBuilder;
#79 = Utf8 (I)Ljava/lang/StringBuilder;
#80 = Methodref #52.#81 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#81 = NameAndType #82:#61 // toString:()Ljava/lang/String;
#82 = Utf8 toString
#83 = Methodref #84.#86 // java/io/PrintStream.println:(Ljava/lang/String;)V
#84 = Class #85 // java/io/PrintStream
#85 = Utf8 java/io/PrintStream
#86 = NameAndType #87:#70 // println:(Ljava/lang/String;)V
#87 = Utf8 println
#88 = Methodref #29.#89 // java/lang/Thread.sleep:(J)V
#89 = NameAndType #90:#91 // sleep:(J)V
#90 = Utf8 sleep
#91 = Utf8 (J)V
#92 = Methodref #93.#95 // java/lang/InterruptedException.printStackTrace:()V
#93 = Class #94 // java/lang/InterruptedException
#94 = Utf8 java/lang/InterruptedException
#95 = NameAndType #96:#12 // printStackTrace:()V
#96 = Utf8 printStackTrace
#97 = Utf8 i
#98 = Utf8 e
#99 = Utf8 Ljava/lang/InterruptedException;
#100 = Utf8 StackMapTable
#101 = Class #102 // java/lang/Throwable
#102 = Utf8 java/lang/Throwable
#103 = Utf8 SourceFile
#104 = Utf8 SyncDemo.java
{
static int wangzy;
flags: ACC_STATIC
java.lang.Object lock;
flags:
static {};
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: iconst_0
1: putstatic #14 // Field wangzy:I
4: return
LineNumberTable:
line 4: 0
LocalVariableTable:
Start Length Slot Name Signature
public UseSync.SyncDemo(java.lang.Object);
flags: ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
0: aload_0
1: invokespecial #20 // Method java/lang/Object."<init>":()V
4: aload_0
5: new #3 // class java/lang/Object
8: dup
9: invokespecial #20 // Method java/lang/Object."<init>":()V
12: putfield #22 // Field lock:Ljava/lang/Object;
15: aload_0
16: aload_1
17: putfield #22 // Field lock:Ljava/lang/Object;
20: return
LineNumberTable:
line 7: 0
line 5: 4
line 8: 15
line 9: 20
LocalVariableTable:
Start Length Slot Name Signature
0 21 0 this LUseSync/SyncDemo;
0 21 1 obj Ljava/lang/Object;
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=5, locals=2, args_size=1
0: new #3 // class java/lang/Object
3: dup
4: invokespecial #20 // Method java/lang/Object."<init>":()V
7: astore_1
8: new #29 // class java/lang/Thread
11: dup
12: new #1 // class UseSync/SyncDemo
15: dup
16: aload_1
17: invokespecial #31 // Method "<init>":(Ljava/lang/Object;)V
20: ldc #33 // String t1
22: invokespecial #35 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;Ljava/lang/String;)V
25: invokevirtual #38 // Method java/lang/Thread.start:()V
28: new #29 // class java/lang/Thread
31: dup
32: new #1 // class UseSync/SyncDemo
35: dup
36: aload_1
37: invokespecial #31 // Method "<init>":(Ljava/lang/Object;)V
40: ldc #41 // String t2
42: invokespecial #35 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;Ljava/lang/String;)V
45: invokevirtual #38 // Method java/lang/Thread.start:()V
48: return
LineNumberTable:
line 13: 0
line 14: 8
line 15: 28
line 16: 48
LocalVariableTable:
Start Length Slot Name Signature
0 49 0 args [Ljava/lang/String;
8 41 1 obj Ljava/lang/Object;
public void run();
flags: ACC_PUBLIC
Code:
stack=4, locals=4, args_size=1
0: aload_0
1: getfield #22 // Field lock:Ljava/lang/Object;
4: dup
5: astore_1
6: monitorenter
7: iconst_0
8: istore_2
9: goto 75
12: getstatic #14 // Field wangzy:I
15: iconst_1
16: iadd
17: putstatic #14 // Field wangzy:I
20: getstatic #46 // Field java/lang/System.out:Ljava/io/PrintStream;
23: new #52 // class java/lang/StringBuilder
26: dup
27: invokestatic #54 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
30: invokevirtual #58 // Method java/lang/Thread.getName:()Ljava/lang/String;
33: invokestatic #62 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
36: invokespecial #68 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
39: ldc #71 // String
41: invokevirtual #73 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
44: getstatic #14 // Field wangzy:I
47: invokevirtual #77 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
50: invokevirtual #80 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
53: invokevirtual #83 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
56: invokestatic #54 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
59: pop
60: lconst_1
61: invokestatic #88 // Method java/lang/Thread.sleep:(J)V
64: goto 72
67: astore_3
68: aload_3
69: invokevirtual #92 // Method java/lang/InterruptedException.printStackTrace:()V
72: iinc 2, 1
75: iload_2
76: iconst_5
77: if_icmplt 12
80: aload_1
81: monitorexit
82: goto 88
85: aload_1
86: monitorexit
87: athrow
88: return
Exception table:
from to target type
56 64 67 Class java/lang/InterruptedException
7 82 85 any
85 87 85 any
LineNumberTable:
line 20: 0
line 23: 7
line 24: 12
line 25: 20
line 27: 56
line 28: 64
line 30: 68
line 23: 72
line 20: 80
line 34: 88
LocalVariableTable:
Start Length Slot Name Signature
0 89 0 this LUseSync/SyncDemo;
9 71 2 i I
68 4 3 e Ljava/lang/InterruptedException;
StackMapTable: number_of_entries = 6
frame_type = 253 /* append */
offset_delta = 12
locals = [ class java/lang/Object, int ]
frame_type = 118 /* same_locals_1_stack_item */
stack = [ class java/lang/InterruptedException ]
frame_type = 4 /* same */
frame_type = 2 /* same */
frame_type = 255 /* full_frame */
offset_delta = 9
locals = [ class UseSync/SyncDemo, class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 2
}
我们发现比不带synchronized关键字时多了monitorenter和monitorexit这两个指令
我们可以去JRE里面去找对应的代码支持
http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/interpreter/interpreterRuntime.cpp
//------------------------------------------------------------------------------------------------------------------------
// Synchronization
//
// The interpreter's synchronization code is factored out so that it can
// be shared by method invocation and synchronized blocks.
//%note synchronization_3
static void trace_locking(Handle& h_locking_obj, bool is_locking) {
ObjectSynchronizer::trace_locking(h_locking_obj, false, true, is_locking);
}
//%note monitor_1
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
#ifdef ASSERT
thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
if (PrintBiasedLockingStatistics) {
Atomic::inc(BiasedLocking::slow_path_entry_count_addr());
}
Handle h_obj(thread, elem->obj());
assert(Universe::heap()->is_in_reserved_or_null(h_obj()),
"must be NULL or an object");
if (UseBiasedLocking) {
// Retry fast entry if bias is revoked to avoid unnecessary inflation
ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
} else {
ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
}
assert(Universe::heap()->is_in_reserved_or_null(elem->obj()),
"must be NULL or an object");
#ifdef ASSERT
thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
IRT_END
UseBiasedLocking指的是是否使用偏向锁,如果是偏向锁的话走fast_enter否则走slow_enter方法。
同样我们可以去看一下fast_enter和slow_enter的实现
http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/runtime/synchronizer.cpp
Fast Monitor Enter/Exit
// This the fast monitor enter. The interpreter and compiler use
// some assembly copies of this code. Make sure update those code
// if the following function is changed. The implementation is
// extremely sensitive to race condition. Be careful.
void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
if (UseBiasedLocking) {
if (!SafepointSynchronize::is_at_safepoint()) {
BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
return;
}
} else {
assert(!attempt_rebias, "can not rebias toward VM thread");
BiasedLocking::revoke_at_safepoint(obj);
}
assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
}
slow_enter (obj, lock, THREAD) ;
}
再次检查是否使用偏向锁
slow_enter 源码:
// -----------------------------------------------------------------------------
// Interpreter/Compiler Slow Case
// This routine is used to handle interpreter/compiler slow case
// We don't need to use fast path here, because it must have been
// failed in the interpreter/compiler code.
void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
markOop mark = obj->mark();
assert(!mark->has_bias_pattern(), "should not see bias pattern here");
if (mark->is_neutral()) {
// Anticipate successful CAS -- the ST of the displaced mark must
// be visible <= the ST performed by the CAS.
lock->set_displaced_header(mark);
if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
TEVENT (slow_enter: release stacklock) ;
return ;
}
// Fall through to inflate() ...
} else
if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
assert(lock != mark->locker(), "must not re-lock the same lock");
assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock");
lock->set_displaced_header(NULL);
return;
}
#if 0
// The following optimization isn't particularly useful.
if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) {
lock->set_displaced_header (NULL) ;
return ;
}
#endif
最终指引我们到objectMonitor.hpp
http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/runtime/objectMonitor.hpp
我们看一下对应的构造
// initialize the monitor, exception the semaphore, all other fields
// are simple integers or pointers
ObjectMonitor() {
_header = NULL;
_count = 0;
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL;
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ;
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
_owner 表示现在拥有锁的对象
_EntryList 表示现在排队获取锁的队列
// ObjectWaiter serves as a "proxy" or surrogate thread.
// TODO-FIXME: Eliminate ObjectWaiter and use the thread-specific
// ParkEvent instead. Beware, however, that the JVMTI code
// knows about ObjectWaiters, so we'll have to reconcile that code.
// See next_waiter(), first_waiter(), etc.
class ObjectWaiter : public StackObj {
public:
enum TStates { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER, TS_CXQ } ;
enum Sorted { PREPEND, APPEND, SORTED } ;
ObjectWaiter * volatile _next;
ObjectWaiter * volatile _prev;
Thread* _thread;
ParkEvent * _event;
volatile int _notified ;
volatile TStates TState ;
Sorted _Sorted ; // List placement disposition
bool _active ; // Contention monitoring is enabled
public:
ObjectWaiter(Thread* thread);
void wait_reenter_begin(ObjectMonitor *mon);
void wait_reenter_end(ObjectMonitor *mon);
};
ObjectWaiter 会把Thread封装成一个ObjectWaiter 对象
我们继续查看ObjectMonitor的enter方法
void ATTR ObjectMonitor::enter(TRAPS) {
// The following code is ordered to check the most common cases first
// and to reduce RTS->RTO cache line upgrades on SPARC and IA32 processors.
Thread * const Self = THREAD ;
void * cur ;
cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
if (cur == NULL) {
// Either ASSERT _recursions == 0 or explicitly set _recursions = 0.
assert (_recursions == 0 , "invariant") ;
assert (_owner == Self, "invariant") ;
// CONSIDER: set or assert OwnerIsThread == 1
return ;
}
if (cur == Self) {
// TODO-FIXME: check for integer overflow! BUGID 6557169.
_recursions ++ ;
return ;
}
if (Self->is_lock_owned ((address)cur)) {
assert (_recursions == 0, "internal state error");
_recursions = 1 ;
// Commute owner from a thread-specific on-stack BasicLockObject address to
// a full-fledged "Thread *".
_owner = Self ;
OwnerIsThread = 1 ;
return ;
}
// We've encountered genuine contention.
assert (Self->_Stalled == 0, "invariant") ;
Self->_Stalled = intptr_t(this) ;
// Try one round of spinning *before* enqueueing Self
// and before going through the awkward and expensive state
// transitions. The following spin is strictly optional ...
// Note that if we acquire the monitor from an initial spin
// we forgo posting JVMTI events and firing DTRACE probes.
if (Knob_SpinEarly && TrySpin (Self) > 0) {
assert (_owner == Self , "invariant") ;
assert (_recursions == 0 , "invariant") ;
assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ;
Self->_Stalled = 0 ;
return ;
}
assert (_owner != Self , "invariant") ;
assert (_succ != Self , "invariant") ;
assert (Self->is_Java_thread() , "invariant") ;
JavaThread * jt = (JavaThread *) Self ;
assert (!SafepointSynchronize::is_at_safepoint(), "invariant") ;
assert (jt->thread_state() != _thread_blocked , "invariant") ;
assert (this->object() != NULL , "invariant") ;
assert (_count >= 0, "invariant") ;
// Prevent deflation at STW-time. See deflate_idle_monitors() and is_busy().
// Ensure the object-monitor relationship remains stable while there's contention.
Atomic::inc_ptr(&_count);
{ // Change java thread status to indicate blocked on monitor enter.
JavaThreadBlockedOnMonitorEnterState jtbmes(jt, this);
DTRACE_MONITOR_PROBE(contended__enter, this, object(), jt);
if (JvmtiExport::should_post_monitor_contended_enter()) {
JvmtiExport::post_monitor_contended_enter(jt, this);
}
OSThreadContendState osts(Self->osthread());
ThreadBlockInVM tbivm(jt);
Self->set_current_pending_monitor(this);
// TODO-FIXME: change the following for(;;) loop to straight-line code.
for (;;) {
jt->set_suspend_equivalent();
// cleared by handle_special_suspend_equivalent_condition()
// or java_suspend_self()
EnterI (THREAD) ;
if (!ExitSuspendEquivalent(jt)) break ;
//
// We have acquired the contended monitor, but while we were
// waiting another thread suspended us. We don't want to enter
// the monitor while suspended because that would surprise the
// thread that suspended us.
//
_recursions = 0 ;
_succ = NULL ;
exit (Self) ;
jt->java_suspend_self();
}
Self->set_current_pending_monitor(NULL);
}
Atomic::dec_ptr(&_count);
assert (_count >= 0, "invariant") ;
Self->_Stalled = 0 ;
// Must either set _recursions = 0 or ASSERT _recursions == 0.
assert (_recursions == 0 , "invariant") ;
assert (_owner == Self , "invariant") ;
assert (_succ != Self , "invariant") ;
assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ;
// The thread -- now the owner -- is back in vm mode.
// Report the glorious news via TI,DTrace and jvmstat.
// The probe effect is non-trivial. All the reportage occurs
// while we hold the monitor, increasing the length of the critical
// section. Amdahl's parallel speedup law comes vividly into play.
//
// Another option might be to aggregate the events (thread local or
// per-monitor aggregation) and defer reporting until a more opportune
// time -- such as next time some thread encounters contention but has
// yet to acquire the lock. While spinning that thread could
// spinning we could increment JVMStat counters, etc.
DTRACE_MONITOR_PROBE(contended__entered, this, object(), jt);
if (JvmtiExport::should_post_monitor_contended_entered()) {
JvmtiExport::post_monitor_contended_entered(jt, this);
}
if (ObjectMonitor::_sync_ContendedLockAttempts != NULL) {
ObjectMonitor::_sync_ContendedLockAttempts->inc() ;
}
}
cmpxchg_ptr (Self, &_owner, NULL)这个操作就是我们平时说的CAS操作(compare and swap)就是要把owner设置为当前的self线程
如果cur == NULL的话就说明获取锁失败了;
如果cur == self的话就说明是重入锁,不需要再次获得锁;
如果是第一次获取锁,那么_owner设置为self;
如果没有获得锁那么我们做自旋操作,不断尝试去获取锁;
for (;;) {
jt->set_suspend_equivalent();
// cleared by handle_special_suspend_equivalent_condition()
// or java_suspend_self()
EnterI (THREAD) ;
if (!ExitSuspendEquivalent(jt)) break ;
//
// We have acquired the contended monitor, but while we were
// waiting another thread suspended us. We don't want to enter
// the monitor while suspended because that would surprise the
// thread that suspended us.
//
_recursions = 0 ;
_succ = NULL ;
exit (Self) ;
jt->java_suspend_self();
}
我们进入一下 EnterI (THREAD)这个方法
void ATTR ObjectMonitor::EnterI (TRAPS) {
Thread * Self = THREAD ;
assert (Self->is_Java_thread(), "invariant") ;
assert (((JavaThread *) Self)->thread_state() == _thread_blocked , "invariant") ;
// Try the lock - TATAS
if (TryLock (Self) > 0) {
assert (_succ != Self , "invariant") ;
assert (_owner == Self , "invariant") ;
assert (_Responsible != Self , "invariant") ;
return ;
}
DeferredInitialize () ;
// We try one round of spinning *before* enqueueing Self.
//
// If the _owner is ready but OFFPROC we could use a YieldTo()
// operation to donate the remainder of this thread's quantum
// to the owner. This has subtle but beneficial affinity
// effects.
if (TrySpin (Self) > 0) {
assert (_owner == Self , "invariant") ;
assert (_succ != Self , "invariant") ;
assert (_Responsible != Self , "invariant") ;
return ;
}
// The Spin failed -- Enqueue and park the thread ...
assert (_succ != Self , "invariant") ;
assert (_owner != Self , "invariant") ;
assert (_Responsible != Self , "invariant") ;
// Enqueue "Self" on ObjectMonitor's _cxq.
//
// Node acts as a proxy for Self.
// As an aside, if were to ever rewrite the synchronization code mostly
// in Java, WaitNodes, ObjectMonitors, and Events would become 1st-class
// Java objects. This would avoid awkward lifecycle and liveness issues,
// as well as eliminate a subset of ABA issues.
// TODO: eliminate ObjectWaiter and enqueue either Threads or Events.
//
ObjectWaiter node(Self) ;
Self->_ParkEvent->reset() ;
node._prev = (ObjectWaiter *) 0xBAD ;
node.TState = ObjectWaiter::TS_CXQ ;
// Push "Self" onto the front of the _cxq.
// Once on cxq/EntryList, Self stays on-queue until it acquires the lock.
// Note that spinning tends to reduce the rate at which threads
// enqueue and dequeue on EntryList|cxq.
ObjectWaiter * nxt ;
for (;;) {
node._next = nxt = _cxq ;
if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;
// Interference - the CAS failed because _cxq changed. Just retry.
// As an optional optimization we retry the lock.
if (TryLock (Self) > 0) {
assert (_succ != Self , "invariant") ;
assert (_owner == Self , "invariant") ;
assert (_Responsible != Self , "invariant") ;
return ;
}
}
我们可以看到在不断尝试获取锁后我们把Self封装称为一个ObjectWaiter 对象叫node,设置_cxq
_cxq是一个单向的链表,是存放多个线程竞争时的单向链表
然后不断通过自旋去获取锁,如果一直没有获得锁的话 就把当前线程挂起等待被唤醒,如果后续唤醒后,继续尝试获取锁
TryLock 也是一个原子的CAS操作设置Owener为Self,如果成功,则表示锁成功,失败则表示锁失败。源码解释
// Caveat: TryLock() is not necessarily serializing if it returns failure.
// Callers must compensate as needed.
int ObjectMonitor::TryLock (Thread * Self) {
for (;;) {
void * own = _owner ;
if (own != NULL) return 0 ;
if (Atomic::cmpxchg_ptr (Self, &_owner, NULL) == NULL) {
// Either guarantee _recursions == 0 or set _recursions = 0.
assert (_recursions == 0, "invariant") ;
assert (_owner == Self, "invariant") ;
// CONSIDER: set or assert that OwnerIsThread == 1
return 1 ;
}
// The lock had been free momentarily, but we lost the race to the lock.
// Interference -- the CAS failed.
// We can either return -1 or retry.
// Retry doesn't make as much sense because the lock was just acquired.
if (true) return -1 ;
}
}
设置为1就是成功,-1就是失败
所以 if (TryLock (Self) > 0)
才能够表示能够获取锁
这样synchronized的执行操作已经基本完成,这个时候相关的原理我们也能明白了
简单画个图帮助理解