一、锁重入

package com.roocon.thread.t6;public class Demo {/*当第一个线程A拿到当前实例锁后,进入a方法,那么,线程A还能拿到被当前实例所加锁的另一个同步方法b吗?是不是只有当线程A释放了a方法的同步锁后,才可以去获取b方法的同步锁呢?*/public synchronized void a(){System.out.println("a");b();}public synchronized void b(){System.out.println("b");}public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {Demo demo = new Demo();demo.a();}}).start();}
}

运行结果:

a
b

以上结果说明,线程A在释放方法a的同步锁之前,是可以重新获得b方法的同步锁的。同一个线程拿到同一个对象的锁,它是可以进入另一个同步方法的,这就是锁的重入。以上代码仅仅是同一个线程在一个同步方法中去成功调用另一个同步方法,并且,锁的是同一个实例。那么,不同的线程拿同一把对象去加锁,会怎样进行呢?

package com.roocon.thread.t6;public class Demo {/*当第一个线程A拿到当前实例锁后,进入a方法,那么,线程A还能拿到被当前实例所加锁的另一个同步方法b吗?是不是只有当线程A释放了a方法的同步锁后,才可以去获取b方法的同步锁呢?*/public synchronized void a(){System.out.println("a");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}}public synchronized void b(){System.out.println("b");try {Thread.sleep(8000);} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {Demo demo = new Demo();//Demo demo1 = new Demo();new Thread(new Runnable() {@Overridepublic void run() {demo.a();}}).start();new Thread(new Runnable() {@Overridepublic void run() {demo.b();}}).start();}
}

运行结果:

a
b

虽然以上运行结果还是a b,但是,由于锁的是同一个实例,所以,在输出a之后,要等待5s才会输出b。若将以上代码修改为如下,锁的不是同一个实例:

package com.roocon.thread.t6;public class Demo {/*当第一个线程A拿到当前实例锁后,进入a方法,那么,线程A还能拿到被当前实例所加锁的另一个同步方法b吗?是不是只有当线程A释放了a方法的同步锁后,才可以去获取b方法的同步锁呢?*/public synchronized void a(){System.out.println("a");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}}public synchronized void b(){System.out.println("b");try {Thread.sleep(8000);} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {Demo demo = new Demo();Demo demo1 = new Demo();new Thread(new Runnable() {@Overridepublic void run() {demo.a();}}).start();new Thread(new Runnable() {@Overridepublic void run() {demo1.b();}}).start();}
}

运行结果:

a
b

a b几乎是同时输出的。

以上两个代码说明,如果多个线程同时去执行同步方法,如果锁的是同一个实例,那么必须等当前这个同步方法释放锁后,才可以去获取另一个同步锁方法。

而如果锁的不是同一个实例,那么,两个同步方法几乎是可以同时执行。有了以上基础,那么再来理解以下代码,就很简单了。

package com.roocon.thread.t6;public class Demo {public synchronized void a(){System.out.println("a");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("print b()");b();}public synchronized void b(){System.out.println("b");try {Thread.sleep(8000);} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {Demo demo = new Demo();new Thread(new Runnable() {@Overridepublic void run() {demo.a();}}).start();new Thread(new Runnable() {@Overridepublic void run() {demo.b();}}).start();}
}

运行结果:

a
print b()
b
b

以上结果,先输出a,过了5s后再输出print b()  b,再过了8s输出b,也就是,由于锁的是同一个实例,所以,只有线程1当a方法调用完毕后,线程2才可以获取该实例锁进入b方法。

二、自旋锁

自旋锁,自己在不停的旋转,旋的是CPU的时间片,也就是空转CPU。当另外一个线程没有执行结束时,它一直在自旋等待。它会一直等待另外的线程执行完毕。

package com.roocon.thread.t6;

public class Demo2 {    //多个线程执行完毕后,输出,全部执行完毕    public static void main(String[] args) {        new Thread(new Runnable() {            @Override            public void run() {                System.out.println(Thread.currentThread().getName() + "开始执行...");                try {                    Thread.sleep(2000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println(Thread.currentThread().getName() + "执行完毕了");            }        }).start();        new Thread(new Runnable() {            @Override            public void run() {                System.out.println(Thread.currentThread().getName() + "开始执行...");                try {                    Thread.sleep(2000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println(Thread.currentThread().getName() + "执行完毕了");            }        }).start();        System.out.println("全部执行完毕");    }}

运行结果:

全部执行完毕
Thread-0开始执行...
Thread-1开始执行...
Thread-1执行完毕了
Thread-0执行完毕了

以上结果明显,主线程执行结束后,其他线程还在继续执行。那么,怎么解决这个问题呢?

加入条件判断,如果最后只剩下主线程了,则打印。

package com.roocon.thread.t6;public class Demo2 {//多个线程执行完毕后,输出,全部执行完毕public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "开始执行...");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "执行完毕了");}}).start();new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "开始执行...");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "执行完毕了");}}).start();if (Thread.activeCount()==1) {System.out.println("全部执行完毕");}}
}

运行结果:

Thread-0开始执行...
Thread-1开始执行...
Thread-0执行完毕了
Thread-1执行完毕了

为什么不输出“全部执行完毕"呢?因为,以上代码是并行执行的,在执行if语句时,Thread.activeCount()根本就不等于1。所以呢,我们让它在不等于1的时候,也就是除了主线程还有别的线程时,让它自旋等待。自旋完毕后,再去执行输出”全部执行完毕“,达到想要的效果。

package com.roocon.thread.t6;import java.util.Random;public class Demo2 {//多个线程执行完毕后,输出,全部执行完毕public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "开始执行...");try {Thread.sleep(new Random().nextInt(2000));} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "执行完毕了");}}).start();new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "开始执行...");try {Thread.sleep(new Random().nextInt(2000));} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "执行完毕了");}}).start();while (Thread.activeCount() != 1) {//其实在实际应用中,不能这样去判断线程的个数。全部执行完毕不一定会被正确输出。      //自旋等待}System.out.println("全部执行完毕");}
}

运行结果:

Thread-0开始执行...
Thread-1开始执行...
Thread-1执行完毕了
Thread-0执行完毕了全部执行完毕

以上代码只能说是模拟自旋等待过程。

三、模拟死锁

package com.roocon.thread.t6;public class Demo3 {private Object obj1 = new Object();private Object obj2 = new Object();public void a(){synchronized (obj1){try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (obj2){System.out.println("a");}}}public void b(){synchronized (obj2){try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (obj1){System.out.println("b");}}}public static void main(String[] args) {Demo3 demo3 = new Demo3();new Thread(new Runnable() {@Overridepublic void run() {demo3.a();}}).start();new Thread(new Runnable() {@Overridepublic void run() {demo3.b();}}).start();}}

运行结果:

控制台一直在运行,但是无任何输出。

通过命令检测是否真的发生了死锁:

点击线程,检测死锁:

参考资料:

《java并发编程与实战》龙果学院

转载于:https://www.cnblogs.com/pony1223/p/9375206.html

Java并发编程原理与实战十一:锁重入自旋锁死锁相关推荐

  1. Java并发编程原理与实战六:主线程等待子线程解决方案

    Java并发编程原理与实战六:主线程等待子线程解决方案 参考文章: (1)Java并发编程原理与实战六:主线程等待子线程解决方案 (2)https://www.cnblogs.com/pony1223 ...

  2. Java并发编程(1):可重入内置锁

    每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁.线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时会自动释放锁.获得内置锁的唯一途径就是进入由这个锁保护的同步代码块 ...

  3. 12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁

    小陈:呼叫老王...... 老王:来了来了,小陈你准备好了吗?今天我们来讲synchronized的锁重入.锁优化.和锁升级的原理 小陈:早就准备好了,我现在都等不及了 老王:那就好,那我们废话不多说 ...

  4. 吐血整理-高级程序员必备Java并发编程原理,没时间看建议收藏

    简介: Java线程之间的通信对程序员完全透明,内存可见性问题很容易困扰Java程序员,这一系列几篇文章将揭开Java内存模型的神秘面纱.这一系列的文章大致分4个部分,分别是: Java内存模型基础, ...

  5. JUC并发编程系列详解篇十四(自旋锁 VS 适应性自旋锁)

    自旋锁 由于在多处理器环境中某些资源的有限性,有时需要互斥访问(mutual exclusion),这时候就需要引入锁的概念,只有获取了锁的线程才能够对资源进行访问,由于多线程的核心是CPU的时间分片 ...

  6. java 变量锁_并发编程高频面试题:可重入锁+线程池+内存模型等(含答案)

    对于一个Java程序员而言,能否熟练掌握并发编程是判断他优秀与否的重要标准之一.因为并发编程是Java语言中最为晦涩的知识点,它涉及操作系统.内存.CPU.编程语言等多方面的基础能力,更为考验一个程序 ...

  7. Java并发编程原理与实战一:聊聊并发

    一.大纲 •你真的了解并发吗 •多线程和并发 •多线程和多进程 •线程一定快吗 •学习并发的四个阶段 •学习目标 •适合人群 •荐书 二.学习并发的四个阶段 •熟练掌握API,能够完成并发编程 •熟读 ...

  8. Java并发编程(7):使用synchronized获取互斥锁的几点说明

    在并发编程中,多线程同时并发访问的资源叫做临界资源,当多个线程同时访问对象并要求操作相同资源时,分割了原子操作就有可能出现数据的不一致或数据不完整的情况,为避免这种情况的发生,我们会采取同步机制,以确 ...

  9. java并发编程(二)多个线程多个锁

    多个线程多个锁 多个线程多个锁:多个线程,每个线程都可以拿到自己制定的锁,分别获得锁之后,执行synchronized方法体的内容.就是在上次那个博客上说道的锁竞争的问题,是因为所有的线程过来以后都争 ...

  10. java 多进程多线程_Java并发编程原理与实战三:多线程与多进程的联系以及上下文切换所导致资源浪费问题...

    一.进程 考虑一个场景:浏览器,网易云音乐以及notepad++ 三个软件只能顺序执行是怎样一种场景呢?另外,假如有两个程序A和B,程序A在执行到一半的过程中,需要读取大量的数据输入(I/O操作),而 ...

最新文章

  1. Java并发编程-volatile
  2. Android初学:联系创建Activity
  3. ECS 支持 IPv6 啦,快来尝鲜吧~
  4. 驳《从团购网站看中国人的创新精神》
  5. oracle 字段 查找重复,oracle数据库查询重复的索引列
  6. Bootstrap 进度条堆叠
  7. mysql oracle 锁机制_Mysql锁机制
  8. html中单选框重置,HTML表单和组件
  9. java关键字:volatile
  10. 编译使用CEF2623遇到的错误解决办法
  11. Java项目源码下载SSM网上水果生鲜超市商城|电商购物系统
  12. 全解电磁流量计功能和精度性能
  13. b和kb的换算_b和bit换算(KB转换G)
  14. 费马小定理、欧拉定理总结
  15. 数据分析的步骤是什么?
  16. android的虚拟机
  17. 即插即用demo系列——文本相似度比较
  18. MySQL 修改报错 You can't specify target table 'tb_trade' for update in FROM clause
  19. 【信管9.2】项目沟通管理过程
  20. unsigned long long类型与long long类型

热门文章

  1. BAT工程师自研存储引擎,火爆Github!!大家速度顶起来
  2. 「leetcode」17.电话号码的字母组合【回溯算法】详解!
  3. 苹果Mac仿windows10任务栏工具:​​​​​​​​uBar
  4. 区块链开发(三)以太坊客户端命令行选项汇总
  5. 在BetterZip的收藏夹中如何添加经常使用的文件夹?
  6. 入门:Mac终端常用知识
  7. C# Windows异步I/O操作
  8. 关闭所有的screen
  9. 关于TCP/IP,必知必会的十个问题
  10. OC开发实例变量的访问控制详解