首页 快讯正文

重庆旅游攻略:Java并发编程实战 03互斥锁 解决原子性问题

admin 快讯 2020-05-07 20 0

文章系列

Java并发编程实战 01并发编程的Bug源头
Java并发编程实战 02Java若何解决可见性和有序性问题

摘要

在上一篇文章02Java若何解决可见性和有序性问题当中,我们解决了可见性和有序性的问题,那么另有一个原子性问题咱们还没解决。在第一篇文章01并发编程的Bug源头当中,讲到了把一个或者多个操作在 CPU 执行的过程中不被中止的特征称为原子性,那么原子性的问题该若何解决。

统一时刻只有一个线程执行这个条件非常主要,我们称为互斥,若是能珍爱对共享变量的修改时互斥的,那么就能保住原子性。

浅易锁

我们把一段需要互斥执行的代码称为临界区,线程进入临界区之前,首先实验获取加锁,若加锁乐成则可以进入临界区执行代码,否则就守候,直到持有锁的线程执行了解锁unlock()操作。如下图:

然则有两个点要我们明了清晰:我们的锁是什么?要珍爱的又是什么?

改善后的锁模子

在并发编程天下中,锁和锁要珍爱的资源是有对应关系的。
首先我们需要把临界区要珍爱的资源R符号出来,然后需要建立一把该资源的锁LR,最后针对这把锁,我们需要在收支临界区时添加加锁lock(LR)操作和解锁unlock(LR)操作。如下:

Java语言提供的锁手艺:synchronized

synchronized可修饰方式和代码块。加锁lock()和解锁unlock()都会在synchronized修饰的方式或代码块前后自动加上加锁lock()和解锁unlock()操作。这样做的利益就是加锁和解锁操作会成对泛起,究竟忘了执行解锁unlock()操作可是会让其他线程死等下去。
那我们怎么去锁住需要珍爱的资源呢?在下面的代码中,add1()非静态方式锁定的是this工具(当前实例工具),add2()静态方式锁定的是X.class(当前类的Class工具)

public class X {
    public synchronized void add1() {
        // 临界区
    }
    public synchronized static void add2() {
        // 临界区
    }
}

上面的代码可以明了为这样:

public class X {
    public synchronized(this) void add() {
        // 临界区
    }
    public synchronized(X.class) static void add2() {
        // 临界区
    }
}

使用synchronized 解决 count += 1 问题

在01 并发编程的Bug源头文章当中,我们提到过count += 1 存在的并发问题,现在我们实验使用synchronized解决该问题。

public class Calc {
    private int value = 0;
    public synchronized int get() {
        return value;
    }
    public synchronized void addOne() {
        value += 1;
    }
}

addOne()方式被synchronized修饰后,只有一个线程能执行,以是一定能保证原子性,那么可见性问题呢?在上一篇文章02 Java若何解决可见性和有序性问题当中,提到了管程中的锁规则,一个锁的解锁 Happens-Before 于后续对这个锁的加锁。管程,在这里就是synchronized(管程的在后续的文章中先容)。凭据这个规则,前一个线程执行了value += 1操作是对后续线程可见的。而查看get()方式也必须加上synchronized修饰,否则也没法保证其可见性。
上面这个例子如下图:

那么可以使用多个锁珍爱一个资源吗,修改一下上面的例子后,get()方式使用this工具锁来珍爱资源valueaddOne()方式使用Calc.class类工具来珍爱资源value,代码如下:

public class Calc {
    private static int value = 0;
    public synchronized int get() {
        return value;
    }
    public static synchronized void addOne() {
        value += 1;
    }
}

上面的例子用图来示意:
重庆旅游攻略:Java并发编程实战 03互斥锁 解决原子性问题 第1张

在这个例子当中,get()方式使用的是this锁,addOne()方式使用的是Calc.class锁,因此这两个临界区(方式)并没有互斥性,addOne()方式的修改对get()方式是不能见的,以是就会导致并发问题。
结论:不能使用多把锁珍爱一个资源,但能使用一把锁珍爱多个资源(这里没写例子,只写了一把锁珍爱一个资源)

珍爱没有关联关系的多个资源

在银行的营业当中,修改密码和取款是两个再经常不外的操作了,修改密码操作和取款操作是没有关联关系的,没有关联关系的资源我们可以使用差别的互斥锁来解决并发问题。代码如下:

public class Account {
    // 珍爱密码的锁
    private final Object pwLock = new Object();
    // 密码
    private String password;

    // 珍爱余额的锁
    private final Object moneyLock = new Object();
    // 余额
    private Long money;

    public void updatePassword(String password) {
        synchronized (pwLock) {
            // 修改密码
        }
    }

    public void withdrawals(Long money) {
        synchronized (moneyLock) {
            // 取款
        }
    }
}

划分使用pwLockmoneyLock来珍爱密码和余额,这样修改密码和修改余额就可以并行了。使用差别的锁对受珍爱的资源举行举行更细化治理,能够提升性能,这种锁叫做细粒度锁。
在这个例子当中,你可能发现我使用了final Object来当成一把锁,这里解释一下:使用锁必须是不能变工具,若把可变工具作为锁,当可变工具被修改时相当于换锁,而且使用LongInteger作为锁时,在-128到127之间时,会使用缓存,详情可查看他们的valueOf()方式。

珍爱有关联关系的多个资源

在银行营业当中,除了修改密码和取款的操作比较多之外,另有一个操作比较多的功效就是转账。账户 A 转账给 账户B 100元,账户A的余额削减100元,账户B的余额增添100元,那么这两个账户就是有关联关系的。在没有明了互斥锁之前,写出的代码可能如下:

public class Account {
    // 余额
    private Long money;
    public synchronized void transfer(Account target, Long money) {
        this.money -= money;
        if (this.money < 0) {
            // throw exception
        }
        target.money += money;
    }
}

在转账transfer方式当中,锁定的是this工具(用户A),那么这里的目的用户target(用户B)的能被锁定吗?固然不能。这两个工具是没有关联关系的。准确的操作应该是获取this锁和target锁才能去举行转账操作,准确的代码如下:

public class Account {
    // 余额
    private Long money;
    public synchronized void transfer(Account target, Long money) {
        synchronized(this) {
            synchronized (target) {
                this.money -= money;
                if (this.money < 0) {
                    // throw exception
                }
                target.money += money;
            }
        }
    }
}

在这个例子当中,我们需要清晰的明了要珍爱的资源是什么,只要我们的锁能笼罩所有受珍爱的资源就可以了
然则你以为这个例子很完善?那就错了,这内里很有可能会发生死锁。你看出来了吗?下一篇文章我就用这个例子来聊聊死锁。

总结

使用互斥锁最最主要的是:我们的锁是什么?锁要珍爱的资源是什么?,要理清晰这两点就好下手了。而且锁必须为不能变工具。使用差别的锁珍爱差别的资源,可以细化治理,提升性能,称为细粒度锁

参考文章:
极客时间:Java并发编程实战 03互斥锁(上)
极客时间:Java并发编程实战 04互斥锁(下)

小我私家博客网址: https://colablog.cn/

若是我的文章辅助到您,可以关注我的微信民众号,第一时间分享文章给您
重庆旅游攻略:Java并发编程实战 03互斥锁 解决原子性问题 第2张

,

sunbet

www.ysycy.com与伊顺源清真餐饮达成战略合作,在伊顺及亚太地区建立直营平台。为Sunbet会员提供线上多种娱乐游戏,将用完善的技术、贴心的服务、雄厚的资金赢取每位Sunbet代理、会员的口碑。

版权声明

本文仅代表作者观点,
不代表本站诚信在线的立场。
本文系作者授权发表,未经许可,不得转载。