栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

2. Java并发编程-互斥锁、死锁

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

2. Java并发编程-互斥锁、死锁

上一节讲了引起并发问题原因中的可见性和有序性。 通过利用Java内存模型开发者可以很好的避开上述问题。 本节我们来探索剩下的一个引起并发问题的原因:原子性。 什么是原子性?

即一个或多个操作在CPU执行的过程中不被中断的特性。

互斥

一段代码同一时刻只有一个线程运行,称为互斥。 当我们能保证对共享变量的写是互斥的,就能保证原子性 了。

临界区

将一段需要互斥执行的代码称为临界区。

Java提供的锁技术:synchronized

1)当修饰静态方法时,锁定的是当前类Class对象
2)当修饰非静态方法时,锁定的是当前对象

死锁

一组互相竞争资源的线程因相互等待,导致永久阻塞的现象。

如何解决死锁问题

一旦发生死锁通常没有好的方法,只能重启应用。 重点是在预防死锁。

死锁发生的条件:
1)互斥,共享资源A和B只能被一个线程占用
2)占有且等待,线程一已经取得了资源A,在等待B资源的时候不会释放资源A
3)不可抢占,其他线程不能强行抢占线程A占有的资源
4)循环等待。 如A线程等待B占有的资源,线程B又等待A线程占有的资源

以上4个条件缺一不可,反过来意味着我们只要破坏其中一条即可解决死锁问题。
这其中第一条无法破除,因为用锁就是为了互斥性。
第二条占用且等待,我们可以一次性申请所有的资源
以转账为示例,将同时申请两个账户的锁操作放到临界区中,保证只能一个线程同时申请到两个锁资源,示例代码如下:

class Account {

    private static final Allocate allocate = new Allocate();

    private int balance;

    public Account(int balance) {
        this.balance = balance;
    }

    void transfer(Account target, int amount) {
        while (allocate.apply(this, target))
            ;

        try {
            synchronized (this) {
                synchronized (target) {
                    if (this.balance >= amount) {
                        this.balance -= amount;
                        target.balance += amount;
                    }
                }
            }

        } finally {
            allocate.free(this, target);
        }
    }


}


class Allocate {
    private List locks = new ArrayList<>();

    synchronized boolean apply(Object lock1, Object lock2) {
        if (locks.contains(lock1) || locks.contains(lock2)) {
            return false;
        }

        locks.add(lock1);
        locks.add(lock2);

        return true;
    }

    synchronized void free(Object lock1, Object lock2) {
        locks.remove(lock1);
        locks.remove(lock2);
    }

}
 

第三条不可抢占,当占用部分资源的线程在进一步申请其他资源时,如果申请不到,可以主动放弃它已占有的资源
这一点默认synchronized锁是做不到的。即语言层面无法做到,但SDK可以,java.util.concurrent并发包下提供的Lock类可以解决此类问题。

第四条循环等待,可以靠按序申请资源来预防。
这一点比较好理解,即保持不同线程加锁的顺序一致,能避免互相持有对方锁的情况。

小结

当使用互斥锁时,要分析多个资源之间的关系,如果没关系,每个资源一把锁即可,如果资源之间有关系,就要选择一个粒度更大的锁,能覆盖所有相关的资源。

当使用细粒度锁在锁定多个资源时,要注意死锁的问题,能及时识别风险很重要。破除死锁即可用上面列出的三个方法,但使用不同方法时页需要比较优劣,上面转账的示例采用了破坏占用且等待的方法,实际上就不如破除不可抢占和破除循环等待简单高效。

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号