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<Object> 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类可以解决此类问题。

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

小结

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值