随手记android面试题,synchorinzed的一个面试题

背景

前两天一个朋友面试,遇到了一个题目

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21import java.util.LinkedList;

public class Stack{

LinkedList list = new LinkedList();

public synchronized void push(Object x){

synchronized(list) {

list.addLast( x );

notify();

}

}

public synchronized Object pop()

throws Exception{

synchronized(list) {

if( list.size() <= 0 ) {

wait();

}

return list.removeLast();

}

}

}

上面的代码,一般情况下工作正常,问什么时候会有问题,并且问题的根源是什么

现象

既然这么问,那么肯定是有问题的,下面就写代码复现一下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31public static void main(String[] args)

{

Stack stack = new Stack();

new Thread(() -> {

System.out.println("push thread" + Thread.currentThread().getName());

while (true)

{

try

{

stack.push("asd");

} catch (Exception e)

{

System.out.println("sd");

}

}

}).start();

new Thread(() -> {

System.out.println("pop thread" + Thread.currentThread().getName());

while (true)

{

try

{

stack.pop();

} catch (Exception e)

{

e.printStackTrace();

}

}

}).start();

}

果不其然,代码跑起来之后就卡主了。dump了一下进程,发现是出现了死锁

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20"Thread-1" #12 prio=5 os_prio=31 tid=0x00007f8848873000 nid=0x5803 in Object.wait() [0x000070000a55b000]

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

- waiting on <0x000000076ada6460> (a Stack)

at java.lang.Object.wait(Object.java:502)

at Stack.pop(Stack.java:32)

- locked <0x000000076ada6610> (a java.util.LinkedList)

- locked <0x000000076ada6460> (a Stack)

at Stack.lambda$main$1(Stack.java:69)

at Stack$$Lambda$2/381259350.run(Unknown Source)

at java.lang.Thread.run(Thread.java:748)

"Thread-0" #11 prio=5 os_prio=31 tid=0x00007f884701c800 nid=0x5603 waiting for monitor entry [0x000070000a458000]

java.lang.Thread.State: BLOCKED (on object monitor)

at Stack.push(Stack.java:14)

- waiting to lock <0x000000076ada6610> (a java.util.LinkedList)

- locked <0x000000076ada6460> (a Stack)

at Stack.lambda$main$0(Stack.java:53)

at Stack$$Lambda$1/1607521710.run(Unknown Source)

at java.lang.Thread.run(Thread.java:748)

pop线程持有list的锁,等待获取到stack的锁,而push线程持有stack的锁,等待获取list的锁,因此就出现了死锁导致卡主。

而这种情况的产生,是因为在代码中调用了wait(),因为没有指明对象,所以默认的就是对当前对象执行wait()。因为我们在pop方法中用了两个synchronized,其实就是拿了两个锁,一个是当前对象的锁,一个是list的锁。直接调用wait,释放了this对象的锁,然后push线程拿到了,但是因为pop线程调用了wait之后挂起来了,所以list的锁也就没有释放掉。现在就属于pop等待this对象的锁,而push持有this对象,等待list的锁,因此死锁就产生了。

如果把wait改成list.wait(),会是下面的清空

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19"Thread-1" #15 prio=5 os_prio=0 cpu=31.25ms elapsed=4.21s tid=0x0000019b436f9000 nid=0x1a5c in Object.wait() [0x0000005c5b0fe000]

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(java.base@13.0.2/Native Method)

- waiting on <0x00000006206104b0> (a java.util.LinkedList)

at java.lang.Object.wait(java.base@13.0.2/Object.java:326)

at Stack.pop(Stack.java:19)

- locked <0x00000006206104b0> (a java.util.LinkedList)

- locked <0x0000000620610240> (a Stack)

at Stack.lambda$main$1(Stack.java:36)

at Stack$$Lambda$16/0x0000000800ba5040.run(Unknown Source)

at java.lang.Thread.run(java.base@13.0.2/Thread.java:830)

"Thread-0" #14 prio=5 os_prio=0 cpu=0.00ms elapsed=4.21s tid=0x0000019b436f7000 nid=0x453c waiting for monitor entry [0x0000005c5affe000]

java.lang.Thread.State: BLOCKED (on object monitor)

at Stack.push(Stack.java:8)

- waiting to lock <0x0000000620610240> (a Stack)

at Stack.lambda$main$0(Stack.java:29)

at Stack$$Lambda$15/0x0000000800ba4440.run(Unknown Source)

at java.lang.Thread.run(java.base@13.0.2/Thread.java:830)

情况刚好相反,pop等待list的锁,push等待stack的锁。

因此如果想要避免这种情况,需要把pop函数上面的synchronized去掉,只保留一个锁即可

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值