java同步锁工作原理_Java Synchronized 重量级锁原理深入剖析下(同步篇)

前言

线程并发系列文章:

上篇分析了重量级锁在线程互斥场景下加锁、释放锁的过程,本篇将分析重量级锁在线程同步下的等待、通知机制。

通过本篇文章,你将了解到:

1、为什么 wait/notify/notifyAll 需要上锁

2、wait/notify/notifyAll 源码入口

3、Object.wait(xx) 流程解析

4、Object.notify()/Object.notifyAll() 流程解析

5、wait/notify/notifyAll 流程图

6、线程互斥同步下 锁的流程图

7、wait/notify/notifyAll 疑难点解析

1、为什么 wait/notify/notifyAll 需要上锁

一个Demo

线程同步需要在某种条件下调用wait/notify进行同步,先来看简单的例子:

public class TestThread {

static Object object = new Object();

static int count = 1;

public static void main(String args[]) {

//线程 A 消费者

new Thread(new Runnable() {

@Override

public void run() {

try {

count--;

if (count == 0) {

//count == 0 才会等待

object.wait();

}

} catch (Exception e) {

}

}

}).start();

//线程 B 生产者

new Thread(new Runnable() {

@Override

public void run() {

try {

//生产好了就通知线程A

count++;

object.notify();

} catch (Exception e) {

}

}

}).start();

}

}

上述功能很简单:线程B生产东西(增加count值),线程A消费东西(减少count值),线程A发现没东西可用了就调用wait挂起等待相应的条件满足后再次运行。

此处的条件即是:count的值。

正常情况下是:线程A等待count值,线程B通知线程A count值已经准备好了,这就是线程之间的同步。

wait 之前为什么需要获取锁

现在从多线程并发的角度来看这Demo,可能的运行顺序如下:

1、count初始值为1。

2、线程A先执行到count==0,准备调用object.wait()。

3、此时线程B已经修改好了count值,并且调用了Object.notify()。

4、线程A此时调用Object.wait()后,因为错过了Object.notify(),所以就永远阻塞于此处。

导致上面问题的原因是:count是线程间共享的,对它的修改存在并发问题,因此需要加锁来实现互斥访问count。

notify 之前为什么需要获取锁

你也许会说:既然锁是为了保护count,那么只保护对应的共享变量即可,notify可以不上锁啊。如下代码:

//线程A

synchronized (object) {

count--;

if (count == 0) {

//count == 0 才会等待

object.wait();

}

}

//线程B

synchronized (object) {

//生产好了就通知线程A

count++;

}

object.notify();

notify 的目的是将等待队列里的线程插入到同步队列里,假设是notify没有在同步代码块里,那么线程B修改count值后释放锁,因为还没有notify,因此A没有移动到同步队列里,最终无法唤醒线程A,A就会一直阻塞等待。

notifyAll也是一样的道理。

notify具体原理接下来会详细分析。

JVM如何避免不正常地调用

wait/notify/notifyAll 需要在同步块里调用,而用户不一定这么操作,因此JVM会在调用wait/notify/notifyAll 时检测当前线程是否已经获取了锁,没有锁则会抛出异常。

#ObjectMonitor.cpp

void ObjectMonitor::notify(TRAPS) {

CHECK_OWNER();

...

}

void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {

...

CHECK_OWNER();

...

}

void ObjectMonitor::notifyAll(TRAPS) {

CHECK_OWNER();

...

}

#define CHECK_OWNER() \

do { \

if (THREAD != _owner) {

//当前线程不是重量级锁的获得者 \

if (THREAD->is_lock_owned((address) _owner)) { \

//当前线程是之前轻量级锁的获得者

_owner = THREAD ; /* Convert from basiclock addr to Thread addr */ \

_recursions = 0; \

OwnerIsThread = 1 ; \

} else {

//没有获取抛出异常 \

TEVENT (Throw IMSX) ; \

THROW(vmSymbols::java_lang_IllegalMonitorStateException()); \

} \

} \

} while (false)

可以看出,调用wait/notify/notifyAll的时候会调用宏CHECK_OWNER()去检测当前线程是否获取了锁,没有则抛出IllegalMonitorStateException 异常。

小结:

wait/notify/notifyAll 需要包在synchronized 同步块里的原因是保护同步的条件在并发场景下能够被正确访问。

2、wait/notify/notifyAll 源码入口

wait/notify/notifyAll 方法是声明在Java顶级类Object.java里的,通过寻找发现是native层实现的。

#Object.c

static JNINativeMethod methods[] = {

{"hashCode", "()I", (void *)&JVM_IHashCode},

{"wait", "(J)V", (void *)&JVM_MonitorWait},

{"notify", "()V", (void *)&JVM_MonitorNotify},

{"notifyAll", "()V", (void *)&JVM_MonitorNotifyAll},

{"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone},

};

可以看到是动态注册的JNI函数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值