文章目录
- 死锁
 - 检查是否发生了死锁
 - 死锁的概念
 - 死锁产生的条件
 - 预防死锁
 - 解决死锁
 
- 活锁
 - 概念
 - 解决
 
- ReentrantLock
 - 概念
 - 可重入
 - 可打断
 - 可超时
 - 可设置公平锁
 - 条件变量
 
 
 
死锁
检查是否发生了死锁
jstack通过 线程栈快照 定位线程中出现长时间停顿的原因, jconsole 图像界面 检查是否发生了死锁
死锁的概念
多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。
 就是两个线程互相占有自己的锁, 却又尝试获取别人的锁, 导致进入死锁状态.
死锁产生的条件
-  
互斥:进程要求对所分配的资源进行排他性控制, 就是一段时间内某资源仅为一个进程所占用
 -  
请求和保持:当进程因请求资源而阻塞时, 对已获得的资源保持不放
 -  
不可剥夺:进程已经获得的资源在为使用完之前,不能剥夺,只能在使用完由自己释放
 -  
环路等待:发生死锁时, 必然存在一个进程–资源的环形链等待链,链中每一个进程已获得的资源同时被下一个进程所请求.
 
预防死锁
-  
破坏请求条件:资源一次性分配, 这样就不会再有请求了. 但是资源利用率会降低
 -  
破坏请求保持条件:进程运行前一次申请它所需要的全部资源, 在它的资源满足前, 不进行运行。一旦运行,资源将不可剥夺.
这种做法可能严重浪费资源,可能导致饥饿现象.个别线程长时间占用某个资源, 导致该资源的进程迟迟无法运行. -  
破坏不可剥夺条件:进程申请资源一旦被阻塞, 就必须释放已经占有的所有资源.
 -  
破坏环路等待条件:系统给每类资源赋予一个编号, 每一个进程按照编号递增的顺序请求资源, 释放则相反.
 
解决死锁
- 每个线程按照确定的顺序获取锁 , 这样就不会发生一直等待的情况. 但是可能出现饥饿的情况.
 - 使用tryLock方法固定时长等待锁, 在线程获取锁超时以后, 主动释放之前已经获得的所有锁.
 
活锁
概念
任务没有被阻塞, 但是由于某些条件没有满足, 导致一直重复尝试–失败–尝试–失败的过程, 处于活锁的实体不断的在改变状态, 活锁有可能自行解开的.
比如一个进程把一个数加到20就退出 , 一个数减到0就退出 ,他们两个一起执行就不会成功.
解决
解决活锁的一个简单的办法就是在下一次尝试获取资源之前, 随机休眠一段时间.
ReentrantLock
概念
可中断, 其他线程可以把竞争锁的线程打断, 去处理其他逻辑, 防止一直等待可设置超时时间, 规定时间内竞争不到锁就放弃, 不像synchroniezd的EntryList一样会一直竞争, 可解决死锁可设置公平锁,ReentrantLock默认不公平的,需要自己调用构造方法设置,预防饥饿的情况, 遵循先到先得的原则, 防止线程过多的情况 导致有些锁一直拿不到锁可支持多个条件变量, 就是多个类似于synchroniezd一样的waitset. 以后notifyAll的时候可以选择同一个条件的线程一起唤醒.
一般解决饥饿问题不使用公平锁, 会降低并发度, 应该使用tryLock(Time)方法, 一旦获取锁超时就放弃
可重入
同一个线程如果首次获取了这把锁, 那么因为它是这把锁的拥有者, 因此有权利再次获得这把锁.
 不可重入的话就会被锁住
public class ReentrantLockTest {
    private static ReentrantLock reentrantLock=new ReentrantLock();
    public static void main(String[] args) {
        reentrantLock.lock();
        try {
            System.out.println("进入m1");
            m1();
        }finally {
            reentrantLock.unlock();
        }
    }
    public static void m1(){
        reentrantLock.lock();
        try {
            System.out.println("进入m2");
            m2();
        }finally {
            reentrantLock.unlock();
        }
    }
    public static void m2(){
        reentrantLock.lock();
        try {
            System.out.println("已经在m2");
        }finally {
            reentrantLock.unlock();
        }
    }
}
 
可打断
public class ReentrantLockTest {
    private static ReentrantLock reentrantLock=new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            try {
                reentrantLock.lockInterruptibly();  //此锁可打断 , 如果仅仅是reentrantLock.lock, 则无法打断
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("锁被打断");
                return;
            }
            System.out.println("竞争锁成功,执行逻辑");
        });
        reentrantLock.lock(); //主线程获取锁, t1线程就只能竞争 ,并且设置可打断的竞争
        t1.start();
        TimeUnit.SECONDS.sleep(2);  
        t1.interrupt();       //打断
    }
}
 
可超时
public class ReentrantLockTest {
    private static ReentrantLock reentrantLock=new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            try {
                if (!reentrantLock.tryLock(5,TimeUnit.SECONDS)) { //竞争5s锁, 竞争不到返回false
                    System.out.println("5s了,竞争不到锁,执行其他逻辑");
                    return;
                }
                System.out.println("竞争到锁了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        reentrantLock.lock(); //主线程获取锁, t1线程就只能竞争
        t1.start();
    }
}
 
可设置公平锁
一般解决饥饿问题不使用公平锁, 会降低并发度, 应该使用tryLock(Time)方法, 一旦获取锁超时就放弃
 
条件变量
synchronized是所有不满足条件的线程都在一个WaitSet中等待,唤醒时就全部一起唤醒
而ReentrantLock支持多个专门等待不同条件的ConditionObject,类似于waitset,唤醒时按照特定条件的waitset进行唤醒
 
public class ReentrantLockTest {
    private static ReentrantLock reentrantLock=new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        Condition condition1 = reentrantLock.newCondition();     //休息室1
        Condition condition2 = reentrantLock.newCondition();     //休息室2
        Thread t1=new Thread(()->{
            try {
                condition1.await();     //线程1进入休息室1等待
                condition1.awaitNanos(10);  //根据纳秒等待
                condition1.awaitUninterruptibly();  // 可打断的等待
                condition1.signal();   //唤醒休息室1特定的一个
                condition1.signalAll();   //唤醒休息室2所有
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        Thread t2=new Thread(()->{
            try {
                condition2.await();     //线程2进入休息室2等待
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
    }
}
                


















