7. 死锁(概念,产生条件,避免)
7.1介绍
线程死锁是指两个或者多个线程互相持有对方所需要的资源,导致线程处于等待状态。当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁
7.2产生的条件
1) 互斥条件:进程对于分配到的资源具有排它性,即一个资源只能被一个进程占用,直到该线程释放资源。
2) 请求和保持条件:对于占用资源发生阻塞时,对已获得的资源不释放。
3) 不剥夺条件:任何一个资源没被该进程释放时,其他任何进程无法剥夺其资源。
循环条件:当发生死锁时,所有等待进程行程一个回环,造成永久阻塞
7.3避免
1) 加锁顺序,必须事先知道所有会用到的锁。
2) 加锁时限,tryLock(long timeout, TimeUnit unit) 的方法(ReentrantLock、ReentrantReadWriteLock)加上一个超时时间,若一个线程在指定时间没有成功获得所需要的锁,则会进行回退并释放所有已获得的资源。
3) 尽量使用 Java. util. concurrent 并发类代替自己手写锁
4) 尽量降低锁的使用粒度,尽量不要几个功能用同一把锁
5) 尽量减少同步的代码块
6) 死锁检测,死锁检测即每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中
8. 线程相关类
8.1ThreadLocal
1) 它并不是一个线程,而是一个可以在每个线程中存储数据的数据存储类,通过它可以在指定线程中存取数据,用于简化多线程编程时的并发访问,很简洁的隔离多线程程序的竞争资源。
2) 多于多线程资源共享问题,同步机制采用以时间换空间的策略,而这个变量使用空间换时间,后者是为每一个线程提供一个变量,因此可以同时访问而互不影响。
3) ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
4) 方法:get返回此线程局部变量的当前线程中的副本中的值,如果是第一次调用该方法则创建并初始化此副本;initialValue返回此线程初始化的值。
5) 使用场景:是数据库连接和 session 管理。
若多个线程之间需要共享资源,以达到线程间的通信时,就使用同步机制;若仅仅需要隔离多线程之间的关系资源,则可以使用ThreadLocal
8.2使用
1) 日期转换类SimpleDateFormat非线程安全,由于内部的局部变量calendar会更改。
2) 可使用ThreadLocal让SimpleDateFormat变量变成线程独享,进行线程安全
private static final String date_format = "yyyy-MM-dd HH:mm:ss";
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>(); public static DateFormat getDateFormat()
{
DateFormat df = threadLocal.get();
if(df==null){
df = new SimpleDateFormat(date_format);
threadLocal.set(df);
}
return df;
}