美文网首页JUC并发相关
5. 并发终结之线程活性故障

5. 并发终结之线程活性故障

作者: 涣涣虚心0215 | 来源:发表于2020-10-01 00:02 被阅读0次

线程除了上下文切换导致RUNNABLE和非RUNNABLE状态的切换,还有程序自身错误导致线处于RUNNABLE但是任务没有进展,这种现象称为线程活性故障
常见的活性故障:

  • 死锁:死锁典型场景一个线程X持有锁A的情况下继续申请锁B,同时另一个线程Y在持有锁B的情况下,申请持有锁A。这个时候线程A和线程B都进入BLOCK状态。
class Test{
    private final Object object1 = new Object();
    private final Object object2 = new Object();
    public void test1() throws InterruptedException {
        synchronized (object1){
            System.out.println(Thread.currentThread().getName()+"==="+"Take lock 1....");
            Thread.sleep(2000);
            synchronized (object2){
                System.out.println(Thread.currentThread().getName()+"==="+"Take lock 2...");
            }
        }
    }
    public void test2()throws InterruptedException {
        synchronized (object2){
            System.out.println(Thread.currentThread().getName()+"==="+"Take lock 2....");
            Thread.sleep(2000);
            synchronized (object1){
                System.out.println(Thread.currentThread().getName()+"==="+"Take lock 1...");
            }
        }
    }
}

除此特征之外,死锁还有另外一种特征,比如ClassA有同步方法synchronizedA()和synchronizedB(),ClassB有同步方法synchronizedC()和synchronizedD()。如果synchronizedA()方法调用了synchronizedC(),而synchronizedD()调用了synchronizedB(),这时候一个线程执行ClassA.SynchronizedA()时,另一个线程执行ClassB.SynchronizedD(),那么这两个线程也有可能产生死锁。

class ClassA {
    private ClassB classB;
    public ClassB getClassB() {
        return classB;
    }
    public void setClassB(ClassB classB) {
        this.classB = classB;
    }
    public synchronized void testA1() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + "===" + "Take A1....");
        classB.testB1();
    }
    public synchronized void testA2() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + "===" + "Take A2...");
    }
}

class ClassB {
    private ClassA classA;
    public ClassA getClassA() {
        return classA;
    }
    public void setClassA(ClassA classA) {
        this.classA = classA;
    }
    public synchronized void testB1() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + "===" + "Take B1....");
    }
    public synchronized void testB2() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + "===" + "Take B2...");
        classA.testA2();
    }
}
------------------
main
Thread thread1 = new Thread(() -> {
    try {
        classA.testA1();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
Thread thread2 = new Thread(() -> {
    try {
        classB.testB2();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
  • 锁死:锁死即没有相应的通知线程来唤醒等待线程,比如Object.wait()/Conditional.await()前面没有对保护条件进行判断,所以Object.wait()/Conditional.await()最好放在循环里;另外CountDownLatch.countDown()方法没有放在finally里面使得CountDownLatch.await()一直初一等待状态
    锁死的类型有一下几种:
    1 - 信号丢失锁死:由于没有相应的通知线程来唤醒等待线程而使等待线程一直处于等待状态的一种活性故障
    2 - 嵌套监视器锁死:嵌套锁导致等待线程永远无法被唤醒的一种活性故障。
    image.png

受保护方法monitorY.wait()会释放锁,但是释放的是monitorY对应的锁,而monitorX锁还是一直占着(只有monitorY被唤醒才能释放monitorX);此时通知线程需要调用monitorY.notifyAll()能够唤醒等待线程需要持有相应的锁monitorY,但是monitorY的临界区是在monitorX里面,因此通知线层也需要持有monitorX的锁(此时被等待线程占用),导致通知线程无法调用monitorY.notifyAll()来唤醒等待线程。

class ClassA {
    private BlockingQueue queue = new ArrayBlockingQueue(1);
    private int processed = 0;
    private int accepted = 0;

//如果doProcess()正好take()阻塞了,则需要queue.put()生产者来唤醒,但是Synchronized锁被占用,导致accept
//获取不到锁,不能进行唤醒。
    public synchronized void doProcess() throws InterruptedException {
        String msg = (String)queue.take();
        System.out.println("==="+msg);
        processed ++;
    }
    public synchronized void accept(String msg) throws InterruptedException {
        queue.put(msg);
        accepted ++;
    }
}
----------------------------
 class Test {
    public static void main(String[] args) {
        Object monitorA = new Object();
        SharedObject sharedObject = new SharedObject();
        new Thread(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " start");
            synchronized (monitorA) {
                System.out.println(Thread.currentThread().getName() + " get lock a");
                while (!sharedObject.isFlag()) {
                    synchronized (sharedObject) {
                        System.out.println(Thread.currentThread().getName() + " get lock b");
                        try {
                            System.out.println(Thread.currentThread().getName() + " before wait");
                            sharedObject.wait();
                            System.out.println(Thread.currentThread().getName() + " after wait");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(Thread.currentThread().getName() + " after monitor b");
                }

            }
        }, "waitThread").start();
        new Thread(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " start");
            synchronized (monitorA) {
                System.out.println(Thread.currentThread().getName() + " get lock a");
                synchronized (sharedObject) {
                    System.out.println(Thread.currentThread().getName() + " get lock b");
                    sharedObject.setFlag(true);
                    sharedObject.notify();
                    System.out.println(Thread.currentThread().getName() + " after notify");
                }
            }
        }, "notifyThread").start();
    }
}
class SharedObject {
    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
    private boolean flag = false;
}
=========从下面图可以看到notifyThread是处于BLOCKED状态,waitThread是处于WAITING状态。
waitThread start
waitThread get lock a
waitThread get lock b
waitThread before wait
notifyThread start
image.png
  • 活锁:活锁指线程一直处于RUNNING状态,但是其他任务无法进展的一种活性故障;其他线程持续重试获得锁失败,导致无法运行。
  • 饥饿:一个(或多个)线程始终无法获得其所需的资源,不会导致死锁,无限延迟(比如线程优先级低的一直不能跑)
  • 锁泄漏:一个线程获得锁后,由于程序错误,使得该锁一直得不到释放而其他线程无法获得锁的现象。

相关文章

网友评论

    本文标题:5. 并发终结之线程活性故障

    本文链接:https://www.haomeiwen.com/subject/eiljlktx.html