并发的两个‘老’问题
上一篇文章我们认识了什么是并发编程,随着并发编程而来的则是并发带来的各种问题,死锁和资源问题,那这一篇我们继续跟着作者去看一看这两个‘老‘问题。
死锁
我理解的死锁是指 “无外界条件干预无法自行解决的一种状态”
这种状态一旦出现会导致程序逻辑问题,资源占用问题等很严重的问题。
下面这段代码截自书中的代码, 演示了死锁的场景
代码语言:javascript复制public class DeadLockDemo {
privat static String A = "A";
private static String B = "B";
public static void main(String[] args) {
new DeadLockDemo().deadLock();
}
private void deadLock() {
Thread t1 = new Thread(new Runnable() {
@Override
publicvoid run() {
synchronized (A) {
try { Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println("1");
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
publicvoid run() {
synchronized (B) {
synchronized (A) {
System.out.println("2");
}
}
}
});
t1.start();
t2.start();
}
}
当出现死锁的时候, 线上的业务是可以反馈出来的, 同时我们通过线程 dump 是可以查询到具体的死锁地方.
当我们知道什么是死锁问题后, 那就应该在开发阶段首先去考虑并且避免这种场景
- 避免一个线程获取多个锁
- 避免一个线程在锁内占用多个资源
- 如果可以的话尝试使用 tryLock(time) 定时锁
- 数据库锁, 加锁和解锁在一个连接, 否则可能出现解锁失败
资源
什么是资源,对于软件程序来说资源就是
- 网络带宽
- 内存容量
- cpu
- ······
问 :那什么情况会出现资源问题呢?
答 :不合理的分配情况下
怎么做才能做到合理分配是我们在做程序设计时需要考虑的,比如软件运行的网络环境,磁盘大小,内存大小,系统的cpu能力、核心数等等
一般在资源分配上遵循两种任务情况来决定,1 是 IO密集型任务 2 是 CPU密集型任务。再简单一点就是你的任务是需要数据的还是需要计算的。需要数据的就是IO,需要计算的就是CPU, 根据这两种情况来指定我们的资源分配。推荐一篇文章https://blog.csdn.net/youanyyou/article/details/78990156
合理的资源分配,可以将系统的性能发挥的更好,反之更坏。所以线程不是创建的越多越好。
线程池的线程复用,根据资源合理的分配线程数,都可以帮我们有效的解决资源问题。线程数的大小设置。
建议
CPU密集型任务,最佳线程数设置为 CPU数 1
IO密集型任务, 最佳线程数目 = ((线程等待时间 线程CPU时间)/ 线程CPU时间 )* CPU数目