wait 加锁示例

public class WaitDemo {
private static Object locker = new Object(); public static void main(String[] args) throws InterruptedException {
WaitDemo waitDemo = new WaitDemo(); // 启动新线程,防止主线程被休眠
new Thread(() -> {
try {
waitDemo.doWait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
Thread.sleep(200); // 此行本身没有意义,是为了确保 wait() 先执行再执行 notify()
waitDemo.doNotify();
} /**
* 执行 wait()
*/
private void doWait() throws InterruptedException {
synchronized (locker) {
System.out.println("wait start.");
locker.wait();
System.out.println("wait end.");
}
} /**
* 执行 notify()
*/
private void doNotify() {
synchronized (locker) {
System.out.println("notify start.");
locker.notify();
System.out.println("notify end.");
}
}
}

以上程序的执行结果为:

wait start.

notify start.

notify end.

wait end.

代码解析

从上述代码可以看出,我们给 wait() 和 notify() 两个方法上了同一把锁(locker),但在调用完 wait() 方法之后 locker 锁就被释放了,所以程序才能正常执行 notify() 的代码,因为是同一把锁,如果不释放锁的话,是不会执行 notify() 的代码的,这一点也可以从打印的结果中证实(结果输出顺序),所以综合以上情况来说 wait() 方法是释放锁的

sleep 加锁示例

public class WaitDemo {
private static Object locker = new Object(); public static void main(String[] args) throws InterruptedException {
WaitDemo waitDemo = new WaitDemo();
// 启动新线程,防止主线程被休眠
new Thread(() -> {
synchronized (locker) {
try {
System.out.println("sleep start.");
Thread.sleep(1000);
System.out.println("sleep end.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start(); Thread.sleep(200);
waitDemo.doNotify();
} /**
* 执行 notify()
*/
private void doNotify() {
synchronized (locker) {
System.out.println("notify start.");
locker.notify();
System.out.println("notify end.");
}
}
}

以上程序的执行结果为:

sleep start.

sleep end.

notify start.

notify end.

代码解析

从上述代码可以看出 sleep(1000) 方法(行号:11)执行之后,调用 notify() 方法并没有获取到 locker 锁,从上述执行结果中可以看出,而是执行完 sleep(1000) 方法之后才执行的 notify() 方法,因此可以证明调用 sleep() 方法并不会释放锁

知识扩展

1.sleep 和 wait 有什么区别?

sleepwait 几乎是所有面试中必问的题,但想完全回答正确似乎没那么简单。

对于 sleepwait 的区别,通常的回答是这样的:

  • wait 必须搭配 synchronize 一起使用,而 sleep 不需要;
  • 进入 wait 状态的线程能够被 notify 和 notifyAll 线程唤醒,而 sleep 状态的线程不能被 notify 方法唤醒;
  • wait 通常有条件地执行,线程会一直处于 wait 状态,直到某个条件变为真,但是 sleep 仅仅让你的线程进入睡眠状态;
  • wait 方法会释放对象锁,但 sleep 方法不会。

但上面的回答显然遗漏了一个重要的区别,在调用 wait 方法之后,线程会变为 WATING 状态,而调用 sleep 方法之后,线程会变为 TIMED_WAITING 状态。

2.wait 能不能在 static 方法中使用?为什么?

不能,因为 wait 方法是实例方法(非 static 方法),因此不能在 static 中使用,源码如下:

public final void wait() throws InterruptedException {
wait(0);
}

3.wait/notify 可以不搭配 synchronized 使用吗?为什么?

不行,因为不搭配 synchronized 使用的话程序会报错,如下图所示:

更深层次的原因是因为不加 synchronized 的话会造成 Lost Wake-Up Problem,唤醒丢失的问题,详情可见:https://juejin.im/post/5e6a4d8a6fb9a07cd80f36d1

总结

本文我们通过 synchronized 锁定同一对象,来测试 wait 和 sleep 方法,再通过执行结果的先后顺序证明:wait 方法会释放锁,而 sleep 方法并不会。同时我们还讲了几个 waitsleep 的常见面试问题,希望本文可以帮助到你。

如何证明sleep不释放锁,而wait释放锁?的更多相关文章

  1. SQLServer 查询使用键查找时锁申请及释放顺序

    最近看了高兄的一篇文章,Sql Server 高频,高并发访问中的键查找死锁解析,很有收获,里面讲到了键查找引起的死锁问题. 当然看的过程中,其实自己有个疑问: 对于键查找这类查询,会申请哪些锁,锁申 ...

  2. 深入浅出 Java Concurrency (9): 锁机制 part 4 锁释放与条件变量 (Lock.unlock And Condition)

    本小节介绍锁释放Lock.unlock(). Release/TryRelease unlock操作实际上就调用了AQS的release操作,释放持有的锁. public final boolean ...

  3. 追踪SQL Server执行delete操作时候不同锁申请与释放的过程

    一直以为很了解sqlserver的加锁过程,在分析一些特殊情况下的死锁之后,尤其是并发单表操作发生的死锁,对于加解锁的过程,有了一些重新的认识,之前的知识还是有一些盲区在里面的.delete加锁与解锁 ...

  4. 多线程编程-- part5.1 互斥锁之非公平锁-获取与释放

    非公平锁之获取锁 非公平锁和公平锁在获取锁的方法上,流程是一样的:它们的区别主要表现在“尝试获取锁的机制不同”.简单点说,“公平锁”在每次尝试获取锁时,都是采用公平策略(根据等待队列依次排序等待):而 ...

  5. SQL Server查询使用键查找时锁申请及释放顺序

    当然看的过程中,其实自己有个疑问: 对于键查找这类查询,会申请哪些锁,锁申请和释放的顺序是怎样的? 准备 备注:测试表仍然使用高兄文中创建的测试表testklup 在开始之前,使用dbcc ind 命 ...

  6. ZooKeeper 分布式锁 Curator 源码 02:可重入锁重复加锁和锁释放

    ZooKeeper 分布式锁 Curator 源码 02:可重入锁重复加锁和锁释放 前言 加锁逻辑已经介绍完毕,那当一个线程重复加锁是如何处理的呢? 锁重入 在上一小节中,可以看到加锁的过程,再回头看 ...

  7. java并发库 Lock 公平锁和非公平锁

    jdk1.5并发包中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,关于两者区别,java并发编程实践里面有解释 公平锁:   Threads acquir ...

  8. java 多线程8 : synchronized锁机制 之 方法锁

    脏读 一个常见的概念.在多线程中,难免会出现在多个线程中对同一个对象的实例变量或者全局静态变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数 ...

  9. 内部锁之一:锁介绍(偏向锁 & 轻量级锁 & 重量级锁 & 各自优缺点及场景)

    一.内部锁介绍 上篇文章<Synchronized之二:synchronized的实现原理>中向大家介绍了Synchronized原理及优化锁.现在我们应该知道,Synchronized是 ...

  10. Java synchronized实现原理总结和偏量锁、轻量锁、重量锁、自旋锁

    synchronized实现同步的基础:Java中的每一个对象都可以作为锁.具体表现为以下3种形式. 对于普通同步方法,锁是当前实例对象(this). 对于静态同步方法,锁是当前类的Class对象. ...

随机推荐

  1. Vue错误汇总

    1.Vue导入js后没反应{{msg}}仍是{{msg}} 错误原因:js里写错了,或者没new一个Vue html页面: 解决方法:代码加入 new

  2. ubuntu镜像源切换

    换源准备: 换源之前明确使用平台,X86架构.ARM架构的源,ubuntu18.04和unbuntu16.04等源均不同,切忌病急乱投医,看到一个源复制过来添加后没用. 分析一条源: deb http ...

  3. 新版MySQL开始使用时遇到的问题(时区、权限):

    新版MySQL(本人Server version: 8.0.15)在刚开始使用时遇到的问题: 查看mysql安装版本:命令窗口 时区问题解决(The server time zone value 'Ö ...

  4. java命令行输入参数

    Java命令行输入参数 代码用例:命令行输入参数,并进行加法运算. public class Demo01 { public static void main(String[] args) { for ...

  5. 洛谷 CF1012C Hills (动态规划)

    题目大意:有n个山丘 , 可以在山丘上建房子 , 建房子的要求是 : 该山丘的左右山丘严格的矮于该山丘 (如果有的话),你有一架挖掘机,每单位时间可以给一个山丘挖一个单位的高度,问你想要建造 1,2, ...

  6. WTL中GDI+支持资源文件加载

    WTL中GDI+支持资源文件加载 分类: WTL WTL gdi+ gdi+2013-04-22 17:16 78人阅读 评论(0) 收藏 举报 WTLGDI+c++ 今天遇到一个小问题困扰了.就是G ...

  7. jmeter跨线程组获取cookie或jmeter线程组共享cookie-笔者亲测

    一.Jmeter版本 此次示例采用的是apache-jmeter-5.2.1版本 二.设置配置文件使Cookie管理器保存cookie信息. 修改apache-jmeter-5.2.1/bin/jme ...

  8. 使用TransferLearning实现环视图像的角点检测——Tensorflow+MobileNetv2_SSD

    环境说明 依赖环境安装eIQ官方指南: name: eiq_auto channels: - conda-forge - defaults dependencies: - numpy=1.18.1=p ...

  9. File and Code Templates IN IDEA

    File and Code Templates (文件代码模板) 如何自定义设置头文件的注释,其中包括一些作者和文件创建时间和版本的设置 先打开File->Settings(或Alt+Ctrl+ ...

  10. pdf流文件转图片

    需求:将后台返回的pdf流文件转换成图片与页面其他内容一起打印pdf流文件不能直接在前台显示,需要借助pdf.js+viewer.js. 一般情况下,如果要打印pdf流文件,可以直接在新打开的view ...