美文网首页
多线程之死锁,活锁。

多线程之死锁,活锁。

作者: 你弄啥来 | 来源:发表于2019-08-06 00:59 被阅读0次

死锁的定义

集合中每一个进程都在等待只能由本集合中的其他进程才能引发的事件那么该进程就是死锁的。java中指的就是多个线程在竞争多个资源的时候就可能出现死锁现象。

常见的死锁

简单顺序死锁: 如两个线程在执行不同的方法时,方法1是先锁了A然后去争取获得B,而方法2是先去锁了B再去获得锁A。这时如果有两个线程,一个线程执行了方法1,另外一个线程执行方法2的时候。这个时候就会出现死锁的情况。
代码演示:

package com.chen.springboot.thread.dlock;

public class SimpleOrderDeadLock {


    private static Object aLock = new Object();

    private static Object bLock = new Object();


    public static void getAthenB(){
        synchronized (aLock){
            System.out.println(Thread.currentThread()+"获得了aLock 开始获得bLock");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (bLock){
                System.out.println(Thread.currentThread()+"获得了bLock");
            }
        }
    }

    public static void getBthenA(){
        synchronized (bLock){

            System.out.println(Thread.currentThread()+"获得了aLock 开始获得bLock");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (aLock){
                System.out.println(Thread.currentThread()+"获得了bLock");
            }
        }
    }
    static class Athread extends Thread{
        @Override
        public void run() {
            getBthenA();
        }
    }
    static class Bthread extends Thread{
        @Override
        public void run() {
            getAthenB();
        }
    }
    public static void main(String[] args) {
        Athread a = new Athread();
        Bthread b = new Bthread();
        a.start();
        b.start();
    }
}

使用java内存分配工具可以看到两个线程都已经持有了对方的锁,而且还都在等待对方的锁


image.png
动态顺序死锁

相比简单殊勋死锁,可以直接修改锁对象的顺序就能够完美解决死锁的情况。但是动态顺序死锁就不一定了,比如还是两个锁对象。两个对象A和B是通过参数传递过来的时候,这个时候如果先要先锁A,再锁B.一个线程来的时候是没有问题的,但是这个时候如果两一个线程执行方法时传的参数先是B再是A的时候,还是会出现死锁。代码演示:

package com.chen.springboot.thread.dlock.impl;

import com.chen.springboot.thread.dlock.DynamicParamLock;

public class DynamicDeadLock implements DynamicParamLock {

    @Override
    public void doSomething(Object a, Object b) {
        synchronized (a){
            System.out.println(Thread.currentThread().getName()+"先拿到了的所对象是"+a);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (b){
                System.out.println("又拿到了所对象是"+b);
            }
        }
    }


    public static void main(String[] args) {
        Object a = new Object();
        Object b = new Object();
        DynamicParamLock dynamicParamLock = new DynamicDeadLock();

        Rthread r1 = new Rthread(a,b,dynamicParamLock);
        Rthread r2 = new Rthread(b,a,dynamicParamLock);
        r1.start();
        r2.start();
    }


    static class Rthread extends Thread{
        private Object a;
        private Object b;
        private DynamicParamLock paramLock;

        public Rthread(Object a, Object b, DynamicParamLock paramLock) {
            this.a = a;
            this.b = b;
            this.paramLock = paramLock;
        }
        @Override
        public void run() {
           paramLock.doSomething(a,b);
        }
    }

}

如图所示还是会出现死锁状态:


image.png

解决方法 1 将本来动态资源顺序会改变的情况改通过一种机制改成固定的顺序。

可以通过比较两个对象的hash值,判断大小来指定锁资源的顺序

package com.chen.springboot.thread.dlock.impl;

import com.chen.springboot.thread.dlock.DynamicParamLock;

public class DeterminOrderLock implements DynamicParamLock {

    private static Object tieLock = new Object();


    @Override
    public void doSomething(Object a, Object b) {
      //通过确定两个资源内存内部的hash值来进行加锁,
        int ahash = System.identityHashCode(a);
        int bhash = System.identityHashCode(b);
        if(ahash>bhash){
            synchronized (a){
                System.out.println(Thread.currentThread().getName()+"先拿到了锁对象为"+a);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b){
                    System.out.println(Thread.currentThread().getName()+"拿到了锁对象"+b);
                }
            }
        }else if(ahash<bhash){
            synchronized (b){
                System.out.println(Thread.currentThread().getName()+"先拿到了锁对象为"+b);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (a){
                    System.out.println(Thread.currentThread().getName()+"先拿到了锁对象为"+a);
                }
            }
        }else{
            synchronized (tieLock){//由于hash值本身就是一个hash 映射会出现hash 碰撞的现象所以在判断完hash值的基础上在让两个线程竞争同一个资源这样就避免了hash出现的问题
                synchronized (a){
                    System.out.println(Thread.currentThread().getName()+"先拿到了锁对象为"+a);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (b){
                        System.out.println(Thread.currentThread().getName()+"拿到了锁对象"+b);
                    }
                }
            }
        }
    }
}

最终执行的结果中可以看到没有出现死锁的情况:

Thread-1先拿到了锁对象为java.lang.Object@d156cd4
Thread-1拿到了锁对象java.lang.Object@3d29306
Thread-0先拿到了锁对象为java.lang.Object@d156cd4
Thread-0先拿到了锁对象为java.lang.Object@3d29306

解决方法 2 定义的对象中持有ReentranceLock ,通过ReentranceLock中的tryLock的方式来获取锁.

所需上锁的对象有一个ReentranceLock

package com.chen.springboot.thread.dlock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockObject {
    private final Lock lock = new ReentrantLock();
    public Lock getLock() {
        return lock;
    }
}

执行方法的时候调用lock.tryLock方法来实现

package com.chen.springboot.thread.dlock.impl;

import com.chen.springboot.thread.dlock.DynamicParamLock;
import com.chen.springboot.thread.dlock.LockObject;

import java.util.Random;

public class UserReentranceLock implements DynamicParamLock {
    @Override
    public void doSomething(Object a, Object b) {
        LockObject aLock = (LockObject)a;
        LockObject bLock = (LockObject)b;
        Random r = new Random();
        while (true){
            if(aLock.getLock().tryLock()){
                try {
                    System.out.println(Thread.currentThread().getName()+"获得的锁资源为"+aLock);
                    if(bLock.getLock().tryLock()){
                        try {
                            System.out.println(Thread.currentThread().getName()+"获得的锁资源"+bLock);
                            break;
                        }finally {
                            bLock.getLock().unlock();
                        }
                    }
                }finally {
                    aLock.getLock().unlock();
                }
            }  
        }
    }
}

但是通过这种当时调用的时候存在活锁的情况

Thread-1获得的锁资源为com.chen.springboot.thread.dlock.LockObject@7a024d7
Thread-0获得的锁资源为com.chen.springboot.thread.dlock.LockObject@61a09c6b
Thread-1释放了锁资源com.chen.springboot.thread.dlock.LockObject@7a024d7
Thread-0释放了锁资源com.chen.springboot.thread.dlock.LockObject@61a09c6b
Thread-1获得的锁资源为com.chen.springboot.thread.dlock.LockObject@7a024d7
Thread-0获得的锁资源为com.chen.springboot.thread.dlock.LockObject@61a09c6b
Thread-1释放了锁资源com.chen.springboot.thread.dlock.LockObject@7a024d7
Thread-0释放了锁资源com.chen.springboot.thread.dlock.LockObject@61a09c6b
Thread-1获得的锁资源为com.chen.springboot.thread.dlock.LockObject@7a024d7
Thread-0获得的锁资源为com.chen.springboot.thread.dlock.LockObject@61a09c6b
Thread-1释放了锁资源com.chen.springboot.thread.dlock.LockObject@7a024d7
Thread-0释放了锁资源com.chen.springboot.thread.dlock.LockObject@61a09c6b
Thread-1获得的锁资源为com.chen.springboot.thread.dlock.LockObject@7a024d7
Thread-0获得的锁资源为com.chen.springboot.thread.dlock.LockObject@61a09c6b
Thread-1释放了锁资源com.chen.springboot.thread.dlock.LockObject@7a024d7
Thread-0释放了锁资源com.chen.springboot.thread.dlock.LockObject@61a09c6b
Thread-1获得的锁资源为com.chen.springboot.thread.dlock.LockObject@7a024d7
Thread-0获得的锁资源为com.chen.springboot.thread.dlock.LockObject@61a09c6b
Thread-1释放了锁资源com.chen.springboot.thread.dlock.LockObject@7a024d7
Thread-0释放了锁资源com.chen.springboot.thread.dlock.LockObject@61a09c6b
Thread-0获得的锁资源为com.chen.springboot.thread.dlock.LockObject@61a09c6b
Thread-0获得的锁资源com.chen.springboot.thread.dlock.LockObject@7a024d7
Thread-0释放了锁资源com.chen.springboot.thread.dlock.LockObject@7a024d7
Thread-0释放了锁资源com.chen.springboot.thread.dlock.LockObject@61a09c6b
Thread-1获得的锁资源为com.chen.springboot.thread.dlock.LockObject@7a024d7
Thread-1获得的锁资源com.chen.springboot.thread.dlock.LockObject@61a09c6b
Thread-1释放了锁资源com.chen.springboot.thread.dlock.LockObject@61a09c6b
Thread-1释放了锁资源com.chen.springboot.thread.dlock.LockObject@7a024d7

通过打印出来的信息可以看出两个线程,要么同时获得了锁资源,要么同时释放了锁资源。直到最后的时候Thread0才实现了获得第一个锁资源之后,又获得了第二个锁资源才能够最终完成。

活锁的定义: 多个线程在获取锁的时候,再同时为了获取另外一个锁的时候发现锁已经被其他线程占有,这是持有锁的线程会去释放该锁,然后再去重新获取该锁,这个过程就称为活锁反复地去获取和释放锁的过程。

解决方式 添加一个阻塞在while(true)方法最后添加Thread.sleep(10)即可。

相关文章

  • Java面试:多线程中的各种锁,你了解几个?

    学习 java 多线程时,最头疼的知识点之一就是 java 中的锁了,什么互斥锁、排它锁、自旋锁、死锁、活锁等等,...

  • 线程同步中的死锁

    何为死锁 多线程各自持有不同的锁,并互相试图获取对方已持有的锁,导致无限等待的状况,称为死锁。比如: 避免死锁 避...

  • 多线程之死锁,活锁。

    死锁的定义 集合中每一个进程都在等待只能由本集合中的其他进程才能引发的事件那么该进程就是死锁的。java中指的就是...

  • 死锁-活锁

    死锁大家都知道,但是 有没有老铁 知道活锁呢?我在看《并发编程实战》的时候 了解到这个名次 活锁 活锁 是指 活锁...

  • 并发编程情况下几个相应问题简介

    1.并发编程的挑战之死锁 ​ 死锁是两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多...

  • 5. 死锁

    线程死锁 死锁是两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序...

  • 多线程-锁(死锁)

    每次看,都不懂,然后细看又懂了,再之后又忘记,就又不懂了。貌似屏幕飘过一段对话:?:我懂了?:不,你不懂?:因为记...

  • 6. 使用synchronized实现死锁

    死锁定义 死锁是两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序...

  • 死锁

    线程饥饿死锁 锁顺序死锁 动态锁顺序死锁通过锁顺序来避免死锁 避免死锁

  • 高并发编程-05-活跃性问题

    死锁,饥饿,活锁 1,死锁 多个线程,各自占对方的资源,都不愿意释放,从而造成死锁 工具:使用jconsole可以...

网友评论

      本文标题:多线程之死锁,活锁。

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