并发编程7

锁膨胀过程3

偏向锁流程图总结

否--批量撤销的时候会关闭偏向模式
成功--success是false
失败--success是false
过期了
成功
失败
没有过期
是匿名偏向
获取锁对象lockee
创建一个lock record在栈当中
让锁记录的obj关联lockee
获取lockee对象头mark的信息
判断是否关闭偏向
偏向撤销 并升级成轻量锁
获取当前线程的id--thread_ident
线程id和mark计算
是否偏向自己--t1第二次调用加锁--第一次是匿名偏向
拿到锁直接执行
是否关闭了偏向模式
获取类中的header--注意不是对象的header
CAS替换当前对象为不可偏向状态--肯定进入轻量锁加锁
是否过期
创建一个新的偏向自己的mark重新偏向
cas设置对象头
是匿名偏向还是偏向别人
创建一个偏向自己的mark

场景1—第二次偏向加锁

  • 两个挨着的synchronized代码块

  • 第二次偏向加锁,在栈当中创建一个lock record,判断对象头里面的内容(t1id + epoch +age+101),跟自己锁记录里面的对象头内容(obj ref)是否相同,比较的是位运算计算出来的一个值

  • anticipated_bias_locking_value =
                  (((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
                  ~((uintptr_t) markOopDesc::age_mask_in_place);
    
  • 如果这个值等于0,说明是第一次加偏向锁的线程,则直接获取偏向锁

场景2----轻量锁加锁

  • t3线程来了,首先创建一个锁记录,首先关联对象头,然后在cpu里面产生一个001的对象头,如果和对象头的mark word相同,把mark word改成lock record的地址指针

场景3—轻量锁膨胀为重量锁

  • t4线程来了,在t3线程判断的时候,把对象头里面的后两位改成00了,那么t3线程就会cas失败,进入if逻辑体,进入重量锁升级的逻辑

场景4—匿名偏向锁(第一次加锁)

  • 匿名偏向,首先产生一个无锁的对象头,对象头里面匿名101,代码执行到匿名偏向锁逻辑时,如果是匿名偏向,首先产生一个无锁的new_header,偏向于当前线程的对象头,然后对当前对象的对象头进行cas,也就是判断当前对象有没有被别人改变,如果没有则偏向该线程

场景5—批量重偏向

  • 批量重偏向,如果对同一个类对象进行了偏向锁批量撤销超过20次后,会进入源代码中偏向锁if语句中带有epoch关键字的判断语句,其中epoch会记录偏向锁撤销次数,如果正好是第20次偏向撤销,知道偏向锁过期了,会进行重偏向,没有体现批量重偏向?
  • 怎么体现的批量重偏向?
    • 现在持有这一类对象的锁的所有线程如果偏向t1,当对这个类进行超过20次偏向撤销后,之后再对该类进行加锁时,则直接偏向执行加锁代码的线程。
  • 批量重偏向的是正在持有锁的对象

场景6—偏向撤销

  • 当一个偏向锁如果撤销次数到达40的时候就认为这个对象的设计有问题;那么JVM会把这个对象所对应的类所有的对象都撤销偏向锁;并且新实例化的对象也是不可偏向的

流程图过程(bytecodeinterpreter.cpp)

进入到CASE(monitorenter)

1.获取锁对象

2.创建一个锁记录

3.让锁记录关联lockee

4.获取lockee对象头的信息

5.判断是否关闭偏向

if错误 直接升级成轻量锁

  • 走轻量锁逻辑

if正确 开启偏向

  • 1.线程id和mark计算判断是否是偏向自己

    if 正确 获取到锁

    if错误 偏向是否禁用了

    • 1.获取做cas操作,把对象头设置为header,不管cas是否成功,依然会走到轻量锁逻辑
  • 2.判断是否过期

    if 正确 创建一个偏向自己的mark,cas将markword中保存所记录中的指针

    if错误 会进入slow_enter

  • 3.如果不满足,进入最后一个else

    • 1创建一个偏向自己的对象头
    • 2.如果cas成功,说明是匿名偏向
    • 3.如果cas失败,则说明是偏向别人,去做锁撤销,然后升级为轻量锁

偏向锁源码未讲的一个逻辑分支

  • if  (anticipated_bias_locking_value == 0) {
        // already biased towards this thread, nothing to do
        if (PrintBiasedLockingStatistics) {
            (* BiasedLocking::biased_lock_entry_count_addr())++;
        }
        success = true;
    }
    // 偏向被禁用了
    else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
        // try revoke bias
        // 撤销偏向,注意此处是类当中的mark,而不是对象当中的mark,而类当中的mark是不可偏向的,所以直接就是变成不可偏向的了
        markOop header = lockee->klass()->prototype_header();
        if (hash != markOopDesc::no_hash) {
            header = header->copy_set_hash(hash);
        }
        if (lockee->cas_set_mark(header, mark) == mark) {
            if (PrintBiasedLockingStatistics)
                (*BiasedLocking::revoked_lock_entry_count_addr())++;
        }
    }
    

ObjectMoniter是synchronized中的重量锁,与ReentrantLock类似

上一步流程图失败的都会进入轻量锁逻辑

  • fast_enter

  • 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

  • void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
      markOop mark = obj->mark();
      assert(!mark->has_bias_pattern(), "should not see bias pattern here");
    
        // 首先判断是不是无锁---前一步如果禁用偏向锁模式得到的是001的对象头
      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);
          // 进行cas判断
          // 跟之前if(!success)中执行的轻量锁加锁逻辑相同
        if (mark == obj()->cas_set_mark((markOop) lock, mark)) {
            // 轻量锁加锁成功,则return
          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");
            // 设置一个null的对象头
        lock->set_displaced_header(NULL);
        return;
      }
    
      // The object header will never be displaced to this lock,
      // so it does not matter what the value is, except that it
      // must be non-zero to avoid looking like a re-entrant lock,
      // and must not look locked either.
        // 设置一个没有使用的对象头
        // 
      lock->set_displaced_header(markOopDesc::unused_mark());
        // inflate(膨胀)--去进行重量锁的膨胀
        // 膨胀完执行enter(THREAD)方法,就是加入队列
        // 膨胀成功时010 -- 有别的线程在执行,没有是否锁
      ObjectSynchronizer::inflate(THREAD,
                                  obj(),
                                  inflate_cause_monitor_enter)->enter(THREAD);
    }
    
  • ObjectMonitor.cpp

  • void 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 = Atomic::cmpxchg(Self, &_owner, (void*)NULL);
      if (cur == NULL) {
        // Either ASSERT _recursions == 0 or explicitly set _recursions = 0.
        assert(_recursions == 0, "invariant");
        assert(_owner == Self, "invariant");
        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;
        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 (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(&_count);
    
      JFR_ONLY(JfrConditionalFlushWithStacktrace<EventJavaMonitorEnter> flush(jt);)
      EventJavaMonitorEnter event;
      if (event.should_commit()) {
        event.set_monitorClass(((oop)this->object())->klass());
        event.set_address((uintptr_t)(this->object_addr()));
      }
    
      { // Change java thread status to indicate blocked on monitor enter.
        JavaThreadBlockedOnMonitorEnterState jtbmes(jt, this);
    
        Self->set_current_pending_monitor(this);
    
        DTRACE_MONITOR_PROBE(contended__enter, this, object(), jt);
        if (JvmtiExport::should_post_monitor_contended_enter()) {
          JvmtiExport::post_monitor_contended_enter(jt, this);
    
          // The current thread does not yet own the monitor and does not
          // yet appear on any queues that would get it made the successor.
          // This means that the JVMTI_EVENT_MONITOR_CONTENDED_ENTER event
          // handler cannot accidentally consume an unpark() meant for the
          // ParkEvent associated with this ObjectMonitor.
        }
    
        OSThreadContendState osts(Self->osthread());
        ThreadBlockInVM tbivm(jt);
    
        // 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方法
          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(false, Self);
    
          jt->java_suspend_self();
        }
        Self->set_current_pending_monitor(NULL);
    
        // We cleared the pending monitor info since we've just gotten past
        // the enter-check-for-suspend dance and we now own the monitor free
        // and clear, i.e., it is no longer pending. The ThreadBlockInVM
        // destructor can go to a safepoint at the end of this block. If we
        // do a thread dump during that safepoint, then this thread will show
        // as having "-locked" the monitor, but the OS and java.lang.Thread
        // states will still report that the thread is blocked trying to
        // acquire it.
      }
    
      Atomic::dec(&_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);
    
        // The current thread already owns the monitor and is not going to
        // call park() for the remainder of the monitor enter protocol. So
        // it doesn't matter if the JVMTI_EVENT_MONITOR_CONTENDED_ENTERED
        // event handler consumed an unpark() issued by the thread that
        // just exited the monitor.
      }
      if (event.should_commit()) {
        event.set_previousOwner((uintptr_t)_previous_owner_tid);
        event.commit();
      }
      OM_PERFDATA_OP(ContendedLockAttempts, inc());
    }
    
  • 进入enterI()方法

  • 关键方法是Self->ParkEvent0->Park();

重量锁进入enter简单理解

  • 膨胀成功之后—010

  • 1.cas加锁,有可能在我膨胀之后,别人刚好释放锁 我就去加锁 为了不去排队 也就是不sleep

  • 2.cas失败—自旋—2到3次,也就是cas加锁,如果再失败就会进入队列,进入队列后还有可能自旋,如果再失败,就会睡眠

重量锁的工作模式—ObjectMonitor

  • mark前62位是一个指针 指向一个monitor对象 mp+10
    对象头
    ObjectMonitor
    实例数据 int string boolean
    实例数据
    waitSet
    阻塞的队列--是waiting状态
    EntryList
    owner thread
    _recursions重入次数
    t1 持有锁的线程running
    通过ParkEvent.park方法阻塞底层使用的mutex--阻塞t2-t3-t4--就像一个队列一样--是blocked状态
  • 跟reentrantlock基本差不多,就是多了一个waitset

  • 重量锁mark word前62位指向ObjectMonitor重量锁的指针,后两位是10

waitset

  • waitset是已经拿过锁的,调用了wait方法的,entrylist是等待拿synchronized锁的,waitset中被叫醒的线程会重新进入entrylist重新排队

wait、notify

  • 持有锁的线程发现条件不满足,调用 wait,即可进入 WaitSet 变为 WAITING 状态
    BLOCKED 和 WAITING 的线程都处于阻塞状态,不占用 CPU 时间片
    BLOCKED 线程会在 持有锁的线程释放锁时自动唤醒
    WAITING 线程会在 持有锁的线程调用 notify 或 notifyAll 时唤醒,但唤醒后并不意味者立刻获得锁仍需
    进入EntryList 重新竞争

和sleep的区别

  • 相同:线程的状态相同;都是阻塞状态
  • 区别:
    1、wait是Object的方法,object的9个方法之一;任何对象都可以直接调用;sleep是Thread的静态方法
    2、wait必须配合 synchronized 关键字一起使用;如果一个对象没有获取到锁直接调用wait会异常;sleep则不需要
    3、wait可以通过notify主动唤醒;sleep只能通过打断主动叫醒
    4、wait会释放锁、sleep在阻塞的阶段是不会释放锁的

深入理解wait 和notify sleep

  • public class TestWait1 {
          
          
        
        static final Object key = new Object();
        static boolean isPrettyGril = false; // 有没有女人
        
        /**
         * 多线程情况下 假设某个线程拿到锁了 但是他需要满足某个条件才能执行
         * 如果用sleep 会导致在没有满足条件的情况下:我一直持有锁,别的线程
         * 拿不到锁
         */
        public static void main(String[] args) throws InterruptedException {
          
          
            new Thread(() -> {
          
          
                synchronized (key) {
          
          
                    log.debug("有没有女人[{}]", isPrettyGril);
                    if (!isPrettyGril) {
          
          
                        log.debug("没有女人!等女人,等5秒;别人也不能干活");
                        try {
          
          
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
          
          
                            e.printStackTrace();
                        }
                    }
                    log.debug("等了5秒 有没有女人?[{}]", isPrettyGril);
                    if (isPrettyGril) {
          
          
                        log.debug("------男女搭配干活不累;写完了代码");
                    }else{
          
          
                        log.debug("------下班回家----");
                    }
                }
            }, "jack").start();
            for (int i = 0; i < 10; i++) {
          
          
                new Thread(() -> {
          
          
                    synchronized (key) {
          
          
                        log.debug("我们10个屌丝工作了");
                    }
                }, "其它人").start();
            }
            
            // 要想程序执行,需要下面不加synchronized,这样才能变更isPrettyGirl这个值,睡眠5秒后,执行打印代码
            // 如果加了synchronized,这个线程也一直拿不到锁
            Thread.sleep(1000);
            new Thread(() -> {
          
          
                synchronized (key) {
          
          
                    isPrettyGril = true;
                    log.debug("桥本有菜来了");
                }
            }, "boss").start();
        }
     }
    

问题:sleep不会释放锁 导致其他线程也不会执行

  • import lombok.extern.slf4j.Slf4j;
    @Slf4j(topic = "enjoy")
    public class TestWait2 {
          
          
        static final Object key = new Object();
        static boolean isPrettyGril = false; // 女人
        
        /**
         * 
         */
        public static void main(String[] args) throws InterruptedException {
          
          
            new Thread(() -> {
          
          
                synchronized (key) {
          
          
                    log.debug("有没有女人[{}]", isPrettyGril);
                    if (!isPrettyGril) {
          
          
                        log.debug("没有女人!等女人");
                        try {
          
          
                            // 注意wait的对象是锁对象
                            key.wait();
                        } catch (InterruptedException e) {
          
          
                            e.printStackTrace();
                        }
                    }
                    log.debug("老板喊我了 有没有女人?[{}]", isPrettyGril);
                    if (isPrettyGril) {
          
          
                        log.debug("---男女搭配干活不累;写完了代码");
                    }else{
          
          
                        log.debug("下班回家");
                    }
                }
            }, "jack").start();
            for (int i = 0; i < 10; i++) {
          
          
                new Thread(() -> {
          
          
                    synchronized (key) {
          
          
                        log.debug("我们10个屌丝工作了");
                    }
                }, "其它人").start();
            }
            Thread.sleep(1000);
            new Thread(() -> {
          
          
                synchronized (key) {
          
          
                    isPrettyGril = true;
                    log.debug("桥本有菜来了");
                    key.notify();
                }
            }, "boss").start();
        }
    }
    

问题:单个有条件的线程是没有问题的,设想如果有多个线程都需要满足条件

  • @Slf4j(topic = "enjoy")
    public class TestWait3 {
          
          
        static final Object key = new Object();
        static boolean isPrettyGril = false; // 女人
        static boolean isMoney = false;//工资
        public static void main(String[] args) throws InterruptedException {
          
          
            new Thread(() -> {
          
          
                synchronized (key) {
          
          
                    log.debug("有没有女人[{}]", isPrettyGril);
                    if (!isPrettyGril) {
          
          
                        log.debug("没有女人!等女人");
                        try {
          
          
                            key.wait();
                        } catch (InterruptedException e) {
          
          
                            e.printStackTrace();
                        }
                    }
                    log.debug("老板喊我了 有没有女人?[{}]", isPrettyGril);
                    if (isPrettyGril) {
          
          
                        log.debug("男女搭配干活不累;写完了代码");
                    }else {
          
          
                        log.debug("不干了---下班回家--------");
                    }
                }
            }, "jack").start();
            new Thread(() -> {
          
          
                synchronized (key) {
          
          
                    log.debug("有没有工资[{}]", isMoney);
                    if (!isMoney) {
          
          
                        log.debug("没有工资!等发工资");
                        try {
          
          
                            key.wait();
                        } catch (InterruptedException e) {
          
          
                            e.printStackTrace();
                        }
                    }
                    log.debug("老板喊我了 有没有发工资?[{}]", isMoney);
                    if (isMoney) {
          
          
                        log.debug("-----卧槽好多钱;写完了代码");
                    }else {
          
          
                        log.debug("约会去了");
                    }
                }
            }, "rose").start();
            Thread.sleep(1000);
            new Thread(() -> {
          
          
                synchronized (key) {
          
          
                    isMoney = true;
                    log.debug("发工资了---");
                    // key.wait()的方法有两个
                    // 此时notify()会随机叫醒一个
                    key.notify();
                }
            }, "boss").start();
        }
    }
    

问题:虚假唤醒,怎么解决?

  • package com.enjoy.wait;
    import lombok.extern.slf4j.Slf4j;
    @Slf4j(topic = "enjoy")
    public class TestWait4 {
          
          
        static final Object key = new Object();
        static boolean isPrettyGril = false; // 女人
        static boolean isMoney = false;//工资
        public static void main(String[] args) throws InterruptedException {
          
          
            new Thread(() -> {
          
          
                synchronized (key) {
          
          
                    log.debug("有没有女人[{}]", isPrettyGril);
                    while (!isPrettyGril) {
          
          
                        log.debug("没有女人!等女人");
                        try {
          
          
                            key.wait();
                        } catch (InterruptedException e) {
          
          
                            e.printStackTrace();
                        }
                    }
                    log.debug("老板喊我了 有没有女人?[{}]", isPrettyGril);
                    if (isPrettyGril) {
          
          
                        log.debug("男女搭配干活不累;写完了代码");
                    }else {
          
          
                        log.debug("不干了---下班回家--------");
                    }
                }
            }, "jack").start();
            new Thread(() -> {
          
          
                synchronized (key) {
          
          
                    log.debug("有没有工资[{}]", isMoney);
                    if (!isMoney) {
          
          
                        log.debug("没有工资!等发工资");
                        try {
          
          
                            key.wait();
                        } catch (InterruptedException e) {
          
          
                            e.printStackTrace();
                        }
                    }
                    log.debug("老板喊我了 有没有发工资?[{}]", isMoney);
                    if (isMoney) {
          
          
                        log.debug("-----卧槽好多钱;写完了代码");
                    }
                }
            }, "rose").start();
            for (int i = 0; i < 10; i++) {
          
          
                new Thread(() -> {
          
          
                    synchronized (key) {
          
          
                        log.debug("我们10个屌丝工作了");
                    }
                }, "其它人").start();
            }
            Thread.sleep(1000);
            new Thread(() -> {
          
          
                synchronized (key) {
          
          
                    isMoney = true;
                    log.debug("发工资了---");
                    // 同时注意key.wait()要用while包着
                    key.notifyAll();
                }
            }, "boss").start();
        }
    }
    

最终版

  • package com.enjoy.wait;
    import lombok.extern.slf4j.Slf4j;
    @Slf4j(topic = "enjoy")
    public class TestWait5 {
          
          
        static final Object key = new Object();
        static boolean isPrettyGril = false; // 女人
        static boolean isMoney = false;//工资
        public static void main(String[] args) throws InterruptedException {
          
          
            new Thread(() -> {
          
          
                synchronized (key) {
          
          
                    log.debug("有没有女人[{}]", isPrettyGril);
                    while (!isPrettyGril) {
          
          
                        log.debug("没有女人!等女人");
                        try {
          
          
                            pc.wait();
                        } catch (InterruptedException e) {
          
          
                            e.printStackTrace();
                        }
                    }
                    log.debug("老板喊我了 有没有女人?[{}]", isPrettyGril);
                    if (isPrettyGril) {
          
          
                        log.debug("男女搭配干活不累;写完了代码");
                    }else {
          
          
                        log.debug("不干了---下班回家--------");
                    }
                }
            }, "jack").start();
            new Thread(() -> {
          
          
                synchronized (key) {
          
          
                    log.debug("有没有工资[{}]", isMoney);
                    while (!isMoney) {
          
          
                        log.debug("没有工资!等发工资");
                        try {
          
          
                            pc.wait();
                        } catch (InterruptedException e) {
          
          
                            e.printStackTrace();
                        }
                    }
                    log.debug("老板喊我了 有没有发工资?[{}]", isMoney);
                    if (isMoney) {
          
          
                        log.debug("-----卧槽好多钱;写完了代码");
                    }
                }
            }, "rose").start();
            for (int i = 0; i < 10; i++) {
          
          
                new Thread(() -> {
          
          
                    synchronized (key) {
          
          
                        log.debug("我们10个屌丝工作了");
                    }
                }, "其它人").start();
            }
            
            // 主线程睡眠1秒钟,保证上面的线程先拿到锁
            Thread.sleep(1000);
            new Thread(() -> {
          
          
                synchronized (key) {
          
          
                    isMoney = true;
                    log.debug("发工资了---");
                    pc.notifyAll();
                }
            }, "boss").start();
        }
    }
    

wait可以带参数,表示阻塞时间

  • package com.shadow.test;
    
    import lombok.extern.slf4j.Slf4j;
    
    
    /**
     * 1、什么是对象头?
     *  什么是对象?内存级别而言来研究什么是对象
     */
    
    @Slf4j(topic = "enjoy")
    public class Test6 {
          
          
        static boolean isPrettyGril = false;
        static boolean isMoney = false;
        static  Object key = new Object();
    
    
        /**
         * 多线程情况下 假设某个线程拿到锁了,但是他需要满足某个条件
         * 才能执行 如果用sleep 会导致在没有满足条件的情况下;我一直阻塞
         * 一直持有锁,别的线程也拿不到锁
         * 得有办公室的key
         * boss 需要叫jack 去加班编程;还有其他十个人是自愿加班
         * jack不愿加班---你给我女人
         * @param args
         * @throws InterruptedException
         */
    
        public static   void main(String[] args) throws InterruptedException {
          
          
            log.debug("mian--------");
            new Thread(() -> {
          
          
                synchronized (key) {
          
          
                    try {
          
          
                        /**
                         * wait可以带参数
                         * 第二个纳秒参数 是直接把毫秒+1
                         */
                        key.wait(5000,50);
                        System.out.println("5秒钟之后 mian--end------");
                    } catch (InterruptedException e) {
          
          
                        e.printStackTrace();
                    }
                }
            }, "boss").start();
    
    
    
            Thread.sleep(100);
    
    
    
        }
    
    
    
    
    }
    
    

唤醒wait的方法

  • notify/notifyAll
  • Object.wait(5000),5秒后自己醒来
    • 还有一个纳秒级别的,Object.wait(5000,50)
    • 后面的纳秒,会直接在前面的基础上加1毫秒
  • 打断interrupted
  • wait方法通常和synchronized配合使用
  • 一旦调用wait方法就会升级成重量锁(就算是偏向锁也是),就会有一个ObjectMonitor,线程才能和它关联,而这个对象里面才有waitset,才能存阻塞的线程

猜你喜欢

转载自blog.csdn.net/Markland_l/article/details/113623384