JAVA并发编程(六)-ReentrantLock

🐠一、多把锁

代码:

public class ManyLocks {

    public static void main(String[] args) {


        BigRoom bigRoom = new BigRoom();
        new Thread(() -> {
            bigRoom.study();
        },"小女").start();


        new Thread(() -> {
            bigRoom.sleep();
        },"小南").start();




    }
}

@Slf4j(topic = "BigRoom")
class BigRoom
{

    private final Object studyRoom=new Object();

    private final Object bedRoom=new Object();

    public void study(){
        synchronized (studyRoom) {
            log.debug("study 2小时");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public void sleep(){
        synchronized (bedRoom) {
            log.debug("sleeping 2小时");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

结果:
在这里插入图片描述

  这里我们可以看到小南小女分别在两间房间里头,小南在睡觉,小女在学习,这就会存在多把锁的情况。

🐟二、死锁

1、死锁演示

  有这样的情况:一个线程需要同时获得多把锁,这时就容易发生死锁。

  例如t1线程获得A对象的锁,接下来又想获取B对象的锁。
  t2线程获得到B对象的锁,接下来又想获取A对象的锁。

同样是小南小女,我们来看一下他们的这种情况。

public class ManyLocks {

    public static void main(String[] args) {


        BigRoom bigRoom = new BigRoom();
        new Thread(() -> {
            bigRoom.study();
        },"小女").start();


        new Thread(() -> {
            bigRoom.sleep();
        },"小南").start();




    }
}

@Slf4j(topic = "BigRoom")
class BigRoom
{

    private final Object studyRoom=new Object();

    private final Object bedRoom=new Object();

    public void sleep(){
        synchronized (studyRoom) {
            log.debug("study 2小时");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
                synchronized (bedRoom){
                    log.debug("sleeping 2小时");
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                }

        }
    }

    public void study(){
        synchronized (bedRoom) {
            log.debug("sleeping 2小时");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            synchronized (studyRoom){
                log.debug("study 2小时");

                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        }
    }
}

结果:
在这里插入图片描述
小女线程在学习了两个小时后,想去bedroom睡觉,却发现小南在bedroom当中占用着锁。而小南在bedroom睡觉两个小时之后,想去studyroom学习,却发现小女在studyroom中学习,并且占用着锁。这样就会导致小南和小女各自占用着锁都不释放,从而导致线程一直无法结束,造成死锁的现象。

2、定位死锁

首先我们打开jconsole
在这里插入图片描述
然后我们连接到当前正在跑的进程,我们可以点击检测死锁
在这里插入图片描述

🐬三、活锁

活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束。

@Slf4j(topic = "TestLiveLock")
public class TestLiveLock {

    static volatile int count=10;

    public static void main(String[] args) {
        new Thread(() -> {
            while(count>0){
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                count--;
                log.debug("count:{}",count);
            }
        },"t1"
                ).start();

        new Thread(() -> {
            while(count<20){
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                count++;

                log.debug("count:{}",count);
            }
        },"t2"
        ).start();
    }

}

执行结果:
在这里插入图片描述
我们看到这里我们有t1和t2两个线程,当count>0的时候t1线程一直在减一,当count<20的时候t2线程一直在加一,这样就会导致两个线程一直都无法结束。这就是活锁。

🐳 四、ReentrantLock简介

  在Java中,ReentrantLock 是一个可重入的互斥锁,它提供了与synchronized关键字类似的线程同步机制。相对于synchronized块,ReentrantLock 提供了更灵活的锁定机制,并支持更多的功能。
相对于synchronized它具备如下特点
1、可中断
2、可以设置超时时间
3、可以设置为公平锁
4、支持多个条件变量

与synchronized一样,都支持可重入

语法:

//获取锁
 lock.lock();
    try {
//临界区
    } finally {
       //释放锁
        lock.unlock();
    }

🐋五、ReentrantLock可重入

演示代码:

@Slf4j(topic = "TestReentrantLock")
public class TestReentrantLock {


    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {

        lock.lock();
        try {

            log.debug("进入主方法");

            m1();
        } finally {
            lock.unlock();
        }
    }

    public static void m1() {
        lock.lock();
        try {
            log.debug("进入m1");
            m2();
        } finally {
            lock.unlock();
        }
    }

    public static void m2() {
        lock.lock();

        try {
            log.debug("进入m2");

        } finally {
            lock.unlock();
        }
    }
}

执行结果:
在这里插入图片描述

🦈六、ReentrantLock可打断

演示代码:

@Slf4j(topic = "TestReentrantLock")
public class TestReentrantLock {


    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            try {
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                log.debug("老子不等了");
                return;
            }


            try {
                log.debug("获取到锁了");
            } finally {
                lock.unlock();
            }
        }, "t1");

        new Thread(() -> {
            log.debug("我拿到锁了");
            lock.lock();
        },"t2").start();


        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        t1.start();

        t1.interrupt();


    }}

结果:
在这里插入图片描述
这里可以看到t2线程一直拿着lock的锁不释放,当主线程去打断t1线程的时候就直接不等了,结束了t1线程。这里需要注意的是lock.lockInterruptibly();需要用try catch来进行包裹,这里catch处理的是当受到外部打断的时候,t1线程应该执行的动作。

🐊七、ReentrantLock锁超时

1、tryLock()尝试获取锁

@Slf4j(topic = "TestReentrantLock")
public class TestReentrantLock {


    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {

         new Thread(() -> {

            log.debug("尝试获取锁");
            if (!lock.tryLock()) {
                log.debug("获取不到锁");
                return;
            }
            try {
                log.debug("获取到锁了");
            } finally {
                lock.unlock();
            }


        }, "t1").start();


    }
}

在这里插入图片描述

@Slf4j(topic = "TestReentrantLock")
public class TestReentrantLock {


    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {

        Thread thread = new Thread(() -> {

            log.debug("尝试获取锁");
            if (!lock.tryLock()) {
                log.debug("获取不到锁");
                return;
            }
            try {
                log.debug("获取到锁了");
            } finally {
                lock.unlock();
            }


        }, "t1");


        lock.lock();
        thread.start();


    }
}

结果:
在这里插入图片描述

2、tryLock()锁超时

@Slf4j(topic = "TestReentrantLock")
public class TestReentrantLock {


    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {

        Thread thread = new Thread(() -> {

            log.debug("尝试获取锁");
            try {
                if (!lock.tryLock(1,TimeUnit.SECONDS)) {
                    log.debug("获取不到锁");
                    return;
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            try {
                log.debug("获取到锁了");
            } finally {
                lock.unlock();
            }


        }, "t1");


        lock.lock();
        thread.start();


    }
}

结果:
在这里插入图片描述
这里我们设置了t1线程在一秒后如果获取不到锁就会结束掉t1线程。
注意:这里lock.tryLock(1,TimeUnit.SECONDS),需要用try catch来进行捕获,这里catch所处理的代码是当在等待的过程中被打断的时候,应当进行什么样的处理

🐅八、ReentrantLock公平锁

ReentrantLock默认是不公平的

private static ReentrantLock lock = new ReentrantLock(false);
在这里插入图片描述
公平锁一般没有必要,会降低并发度,后面分析原理时会讲解。

🐆九、ReentrantLock条件变量

在这里插入图片描述
代码演示:

@Slf4j(topic = "TestAwaitSignal")
public class TestAwaitSignal {
    static boolean hasCigaratte = false;

    static boolean hasTakeOut = false;

    static ReentrantLock ROOM = new ReentrantLock();


    //等待烟的休息室
    static Condition waitCigaratteSet = ROOM.newCondition();

    //等待外卖的休息室
    static Condition waitTakeOutSet = ROOM.newCondition();


    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {

            ROOM.lock();
            try {
                log.debug("有烟没?[{}]", hasCigaratte);
                while (!hasCigaratte) {
                    log.debug("没有烟,先歇着");
                    try {
                        waitCigaratteSet.await();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }


                log.debug("有烟没?[{}]", hasCigaratte);

                if (hasCigaratte) {
                    log.debug("开始干活");
                } else {
                    log.debug("没干成活");
                }
            } finally {
                ROOM.unlock();
            }

        }
                , "小南").start();


        new Thread(() -> {
            ROOM.lock();
            try {
                log.debug("有外卖没?[{}]", hasTakeOut);


                try {
                    waitTakeOutSet.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }


                log.debug("有外卖没?[{}]", hasTakeOut);

                if (hasTakeOut) {
                    log.debug("开始吃饭");
                } else {
                    log.debug("没吃上饭 ");
                }
            } finally {
                ROOM.unlock();
            }

        }, "小女").start();


        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            ROOM.lock();
            try {
                hasTakeOut = true;
                waitTakeOutSet.signal();
                log.debug("来送外卖喽");
            } finally {
                ROOM.unlock();
            }
        }, "送外卖喽").start();


    }
}

结果:
在这里插入图片描述

  • 9
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值