zoukankan      html  css  js  c++  java
  • Java锁分类原来是这个样子

    学了几天python,辣条君始终不忘自己其实是个Javaer。来,跟着辣条君一起看看Java锁是如何分类的吧。

    Java锁是为了保证并发过程中,数据维护的准确性。

    乐观锁与悲观锁

    乐观锁:认为当前读取数据的时候,不会有线程去修改数据,所以不需要加锁。当更新数据的时候,首先查看数据和自己曾经记录的数据是否一致,如果一致,则更新之;如果不一致,采取一些手段,比如报错或者自旋(自旋后面会讲)。

    举个例子,一个线程A读取账户余额时,不会加锁,读到20元,线程A账户记录更新为20元。然后线程A为账户余额增加5元,现在想把账户余额更新为25元,先去查看账户现在的数值为20元,和账户记录一致,就将账户余额直接更新为25元,同时将自己的账户记录更新为25元。过了一会儿,线程A又想给账户余额增加5元,于是拿着30元去更新账户余额。此时,发现账户余额为100元,和自己的账户记录(25元)不一致了,就报错(或者自旋),此次更新失败。

    CAS(比较交换)就是一种常见的乐观锁实现方案,java.util.concurrent.atomic中的那些原子类就是通过CAS算法实现的。为了保证更新的原子性,原子类最终实质上是通过JNL调用了CPU的原子操作。CAS先天有两点不足:1、ABA问题。2、长时间的自旋会消耗过多的资源。

    所以乐观锁多用于读数据多的场景,效率较高。

    悲观锁:认为无论自己进行什么操作,那一瞬间都会有其他线程来污染数据,所以一定要加锁。

    悲观锁很好理解,不需要加什么例子了,synchronizedLock都属于悲观锁。关于这两个类的使用,我想另写一篇博客,毕竟日常使用比较多嘛。悲观锁多用于写数据多的场景

    在这里插入图片描述

    自旋锁

    上一节反复提到自旋,自旋究竟是个什么东东呢?

    首先我们要知道,一个Java线程被阻塞,会放弃CPU使用权;被唤醒,会重新获得CPU使用权。这两个切换上下文的过程,是极其消耗资源的。如果,一个同步操作(线程占用锁)的时间极短,那需要用锁的线程可以先等一会儿,待会不用进行上下文切换,拿到锁直接执行,那岂不是极好的。这个等待操作就叫做自旋。

    自旋操作一般会规定自旋次数,如果一定次数还是没有得到锁,那就放弃自旋,进行阻塞。为了更加提升效率,自适应自旋锁出现了,它不拘泥于固定的次数,而是根据以往经验,如果以前自旋一段时间可以得到锁,那么超过最大自旋数的时候,允许多自旋几次;如果以往经验总是失败,那么不一定非得到达最大自旋数,就直接进入自旋状态。

    无锁、偏向锁、轻量级锁、重量级锁

    根据切换资源消耗成本,可以将锁分为无锁、偏向锁、轻量级锁、重量级锁。

    无锁:就是不对资源加锁,例如上面讲到的CAS算法,只是在更新的时候进行一下比较判断就好。

    偏向锁:有一种理想的状态,一段时间内只有一个线程访问同步代码块,这样是不是连更新时比较的步骤都可以省略了。这种情况下可以挂上偏向锁,这样该线程在访问同步代码块的时候就不需要CAS操作了。当,有其他线程来访问共享资源的时候,偏向锁自动升级为轻量级锁。如果没有线程来打扰,只有当虚拟机运行到全局安全点的时候才能撤销偏向锁。

    轻量级锁:当一个线程拥有轻量级锁,另一个线程想拥有这把锁,不会进入阻塞状态,而是先自旋,等待获得锁的机会。但是,当多个线程(至少两个)来获取这把锁时,这把锁会直接升级为重量级锁

    重量级锁:当一个线程拥有重量级锁时,其他线程想要获取该锁,都会直接进入阻塞状态。在JDK1.6之前synchronized机制使用的时重量级锁,1.6版本之后开始使用轻量级锁偏向锁

    在这里插入图片描述

    公平锁和非公平锁

    公平锁:当多个线程请求获取锁时,根据请求的先后顺序放到一个队列里,然后按顺序获取锁。此时,线程从阻塞到唤醒是需要上下文切换的。保证公平性,但是效率可能较低。

    非公平锁:非公平锁,尝试被线程获取的时候,不一定从线程队列中获取,先看看此时有没有新的线程来获取本锁,如果有,直接把锁给该线程,不需要进行上下文切换。失去公平性,但是可能会提高效率。

    可重入锁

    可重入锁是指同一个线程可以多次加同一把锁。ReentrantLocksynchronized都属于可重入锁

    public class MyTest {
        // 方法嵌套
        public synchronized void outThing() {
            // do someting
            innerThing();
        }
        public synchronized void innerThing() {
            // do something
        }
    }
    

    看上面这种情况,方法嵌套通过synchronized机制2次获取了对象的锁(Monitor)。如果是非可重入锁,一定会发生死锁。

    共享锁和独享锁

    共享锁:一个线程给共享资源加上共享锁后,其他线程还可以给这个共享资源加上其他的共享锁。比如常见的读锁。

    独享锁:一个资源被加上独享锁后,就不能添加其他锁了。比如常见的写锁。

    共享锁和独享锁在mysql层面也是通用概念。

    小结

    有了这些Java锁的概念,再去看代码就方便多了。接下来会好好研究下synchronizedReentrantLock

  • 相关阅读:
    程序员的7中武器
    需要强化的知识
    微软中国联合小i推出MSN群Beta 不需任何插件
    XML Notepad 2006 v2.0
    Sandcastle August 2006 Community Technology Preview
    [推荐] TechNet 广播 SQL Server 2000完结篇
    《太空帝国 4》(Space Empires IV)以及 xxMod 英文版 中文版 TDM Mod 英文版 中文版
    IronPython 1.0 RC2 更新 1.0.60816
    Microsoft .NET Framework 3.0 RC1
    《Oracle Developer Suite 10g》(Oracle Developer Suite 10g)V10.1.2.0.2
  • 原文地址:https://www.cnblogs.com/pjjlt/p/13924076.html
Copyright © 2011-2022 走看看