java死锁2_Java面试必问-死锁终极篇(2)

当使用synchronized关键词提供的内置锁时,只要线程没有获得锁,那么就会永远等待下去,然而Lock接口提供了boolean tryLock(long time, TimeUnit unit) throws InterruptedException方法,该方法可以按照固定时长等待锁,因此线程可以在获取锁超时以后,主动释放之前已经获得的所有的锁。通过这种方式,也可以很有效地避免死锁。

还是按照之前的例子,时序图如下:

a0a8c2f9880a559f2a899c32657a8089.png

时序图

下面系统阐述一下死锁的定义:所谓死锁,就是多个进程循环等待它方占有的资源而无限期的僵持下去的局面。这时,两个进程都被阻塞,并且保持下去,这种状况就是死锁(deadlock).。死锁是指多个进程在运行过程中,因为争夺资源而造成的一种僵局,如果没有外力推进,处于僵局中的进程就无法继续执行。

死锁条件里面的竞争资源,可以是线程池里的线程、网络连接池的连接,中数据引擎提供的锁,等等一切可以被称作竞争资源的东西。

用个例子来看看这个死锁的特征:

final ExecutorService executorService =

Executors.newSingleThreadExecutor();

Future f1 = executorService.submit(new Callable() {

public Long call() throws Exception{

System.out.println("start f1");

Thread.sleep(1000);//延时

Future f2 =

executorService.submit(new Callable() {

public Long call() throws Exception{

System.out.println("start f2");

return -1L;

}

});

System.out.println("result" + f2.get());

System.out.println("end f1");

return -1L;

}

});

0fe6b6334f15e25d0960c7bbebc8ec37.png

在这个例子中,线程池的任务1依赖任务2的执行结果,但是线程池是单线程的,也就是说任务1不执行完,任务2永远得不到执行,那么因此造成了死锁。原因图解如下:

a0a8c2f9880a559f2a899c32657a8089.png

线程池死锁

执行jstack命令,可以看到如下内容:

"pool-1-thread-1" prio=5 tid=0x00007ff4c10bf800 nid=0x3b03 waiting on condition [0x000000011628c000]

java.lang.Thread.State: WAITING (parking)

at sun.misc.Unsafe.park(Native Method)

- parking to wait for <0x00000007ea51cf40> (a java.util.concurrent.FutureTask$Sync)

at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)

at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)

at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:994)

at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1303)

at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:248)

at java.util.concurrent.FutureTask.get(FutureTask.java:111)

at com.test.TestMain$1.call(TestMain.java:49)

at com.test.TestMain$1.call(TestMain.java:37)

at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)

at java.util.concurrent.FutureTask.run(FutureTask.java:166)

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

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

可以看到当前线程wait在java.util.concurrent.FutureTask对象上。

解决办法:扩大线程池线程数 or 任务结果之间不再互相依赖。

作者开始在muduo/base中封装了和网络无关的一些操作,如日志、时间、队列、互斥量、条件变量、线程、线程池等,给后面网络库的设计带来了极大便利。同步嵌套是产生死锁的常见情景,从上面的代码中我们可以看出,当t1线程拿到锁a后,睡眠2秒,此时线程t2刚好拿到了b锁,接着要获取a锁,但是此时a锁正好被t1线程持有,因此只能等待t1线程释放锁a,但遗憾的是在t1线程内又要求获取到b锁,而b锁此时又被t2线程持有,到此结果就是t1线程拿到了锁a同时在等待t2线程释放锁b,而t2线程获取到了锁b也同时在等待t1线程释放锁a,彼此等待也就造成了线程死锁问题。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候,死锁会让你的程序挂起无法完成任务。

// 连接1

final MultiThreadedHttpConnectionManager connectionManager1 = new MultiThreadedHttpConnectionManager();

final HttpClient httpClient1 = new HttpClient(connectionManager1);

httpClient1.getHttpConnectionManager().getParams().setMaxTotalConnections(1); //设置整个连接池最大连接数

// 连接2

final MultiThreadedHttpConnectionManager connectionManager2 = new MultiThreadedHttpConnectionManager();

final HttpClient httpClient2 = new HttpClient(connectionManager2);

httpClient2.getHttpConnectionManager().getParams().setMaxTotalConnections(1); //设置整个连接池最大连接数

ExecutorService executorService = Executors.newFixedThreadPool(2);

executorService.submit(new Runnable() {

public void run(){

try {

PostMethod httpost = new PostMethod("http://www.baidu.com");

System.out.println(">>>> Thread A execute 1 >>>>");

httpClient1.executeMethod(httpost);

Thread.sleep(5000l);

System.out.println(">>>> Thread A execute 2 >>>>");

httpClient2.executeMethod(httpost);

System.out.println(">>>> End Thread A>>>>");

} catch (Exception e) {

// ignore

}

}

});

executorService.submit(new Runnable() {

public void run(){

try {

PostMethod httpost = new PostMethod("http://www.baidu.com");

System.out.println(">>>> Thread B execute 2 >>>>");

httpClient2.executeMethod(httpost);

Thread.sleep(5000l);

System.out.println(">>>> Thread B execute 1 >>>>");

httpClient1.executeMethod(httpost);

System.out.println(">>>> End Thread B>>>>");

} catch (Exception e) {

// ignore

}

}

});

整个过程图解如下:

a0a8c2f9880a559f2a899c32657a8089.png

连接池死锁

在死锁产生后,我们用jstack工具查看一下当前线程堆栈信息,可以看到如下内容:

"pool-1-thread-2" prio=5 tid=0x00007faa7909e800 nid=0x3b03 in Object.wait() [0x0000000111e5d000]

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

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

- waiting on <0x00000007ea73f498> (a org.apache.commons.httpclient.MultiThreadedHttpConnectionManager$ConnectionPool)

at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.doGetConnection(MultiThreadedHttpConnectionManager.java:518)

- locked <0x00000007ea73f498> (a org.apache.commons.httpclient.MultiThreadedHttpConnectionManager$ConnectionPool)

at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.getConnectionWithTimeout(MultiThreadedHttpConnectionManager.java:416)

at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:153)

at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)

at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)

at com.test.TestMain$2.run(TestMain.java:79)

at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)

at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)

at java.util.concurrent.FutureTask.run(FutureTask.java:166)

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

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

"pool-1-thread-1" prio=5 tid=0x00007faa7a039800 nid=0x3a03 in Object.wait() [0x0000000111d5a000]

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

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

- waiting on <0x00000007ea73e0d0> (a org.apache.commons.httpclient.MultiThreadedHttpConnectionManager$ConnectionPool)

at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.doGetConnection(MultiThreadedHttpConnectionManager.java:518)

- locked <0x00000007ea73e0d0> (a org.apache.commons.httpclient.MultiThreadedHttpConnectionManager$ConnectionPool)

at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.getConnectionWithTimeout(MultiThreadedHttpConnectionManager.java:416)

at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:153)

at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)

at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)

at com.test.TestMain$1.run(TestMain.java:61)

at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)

at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)

at java.util.concurrent.FutureTask.run(FutureTask.java:166)

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

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

当然,我们在这里只是一些极端情况的假定,假如线程在使用完连接池之后很快就归还,在归还连接数后才占用下一个连接池,那么死锁也就不会发生。

在我的理解当中,死锁就是“两个任务以不合理的顺序互相争夺资源”造成,因此为了规避死锁,应用程序需要妥善处理资源获取的顺序。

聊到计算视觉及时下对这项技术的主流应用,黄博士说“计算视觉是一个从二维世界往三维世界的一个逆过程,实际上是一个病态问题。即官方rar应用自带的性能测试,测试模拟解压缩环境下cpu的处理速度(因为规避闪存影响没有实际调用i/o),亮点是可以随意设定线程数,耗时较短只有1分钟而且不在很多厂商的白名单里面。在实际进行中,针对当前酒店婚宴上菜过程中所存在的问题,需要做到:在上菜时,需要与司仪提前做好沟通,当司仪宣布婚宴开始后,此时厨师长就需要及时下达命令开始上菜,相应传菜生提前做好准备,一声令下后就要依次分区进行上菜。

本文来自电脑杂谈,转载请注明本文网址:

http://www.pc-fly.com/a/jisuanjixue/article-110757-2.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值