java重入锁 自旋锁_Java并发编程原理与实战十一:锁重入&自旋锁&死锁

一、锁重入

packagecom.roocon.thread.t6;public classDemo {/*当第一个线程A拿到当前实例锁后,进入a方法,那么,线程A还能拿到被当前实例所加锁的另一个

同步方法b吗?是不是只有当线程A释放了a方法的同步锁后,才可以去获取b方法的同步锁呢?*/

public synchronized voida(){

System.out.println("a");

b();

}public synchronized voidb(){

System.out.println("b");

}public static voidmain(String[] args) {new Thread(newRunnable() {

@Overridepublic voidrun() {

Demo demo= newDemo();

demo.a();

}

}).start();

}

}

运行结果:

a

b

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

packagecom.roocon.thread.t6;public classDemo {/*当第一个线程A拿到当前实例锁后,进入a方法,那么,线程A还能拿到被当前实例所加锁的另一个

同步方法b吗?是不是只有当线程A释放了a方法的同步锁后,才可以去获取b方法的同步锁呢?*/

public synchronized voida(){

System.out.println("a");try{

Thread.sleep(5000);

}catch(InterruptedException e) {

e.printStackTrace();

}

}public synchronized voidb(){

System.out.println("b");try{

Thread.sleep(8000);

}catch(InterruptedException e) {

e.printStackTrace();

}

}public static voidmain(String[] args) {

Demo demo= newDemo();//Demo demo1 = new Demo();

new Thread(newRunnable() {

@Overridepublic voidrun() {

demo.a();

}

}).start();new Thread(newRunnable() {

@Overridepublic voidrun() {

demo.b();

}

}).start();

}

}

运行结果:

a

b

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

packagecom.roocon.thread.t6;public classDemo {/*当第一个线程A拿到当前实例锁后,进入a方法,那么,线程A还能拿到被当前实例所加锁的另一个

同步方法b吗?是不是只有当线程A释放了a方法的同步锁后,才可以去获取b方法的同步锁呢?*/

public synchronized voida(){

System.out.println("a");try{

Thread.sleep(5000);

}catch(InterruptedException e) {

e.printStackTrace();

}

}public synchronized voidb(){

System.out.println("b");try{

Thread.sleep(8000);

}catch(InterruptedException e) {

e.printStackTrace();

}

}public static voidmain(String[] args) {

Demo demo= newDemo();

Demo demo1= newDemo();new Thread(newRunnable() {

@Overridepublic voidrun() {

demo.a();

}

}).start();new Thread(newRunnable() {

@Overridepublic voidrun() {

demo1.b();

}

}).start();

}

}

运行结果:

a

b

a b几乎是同时输出的。

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

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

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();

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

运行结果:

a

print b()

b

b

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

二、自旋锁

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

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("全部执行完毕");

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

运行结果:

全部执行完毕

Thread-0开始执行...

Thread-1开始执行...

Thread-1执行完毕了

Thread-0执行完毕了

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

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

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("全部执行完毕");

}

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

运行结果:

Thread-0开始执行...

Thread-1开始执行...

Thread-0执行完毕了

Thread-1执行完毕了

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

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("全部执行完毕");

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

运行结果:

Thread-0开始执行...

Thread-1开始执行...

Thread-1执行完毕了

Thread-0执行完毕了

全部执行完毕

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

三、模拟死锁

48304ba5e6f9fe08f3fa1abda7d326ab.png

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();

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

运行结果:

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

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

42231431e1ad8edc271ee60215965272.png

点击线程,检测死锁:

982f350aca6fe1c598e50f12e2142edb.png

参考资料:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java 中的 tryLock 方法可以用来实现自。tryLock 方法尝试获取,如果获取成功,则立即返回 true,否则返回 false。可以在循环中调用 tryLock,直到成功获取为止。这样就可以实现自的效果。 例如: ``` Lock lock = new ReentrantLock(); while(!lock.tryLock()){ //do something } try{ //critical section }finally{ lock.unlock(); } ``` 这样做的缺点是会占用CPU资源,如果竞争激烈的话会导致性能问题。 ### 回答2: Java中的tryLock()方法是java.util.concurrent.locks.ReentrantLock类中的一个方法,用于实现自。自是一种基于循环的,当线程尝试获取时,如果发现已被其他线程持有,则不会进等待状态,而是通过循环不断尝试获取,直到获取成功为止。 tryLock()方法可以尝试获取,如果当前没有被其他线程持有,则获取成功并返回true;如果已被其他线程持有,则获取失败,并立即返回false,不会阻塞线程。使用该方法可以避免线程进等待状态,减少线程切换的开销,提高程序的执行效率。 tryLock()方法还提供了载方法,可以设置超时时间,在限定的时间内尝试获取。如果超过指定的时间仍未获取到,则放弃获取,返回false。通过设置超时时间,可以防止线程长时间等待,避免可能的情况发生。 自在某些场景下可以提高程序的性能,特别是对于的竞争不激烈、持有的时间较短的情况。但是在一些高并发场景下,长时间的自可能会消耗大量的CPU资源,导致程序性能下降。因此,需要根据具体的业务场景来选择合适的机制。 综上所述,Java的tryLock()方法实现了自,通过不断尝试获取而不进等待状态,提高了程序的执行效率。但是需要注意在高并发场景下的使用,避免长时间的自带来的性能问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值