博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java并发编程原理与实战十一:锁重入&自旋锁&死锁
阅读量:4691 次
发布时间:2019-06-09

本文共 8992 字,大约阅读时间需要 29 分钟。

一、锁重入

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() {            @Override            public void run() {                Demo demo = new Demo();                demo.a();            }        }).start();    }}

运行结果:

ab

以上结果说明,线程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() {            @Override            public void run() {                demo.a();            }        }).start();        new Thread(new Runnable() {            @Override            public void run() {                demo.b();            }        }).start();    }}

运行结果:

ab

虽然以上运行结果还是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() {            @Override            public void run() {                demo.a();            }        }).start();        new Thread(new Runnable() {            @Override            public void run() {                demo1.b();            }        }).start();    }}

运行结果:

ab

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() {            @Override            public void run() {                demo.a();            }        }).start();        new Thread(new Runnable() {            @Override            public void run() {                demo.b();            }        }).start();    }}

运行结果:

aprint b()bb

以上结果,先输出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() {            @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();        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() {            @Override            public 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() {            @Override            public 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() {            @Override            public void run() {                demo3.a();            }        }).start();        new Thread(new Runnable() {            @Override            public void run() {                demo3.b();            }        }).start();    }}

运行结果:

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

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

点击线程,检测死锁:

 

参考资料:

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

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

你可能感兴趣的文章
P2709 小B的询问
查看>>
PHP echo 和 print 语句
查看>>
第一讲 一个简单的Qt程序分析
查看>>
Centos 6.5下的OPENJDK卸载和SUN的JDK安装、环境变量配置
查看>>
poj 1979 Red and Black(dfs)
查看>>
【.Net基础03】HttpWebRequest模拟浏览器登陆
查看>>
zTree async 动态参数处理
查看>>
Oracle学习之常见错误整理
查看>>
lock_sga引起的ksvcreate :process(m000) creation failed
查看>>
数据库插入数据乱码问题
查看>>
altium annotate 选项设置 complete existing packages
查看>>
【模式识别与机器学习】——SVM举例
查看>>
【转】IT名企面试:微软笔试题(1)
查看>>
IO流入门-第十章-DataInputStream_DataOutputStream
查看>>
DRF的分页
查看>>
Mysql 模糊匹配(字符串str中是否包含子字符串substr)
查看>>
python:open/文件操作
查看>>
流程控制 Day06
查看>>
Linux下安装Tomcat
查看>>
windows live writer 2012 0x80070643
查看>>