【多线程】深入浅出synchronized关键字

深入浅出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的执行操作已经基本完成,这个时候相关的原理我们也能明白了

简单画个图帮助理解
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值