线程活跃性 && ReentrantLock

目录

线程活跃状态

死锁

活锁

饥饿

ReentrantLock

特点 

特点展示 

可重入

可打断

可设置超时时间

可设置为公平锁

支持多个条件变量 

解决死锁问题


线程活跃状态

死锁

当一个线程需要同时获取多把锁时,容易发生死锁。 

@Slf4j(topic = "c.DeadLock")
public class DeadLock {
    public static void main(String[] args) {
        Object lock1 = new Object();
        Object lock2 = new Object();

        new Thread(() -> {
            synchronized (lock1){
                log.debug("获取 lock1");
                try {
                    Thread.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2){
                    log.debug("获取 lock2");
                }
            }
        }, "t1").start();

        new Thread(() -> {
            synchronized (lock2){
                log.debug("获取 lock2");
                try {
                    Thread.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1){
                    log.debug("获取 lock1");
                }
            }
        }, "t2").start();
    }
}

① 线程 t1 在对象 lock2 指向的 monitor 中的 EntryList 里,BLOCKED等待线程 t2 释放

② 线程 t2 在对象 lock1 指向的 monitor 中的 EntryList 里,BLOCKED等待线程 t1 释放

③ t1 等 t2 放,t2 等 t1 放

就像小情侣吵完架,都等着对方先道歉;于是死锁就产生了

活锁

两个线程相互改变对方结束条件,于是两个线程永远都结束不了 

@Slf4j(topic = "c.LiveLock")
public class LiveLock {
    private static int count = 10;

    public static void main(String[] args) {
        new Thread(() -> {
            while(count < 20){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("{}", ++count);
            }
        }, "t1").start();

        new Thread(() -> {
            while(count > 0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("{}", --count);
            }
        }, "t2").start();
    }
}

count 永远达不到 t1 和 t2 所需要的结束条件,于是两个线程永远都不会结束 

解决:提高两个线程睡眠时间长度差距,使得一个线程比另一个线程更早执行完

饥饿

一个线程由于优先级太低,始终得不到 CPU 调度执行,也不能够结束

@Slf4j(topic = "c.HungryLock")
public class HungryLock {
    public static void main(String[] args) {
        Object lock1 = new Object();
        Object lock2 = new Object();

        new Thread(() -> {
            int count = 0;
            while (true){
                synchronized (lock1){
                    synchronized (lock2){
                        log.debug("Java...... {}", ++count);
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }, "Java").start();

        new Thread(() -> {
            int count = 0;
            while(true){
                synchronized (lock1){
                    synchronized (lock2){
                        log.debug("C++...... {}", ++count);
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }, "C++").start();

        new Thread(() -> {
            int count = 0;
            while(true){
                synchronized (lock1){
                    synchronized (lock2){
                        log.debug("Python...... {}", ++count);
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }, "Python").start();

        new Thread(() -> {
            int count = 0;
            while(true){
                synchronized (lock1){
                    synchronized (lock2){
                        log.debug("Go...... {}", ++count);
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }, "Go").start();
    }
}

顺序加锁可以解决死锁问题;因为当线程获取不到第一把锁时,它就不会进入同步代码块

② 但是产生了饥饿问题;某些线程要等很久才能运行,甚至运行不了

ReentrantLock

特点 

① 可重入

② 可打断

③ 可设置超时时间

④ 可设置为公平锁

⑤ 支持多个条件变量

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

特点展示 

可重入

 

@Slf4j(topic = "c.ReentrantLockTest")
class Reentrant{
    private ReentrantLock lock = new ReentrantLock();
    void method1(){
        log.debug("第一层方法");
        lock.lock();
        try {
            method2();
        }finally {
            lock.unlock();
        }
    }

    void method2(){
        log.debug("第二层方法");
        lock.lock();
        try {
            method3();
        }finally {
            lock.unlock();
        }
    }

    void method3(){
        lock.lock();
        try {
          log.debug("第三层方法");
        }finally {
            lock.unlock();
        }
    }
}

可打断

    static void interrupt() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                lock.lockInterruptibly(); // 必须调用此方法
            } catch (InterruptedException e) {
                log.debug("等待锁过程被打断...");
                e.printStackTrace();
                return;
            }
            try {
                log.debug("获取锁成功");
            }finally {
                lock.unlock();
                log.debug("释放锁成功");
            }
        }, "t1");
        t1.start();

        lock.lock();
        log.debug("获得锁");
        Thread.sleep(1000);
        log.debug("进行打断");
        t1.interrupt();
    }

① lock.lockInterruptibly() 才能被打断

② 支持可打断,避免线程一直等待锁

可设置超时时间

t1 立即尝试获得锁;t2 设置获取锁超时时间

    static void tryLock() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            if(!lock.tryLock()){
                log.debug("立即尝试获取锁失败...");
                return;
            }
            try{
                log.debug("立即尝试获取锁成功!");
            }finally {
                lock.unlock();
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            try {
                if(!lock.tryLock(2, TimeUnit.MINUTES)){
                    log.debug("尝试获取锁,超过 2s,获取锁失败...");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                log.debug("尝试获取锁,小于 2s,获取锁成功!");
            }finally {
                lock.unlock();
            }
        }, "t2");

        try{
            lock.lock();
            log.debug("获取锁成功");
            t1.start();
            t2.start();
            Thread.sleep(1000);
            log.debug("睡 1s,放锁");
        }finally {
            lock.unlock();
        }
    }

① t1 立即尝试获取锁,失败

② t2 超时时间为 2s,main 线程 1s 后释放锁,t2 获取锁成功 

可设置为公平锁

① ReentrantLock 默认为步公平 

② 公平锁:按照先来后到的规则

③ 公平锁一般没必要,会降低并发度

1、不公平情

    static void unfairLock() throws InterruptedException {
        ReentrantLock unfairLock = new ReentrantLock(); // 不公平情况
        for(int i = 0; i < 500; i++){
            int index = i;
            new Thread(() -> {
                unfairLock.lock();
                try{ 
                    log.debug("t{}, 获得锁", index);
                } finally {
                    unfairLock.unlock();
                }
            }, "t" + 1).start();
        }

        Thread.sleep(10);
        new Thread(() -> {
            try{
                unfairLock.lock();
                log.debug("不公平插入");
            }finally {
                unfairLock.unlock();
            }
        }, "不公平").start();
    }

按公平来说,此不公平线程应该要最后才能拿到锁

其他线程正好释放锁它正好启动获取锁,于是出现这种不公平插入情况

2、公平情况

ReentrantLock unfairLock = new ReentrantLock(true);

等到循环中线程全部获取锁并释放,它才能获得锁

支持多个条件变量 

相当于 ReentrantLock 有多个 WaitSet,不同的条件对应不同的 WaitSet  

    static void conditionLock() throws InterruptedException {
        ReentrantLock conditionalLock = new ReentrantLock();
        Condition javaWait = conditionalLock.newCondition(); // 进入 Java 休息室
        Condition cWait = conditionalLock.newCondition(); // 进入 C 休息室
        Condition pythonWait = conditionalLock.newCondition(); // 进入 Python 休息室
        Condition goWait = conditionalLock.newCondition(); // 进入 Go 休息室
        Condition otherWait = conditionalLock.newCondition(); // 进入其他休息室

        Random random = new Random(); // 线程进入随机休息室
        for(int i = 0; i < 10; i++){
            new Thread(() -> {
                conditionalLock.lock();
                try{
                    int index = random.nextInt(6);
                    switch (index){
                        case 1 : {
                            log.debug("进入 Java 休息室,一直等待");
                            javaWait.await();
                            log.debug("从 Java 休息室出来");
                            break;
                        }
                        case 2 : {
                            log.debug("进入 C 休息室,一直等待");
                            cWait.await();
                            log.debug("从 C 休息室出来");
                            break;
                        }
                        case 3 : {
                            log.debug("进入 Python 休息室,一直等待");
                            pythonWait.await();
                            log.debug("从 Python 休息室出来");
                            break;
                        }
                        case 4 : {
                            log.debug("进入 Go 休息室,一直等待");
                            goWait.await();
                            log.debug("从 Go 休息室出来");
                            break;
                        }
                        default : {
                            log.debug("进入其他休息室,只等待 2s");
                            otherWait.await(2, TimeUnit.SECONDS);
                            log.debug("超过 2s 了,在其他休息室自动出来");
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    conditionalLock.unlock();
                }
            }, "t" + i).start();
        }

        Thread.sleep(2000);
        conditionalLock.lock();
        try {
            log.debug("-----------------随机唤醒 Java休息室 中某个线程-----------------");
            javaWait.signal();
            Thread.sleep(1000);
            log.debug("-----------------随机唤醒 C 休息室 中某个线程-----------------");
            cWait.signal();
            Thread.sleep(1000);
            log.debug("-----------------随机唤醒 Python 休息室 中某个线程-----------------");
            pythonWait.signal();
            Thread.sleep(1000);
            log.debug("-----------------随机唤醒 Go 休息室 中所以线程-----------------");
            goWait.signalAll();
        }finally {
            conditionalLock.unlock();
        }
    }

解决死锁问题

不要那么自私尝试获取锁失败,就把手中已有锁释放,让别人先用

    static void solveDeadLock(){
        ReentrantLock lock1 = new ReentrantLock();
        ReentrantLock lock2 = new ReentrantLock();
        ReentrantLock lock3 = new ReentrantLock();
        ReentrantLock lock4 = new ReentrantLock();

        new Thread(() -> {
            while (true){
                if(lock1.tryLock()){
                    try{
                        if(lock2.tryLock()){
                            try {
                                log.debug("Java......");
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            } finally {
                                lock2.unlock();
                            }
                        }
                    }finally {
                        lock1.unlock();
                    }
                }
            }
        }, "Java").start();

        new Thread(() -> {
            while(true){
                if(lock2.tryLock()){
                    try{
                        if(lock3.tryLock()){
                            try {
                                log.debug("C++......");
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            } finally {
                                lock3.unlock();
                            }
                        }
                    }finally {
                        lock2.unlock();
                    }
                }
            }
        }, "C++").start();

        new Thread(() -> {
            while(true){
                if(lock3.tryLock()){
                    try{
                        if(lock4.tryLock()){
                            try {
                                log.debug("Python......");
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            } finally {
                                lock4.unlock();
                            }
                        }
                    }finally {
                        lock3.unlock();
                    }
                }
            }
        }, "Python").start();

        new Thread(() -> {
            while(true){
                if(lock4.tryLock()){
                    try{
                        if(lock1.tryLock()){
                            try {
                                log.debug("Go......");
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            } finally {
                                lock1.unlock();
                            }
                        }
                    }finally {
                        lock4.unlock();
                    }
                }
            }
        }, "Go").start();
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值