多线程的四种实现方式
1.继承Thread类,重写run方法
2.实现Runnable接口,重写run方法
3.实现Callable接口
4.实现线程池
引用阿里对线程的建议:`[强制]线程资源必须通过线程池提供,不允许在应用中自行显式创建线程`
说明: 使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题; 如果不适用线程池, 有可能造成系统创建大量同类线程而导致耗尽内存或者"过度切换"的问题
[强制]线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险.
死锁产生的必要条件
产生死锁必须同时满足一下四个条件
-
互斥条件:
进程要求对所分配的资源进行排他性控制。即在一段时间内,某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
-
不剥夺条件:
进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走。即只能由获得该进程的进程自己来释放(只能是主动释放)
-
请求和保持条件:
进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
-
循环等待条件:
存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被链中下一个进程所请求。即存在一个处于等待状态的进程集合{P1, P2, ..., Pn},其中Pi等待的资源被P(i+1)占有(i=0,1,2,3,...,n-1),Pn等待的资源被P0占有,构成循环等待。
避免死锁
- 加锁顺序(线程按照一定的顺序加锁)
- 加锁时限(线程尝试获取锁的时候加上一定的时限,超时则放弃对该锁的请求,并释放自己占有的锁)
- 死锁检测(每当一个线程获得了锁,在线程和锁相关的数据结构【如:map、graph等】中将其记下。当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。)
死锁解决方案
给线程设置优先级,让一个(或几个)线程回退,剩下的线程就像没发生死锁一样继续保持着它们需要的锁。(建议设置随机的优先级,避免固定不变的优先级导致某些线程总是拥有最高的优先级)