精华内容
下载资源
问答
  • 为弥补传统密码锁的不足,进一步提高... 传统机械锁的防盗功能,在现代高科技安防系统无法起到作用,已逐步被更可靠、更智能的电子数字密码锁代替。目前市场上的大部分密码锁产品是以单片机为核心的,利用软件进
  • 提升Java的锁性能

    2021-01-21 17:10:01
    包括分拆锁,并发数据结构,保护数据而非代码,以及缩小锁的作用域。  锁无罪,竞争其罪  如果你在多线程代码碰到了性能问题,你肯定会先抱怨锁。毕竟,从“常识”来讲,锁的性能是很的,并且还限制了程序的...
  • 在JDK1.6之前,synchronized性能比较,在JDK1.6,JDK官方对synchronized进行了一些优化,比如升级、消除等等。这些优化很多都涉及到CAS操作,了解了CAS,有利于我们理解synchronized底层升级过程,话...

    目录

    一、概述

    二、CAS概述和作用

    三、synchronized锁升级过程

    四、偏向锁

    五、轻量级锁

    六、重量级锁

    七、锁的优缺点对比

    八、总结


    一、概述

    在JDK1.6之前,synchronized的性能比较差,在JDK1.6中,JDK官方对synchronized进行了一些优化,比如锁升级、锁消除等等。这些优化很多都涉及到CAS操作,了解了CAS,有利于我们理解synchronized底层锁升级的过程,话不多说,先看下什么是CAS?

    二、CAS概述和作用

    • CAS的全称是: Compare And Swap(比较并交换)。CAS操作涉及到3个值:内存中的值V,旧的预估值X,要修改的新值B,如果旧的预估值X等于内存中的值V,就将新的值B保存到内存中。
    • CAS的作用:CAS可以将比较和交换转换为原子操作,这个原子操作直接由CPU保证,CAS可以保证共享变量赋值时的原子操作。

    下面我们通过一个示例来看看CAS怎么实现无锁并发:

    定义一个共享变量num,然后启动多个线程,同时执行1000次num++.

    import java.util.ArrayList;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class CasDemo {
    
        public static void main(String[] args) throws InterruptedException {
            //定义一个共享变量
            AtomicInteger numAtomicInteger = new AtomicInteger();
            ArrayList<Thread> threads = new ArrayList<>();
    
            //启动五个线程同时进行num++
            for (int i = 0; i < 5; i++) {
                Thread t = new Thread(() -> {
                    for (int j = 0; j < 1000; j++) {
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //保证numAtomicInteger++操作的原子性
                        numAtomicInteger.incrementAndGet();
                    }
                });
                t.start();
                threads.add(t);
            }
    
            //使用join()等待所有线程都执行完成
            for (Thread t : threads) {
                t.join();
            }
            System.out.println("numAtomicInteger = " + numAtomicInteger.get());
        }
    }

    我们看到,这里我们使用到了AtomicInteger原子整形类来替换直接num++操作,运行程序,观察控制台输出:

    可见,AtomicInteger能够实现共享变量的原子操作。接着我们看一下AtomicInteger的incrementAndGet()是怎么保证原子性的。

    //java.util.concurrent.atomic.AtomicInteger#unsafe
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    
    //java.util.concurrent.atomic.AtomicInteger#incrementAndGet
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
    
    //sun.misc.Unsafe#getAndAddInt
    public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            //找到这个对象的某个值
            var5 = this.getIntVolatile(var1, var2);
            //var2: 内存值
            //var5: 预估值
            //var5+var4: 更新值   
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    
        return var5;
    }

    通过源码我们可以看到,AtomicInteger底层使用到了Unsafe类,它提供了原子操作。Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,同时也带来了指针的问题。过度的使用Unsafe类会使得出错的几率变大,因此Java官方并不建议使用的,官方文档也几乎没有。Unsafe对象不能直接调用,只能通过反射获得。

    Unsafe的compareAndSwapInt()方法其实就是CAS方法,它会自旋判断当前内存的值与预估值是否相等, 如果相等就会把内存值用更新值替换,如果不相等则不操作,以此来保证原子性。

    下面我们介绍一下乐观锁和悲观锁。

    • 悲观锁

    从悲观的角度出发,总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞。因此synchronized我们也将其称之为悲观锁。JDK中的ReentrantLock也是一种悲观锁,性能较差。

    • 乐观锁

    从乐观的角度出发,总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,就算改了也没关系,再重试即可。所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去修改这个数据,如何没有人修改则更新,如果有人修改则重试。CAS这种机制我们也可以将其称之为乐观锁,综合性能较好。

    下面总结一下CAS算法的优缺点:

    CAS优点:

    • 可以避免优先级倒置和死锁等问题;
    • 允许更高程度的并行机制;

    缺点:

    CAS虽然很高效的解决了原子操作问题,但是CAS仍然存在三大问题:

    • 循环时间长开销很大

    如果CAS失败,会一直进行尝试,如果CAS长时间一直不成功,可能会给CPU带来很大的开销。

    • 只能保证一个共享变量的原子操作

    当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来保证原子性。

    • ABA问题

    如果内存地址V初次读取的值是A,并且在准备赋值的时候检查到它的值仍然为A,那我们就能说它的值没有被其他线程改变过了吗?

    如果在这段期间它的值曾经被改成了B,后来又被改回为A,那CAS操作就会误认为它从来没有被改变过。这个漏洞称为CAS操作的“ABA”问题。Java并发包为了解决这个问题,提供了一个带有标记的原子引用类“AtomicStampedReference”,它可以通过控制变量值的版本来保证CAS的正确性。因此,在使用CAS前要考虑清楚“ABA”问题是否会影响程序并发的正确性,如果需要解决ABA问题,改用传统的互斥同步可能会比原子类更高效。

    CAS获取共享变量时,为了保证该变量的可见性,需要使用volatile修饰。结合CAS和volatile可以实现无锁并发,CAS只适用于竞争不激烈、多核 CPU 的场景下。

    三、synchronized锁升级过程

    高效并发是从JDK 5到JDK 6的一个重要改进,HotSpot虛拟机开发团队在这个版本上花费了大量的精力去实现各种锁优化技术,包括偏向锁( Biased Locking )、轻量级锁( Lightweight Locking )和适应性自旋(Adaptive Spinning)、锁消除( Lock Elimination)、锁粗化( Lock Coarsening )等,这些技术都是为了在线程之间更高效地共享数据,以及解决竞争问题,从而提高程序的执行效率。

    无锁 --> 偏向锁 --> 轻量级锁 --> 重量级锁

    前面提到很多种锁的状态,那么锁的状态到底存在哪里以及怎么区分当前属于哪一种锁呢?

    其实,锁的状态保存在对象头结构的mark word标记字中,如果小伙伴还不熟悉Java对象内存布局,可以参考笔者另外一篇文章:https://weishihuai.blog.csdn.net/article/details/116401467【Object对象的内存布局学习总结】。

    以上是Java对象处于5种不同状态时,Mark Word中64个位的表现形式,上面每一行代表对象处于某种状态时的样子。其中跟锁状态相关部分的含义如下:

    • lock:表示锁状态的标记位,占用2个二进制位;
    • biased_lock:表示对象是否启用偏向锁标记,占用1个二进制位。biased_lock为1时表示对象启用偏向锁,为0时表示对象没有偏向锁;

    通过倒数三位数,即【biased_lock + lock】,我们可以判断出锁的类型,表达的锁状态含义如下表所示:

    偏向锁标识(biased_lock)

    锁标识(lock)

    锁的类型

    0

    01

    无锁

    1

    01

    偏向锁

    0

    00

    轻量级锁

    0

    10

    重量级锁

    0

    11

    GC标志

    下面我们分别对各种锁状态进行详细的分析。

    四、偏向锁

    【a】偏向锁概念

    首先来看一下什么是偏向锁?

    HotSpot作者经过研究实践发现,在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低,引进了偏向锁。

    偏向锁的“偏”,它的意思是这个锁会偏向于第一个获得它的线程,会在对象头存储锁偏向的线程ID,以后该线程进入和退出同步块时只需要检查是否为偏向锁、锁标志位以及ThreadID即可。

    不过一旦出现多个线程竞争时必须撤销偏向锁,所以撤销偏向锁消耗的性能必须小于之前节省下来的CAS原子操作的性能消耗,不然就得不偿失了。

    【b】案例演示偏向锁

    首先在项目中添加内存布局相关的maven依赖:

    <dependency>
        <groupId>org.openjdk.jol</groupId>
        <artifactId>jol-core</artifactId>
        <version>0.9</version>
    </dependency>
    import org.openjdk.jol.info.ClassLayout;
    
    public class BiasedLockDemo {
        public static void main(String[] args) {
            MyRunnable myRunnable = new MyRunnable();
            Thread thread = new Thread(myRunnable);
            thread.start();
        }
    }
    
    class MyRunnable implements Runnable {
        static Object object = new Object();
    
        @Override
        public void run() {
            synchronized (object) {
                //打印锁对象的内存布局
                System.out.println(ClassLayout.parseInstance(object).toPrintable());
            }
        }
    }

    启动程序,观察控制台输出:

    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           38 f5 75 19 (00111000 11110101 01110101 00011001) (427160888)
          4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    如上我们看到,markword的倒数三位是000,根据前面的图,000表示的是轻量级锁,此时只有一个线程访问,为什么输出来的不是偏向锁标识101呢?

    原因其实是偏向锁在Java 6之后是默认启用的,但在应用程序启动几秒钟之后才激活,可以使用 -XX:BiasedLockingStartupDelay=0 参数关闭延迟。

    配置完启动参数之后再次启动程序,观察内存布局

    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           05 08 ea 18 (00000101 00001000 11101010 00011000) (417990661)
          4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    可以看到,这一次倒数三位就是偏向锁标识101了。不知道怎么查看锁标识的可以参考下图:

    【c】偏向锁的原理

    当线程第一次访问同步块并获取锁时,偏向锁处理流程如下:

    • 1. 虚拟机将会把对象头中的标志位设为“01”,即偏向模式;
    • 2. 同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word之中 ,如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作,偏向锁的效率高;

    【d】偏向锁的撤销

    什么时候会触发撤销偏向锁呢?一旦有多个线程同时过来争抢资源的时候,可能就会发生偏向锁的撤销:

    • 1. 偏向锁的撤销动作必须等待全局安全点;
    • 2. 暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态;
    • 3. 撤销偏向锁,恢复到无锁(标志位为 01)或轻量级锁(标志位为 00)的状态;

    如果确定应用程序中所有锁通常情况下处于竞争状态,可以通过 XX:-UseBiasedLocking=false 参数关闭偏向锁。

    【e】偏向锁的好处

    偏向锁是在只有一个线程执行同步块时进一步提高性能,适用于一个线程反复获得同一锁的情况。偏向锁可以提高带有同步但无竞争的程序性能。它并不一定总是对程序运行有利,如果程序中大多数的锁总是被多个不同的线程访问比如线程池,那偏向模式就是多余的,反而会影响效率。

    五、轻量级锁

    【a】什么是轻量级锁

    轻量级锁是JDK 6之中加入的新型锁机制,它名字中的“轻量级”是相对于使用monitor的传统锁而言的,因此传统的锁机制就称为“重量级”锁。首先需要强调一点的是,轻量级锁并不是用来代替重量级锁的。

    【b】为什么引入轻量级锁?

    轻量级锁考虑的是竞争锁对象的线程不多,而且线程持有锁的时间也不长的场景。因为阻塞线程需要CPU从用户态转到内核态,代价比较大,如果刚刚阻塞不久这个锁就被释放了,那这个代价就有点得不偿失了,因此这个时候就干脆不阻塞这个线程,让它自旋这等待锁的释放。

    【b】轻量级锁的原理

    当关闭偏向锁功能或者多个线程竞争偏向锁导致偏向锁升级为轻量级锁,则会尝试获取轻量级锁,其步骤如下:

    • 1. 判断当前对象是否处于无锁状态(hashcode、0、01),如果是,则JVM首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝(官方称为Displaced Mark Word),将对象的Mark Word复制到栈帧中的锁记录Lock Record中,将Lock Reocrd中的owner指向当前对象。
    • 2. JVM利用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,如果成功表示竞争到锁,则将锁标志位变成00,执行同步操作。
    • 3. 如果失败则判断当前对象的Mark Word是否指向当前线程的栈帧,如果是则表示当前线程已经持有当前对象的锁,则直接执行同步代码块;否则只能说明该锁对象已经被其他线程抢占了,这时轻量级锁需要膨胀为重量级锁,锁标志位变成10,后面等待的线程将会进入阻塞状态。

    【c】轻量级锁的释放

    轻量级锁的释放也是通过CAS操作来进行的,主要步骤如下:

    • 1、取出在获取轻量级锁保存在Displaced Mark Word中的数据;
    • 2、用CAS操作将取出的数据替换当前对象的Mark Word中,如果成功,则说明释放锁成功;
    • 3、 如果CAS操作替换失败,说明有其他线程尝试获取该锁,则需要将轻量级锁膨胀升级为重量级锁;

    对于轻量级锁,其性能提升的依据是“对于绝大部分的锁,在整个生命周期内都是不会存在竞争的”,如果打破这个依据则除了互斥的开销外,还有额外的CAS操作,因此在有多线程竞争的情况下,轻量级锁比重量级锁更慢。

    【d】轻量级锁优缺点

    • 优点:在多线程交替执行同步块的情况下,可以避免重量级锁引起的性能消耗;
    • 缺点:如果长时间自旋后还没竞争到锁,将会过度耗费CPU;

    六、重量级锁

    当轻量级锁遇到多个线程竞争锁资源的时候, 这时候轻量级锁可能会膨胀成重量级锁。

    public class HeavyweightLockDemo {
        //锁对象
        private static Object obj = new Object();
    
        public static void main(String[] args) {
            Thread thread1 = new Thread(() -> {
                synchronized (obj) {
                    try {
                        Thread.sleep(20000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "t1");
    
            Thread thread2 = new Thread(() -> {
                synchronized (obj) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "t2");
    
            thread1.start();
            thread2.start();
            System.out.println(ClassLayout.parseInstance(obj).toPrintable());
        }
    }

    启动程序,观察锁对象的内存布局:

    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           aa de 12 03 (10101010 11011110 00010010 00000011) (51568298)
          4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes

    我们看到,当多个线程去争抢同一把锁对象obj的时候,锁对象的markword倒数三位是010,所以这是一把轻量级锁。

    七、锁的优缺点对比

    下表是对各种状态的锁的对比:

    锁的类型

    优点

    缺点

    适用场景

    偏向锁

    加锁和解锁不需要额外的消耗,和执行非同步方法相比仅存在纳秒级的差距

    如果线程间存在锁竞争,会带来额外的锁撤销的消耗

    适用于只有一个线程访问同步块场景

    轻量级锁

    竞争的线程不会阻塞,提高了程序的响应速度

    如果始终得不到锁竞争的线程,使用自旋会消耗CPU

    追求响应时间

    同步块执行速度非常快

    重量级锁

    线程竞争不适用自旋,不会消耗CPU

    线程阻塞,响应时间缓慢

    追求吞吐量

    同步块执行时间较长

    八、总结

    本文首先介绍了CAS原子算法,然后详细分析了synchronized锁升级的过程【无锁 > 偏向锁 > 轻量级锁 > 重量级锁】。

    下面总结一下锁升级的大体过程:

    【a】偏向锁升级成轻量级锁

    大体过程如下图:

    文字描述如下【摘自https://blog.csdn.net/tongdanping/article/details/79647337】:

    当线程1访问代码块并获取锁对象时,会在锁对象的对象头和栈帧中记录偏向的锁的threadID,因为偏向锁不会主动释放锁,因此以后线程1再次获取锁的时候,需要比较当前线程的threadID和锁对象的对象头中的threadID是否一致,如果一致(还是线程1获取锁对象),则无需使用CAS来加锁、解锁;如果不一致(其他线程,如线程2要竞争锁对象,而偏向锁不会主动释放因此还是存储的线程1的threadID),那么需要查看Java对象头中记录的线程1是否存活,如果没有存活,那么锁对象被重置为无锁状态,其它线程(线程2)可以竞争将其设置为偏向锁;如果存活,那么立刻查找该线程(线程1)的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。

    【b】轻量级锁升级成重量级锁

    大体过程如下图:

     

    文字描述如下【摘自https://blog.csdn.net/tongdanping/article/details/79647337】:

    • 1、线程1获取轻量级锁时会先把锁对象的对象头MarkWord复制一份到线程1的栈帧中创建的用于存储锁记录的空间(称为Displaced MarkWord),然后使用CAS把对象头中的内容替换为线程1存储的锁记录(DisplacedMarkWord)的地址;
    • 2、如果在线程1复制对象头的同时(在线程1CAS之前),线程2也准备获取锁,复制了对象头到线程2的锁记录空间中,但是在线程2CAS的时候,发现线程1已经把对象头换了,线程2的CAS失败,那么线程2就尝试使用自旋锁来等待线程1释放锁;
    • 3、如果自旋的时间太长也不行,因为自旋是要消耗CPU的,因此自旋的次数是有限制的,比如10次或者100次,如果自旋次数到了线程1还没有释放锁,或者线程1还在执行,线程2还在自旋等待,这时又有一个线程3过来竞争这个锁对象,那么这个时候轻量级锁就会膨胀为重量级锁。重量级锁把除了拥有锁的线程都阻塞,防止CPU空转;

    参考学习文章:

    https://blog.csdn.net/sspudding/article/details/89563462

    https://www.cnblogs.com/linghu-java/p/8944784.html

    https://www.jianshu.com/p/36eedeb3f912

    https://blog.csdn.net/tongdanping/article/details/79647337

    展开全文
  • 为弥补传统密码锁的不足,... 传统机械锁的防盗功能,在现代高科技安防系统无法起到作用,已逐步被更可靠、更智能的电子数字密码锁代替。目前市场上的大部分密码锁产品是以单片机为的,利用软件进行控制,实际
  • 如果了解过文件锁的用法,那理解数据库锁就简单了。锁其实就协调多个进程或线程并发时,处理访问同一个资源的机制。在项目开发,表锁是MySQL中作用范围较大的一种锁,它锁定的是用户操作的整张表,可以有效避免...

    前言

    如果了解过文件锁的用法,那理解数据库锁就简单了。锁其实就协调多个进程或线程并发时,处理访问同一个资源的机制。在项目开发中,表锁是MySQL中作用范围较大的一种锁,它锁定的是用户操作的整张表,可以有效避免死锁的情况,且加锁速度快,消耗资源小。事情总有两面性,也就是因为表锁是锁了整张表,在并发的时候锁冲突更大,并发能力也就差

    一、表锁的分类

    表锁根据操作的不同分为表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)

    1.读锁

    读锁表示用户读取数据(select)时添加的锁,此时不会阻塞其他用户对同一张表的读操作,但是会阻塞对同一张表的写请求

    2.写锁

    写锁表示用户对数据资源执行写(insert、update、delete)操作时添加的锁,此时除了当前添加写锁的用户外,其他用户都不能对表进行读、写操作,也就是说会阻塞其他用户对同一张表的读、写操作

    MyISAM表的读操作与写操作,以及写操作之间是串行的

    二、隐式的加锁

    当用户操作select查询数据表时(MyISAM表),MySQL服务器会自动地为查询的表添加一个表读锁;当执行增删改(insert、delete、update)操作时,同样MySQL服务器也会自动地为操作的表添加一个表写锁;直到操作结束完毕,服务器为其自动解锁。

    mysql> select * from order_step; 
    

    查询一张7万记录的表,注意表的存储引擎为MyISAM; 查询时间差不多3-5秒。此时新开一个终端,执行更新操

    mysql> update order_step set statusinner=2001 where stepid=75584;
    // 等待上一个终端执行结束
    

    隐式加锁的时候,表的更新操作优先级高于表的查询操作。在添加写锁时,若表中没有任何锁则添加,否则排队等待。在添加读锁时,若表中没有写锁则添加,否则也将排队等待

    三、显示加锁

    用户一般不需要直接用LOCK TABLE命令给MyISAM表显示加锁。我们为了学习,显示加锁来在一定程度模拟事务操作

    LOCK TABLES 数据表名 READ [LOCAL] WRITE,....
    

    1.创建MyISAM表,准备实验环境

    CREATE TABLE `user`(
    id INT PRIMARY KEY AUTO_INCREMENT,
    `username` VARCHAR(50),
    `money` DECIMAL(7,2)
    )ENGINE=myisam CHARSET=utf8; 
    
    INSERT INTO user(`username`,`money`) VALUES('乔峰',5);
    

    2.显式加表锁

    终端A:在自己操作表的时候,不希望其他用户去修改这这个表的数据

    mysql> lock tables user read;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from user;
    +----+-----------+--------+
    | id | username  | money  |
    +----+-----------+--------+
    |  1 | 乔峰      |   5.00 |
    +----+-----------+--------+
    1 rows in set (0.00 sec)
    

    终端B:

    mysql> update user set money=money+5 where id=1;
    // 等待终端A 释放锁
    

    终端A:释放锁

    mysql> unlock tables;
    Query OK, 0 rows affected (0.00 sec)
    

    3.支持并发时插入

    在上面例子中,当终端A没有释放锁的时候,此时终端是不能update的,同时也不能insert。

    mysql> insert into user(username,money) values('张三','100');
    // 插入等待
    

    考虑到并发的情况,我们在加锁的时候添加local选项。让其支持并发时插入

    mysql> lock tables user read local;
    Query OK, 0 rows affected (0.00 sec)
    

    四、查询表级锁争用情况

    mysql> show status like 'table%';
    +----------------------------+-------+
    | Variable_name              | Value |
    +----------------------------+-------+
    | Table_locks_immediate      | 86    |
    | Table_locks_waited         | 0     |
    | Table_open_cache_hits      | 2     |
    | Table_open_cache_misses    | 0     |
    | Table_open_cache_overflows | 0     |
    +----------------------------+-------+
    5 rows in set (0.01 sec)
    
    展开全文
  • 开放式速器位置:前后轴间、左右轮间限滑速器(1)电子辅助制动系统起到限滑作用(2)多片离合器式限滑速器图车辆中央速器为多片离合器形式,可依靠电机对前后桥车轮进行扭矩分配,在中央速器完全死时...

    df3779c34b5475542ffa14a140382b9d.png

    a5d683c019d5265f8ba99392757de9eb.gif

    a5f6e6d630835073cd16a9b9842f0773.png

    4243f33870894d4d9d0fdac68c5bdbdf.png

    开放式差速器

    位置:前后轴间、左右轮间

    8f45be1e19d4ccb0df64771e579ca826.png

    9429dc34fa363fdc164b1e2cdab0ff81.png

    限滑差速器

    c9e4a6f4dc2b58c19ad66aab0b6bed56.png

    (1)电子辅助制动系统起到限滑作用

    f51f6428dae8e81e5da96dd67ceedc4a.png

    2b8e412e7859b36caf53e8d7e441dedb.png

    (2)多片离合器式限滑差速器

    c9b299c6757bf71090f20b3056d4d7af.png

    图中车辆的中央差速器为多片离合器形式,可依靠电机对前后桥的车轮进行扭矩分配,在中央差速器完全锁死时,前后轴可实现50:50的扭矩分配,依据的道路情况,可以通过制动的方式使有附着力的车轮获得动力,实现脱困。

    8286d0e7872b62c3069c995e11edeeb6.png

    a309e5e8963bcfbbe64b42bb837fa449.png

    4ce0ff2196bfb08fa7907d36cb4bc013.png

    1bcf52409b302dcec8308ebdace90bd7.png

    (4)粘性联轴节式限滑差速器

    使用位置:前后轴间

    6d5f43667aac048f8a4994342954e379.png

    粘性联轴节式差速器通常安装在以前轮驱动为基础的四轮驱动汽车上。其特点也是不需驾驶员操纵,就可根据需要自动把动力分配给后驱动桥。当前轮出现打滑时,前后轮就会出现较大的转速差(前轮转速远比后轮快),此时,在前半段中央传动轴的带动下,粘性联轴节内部的硅油会被搅动起来,基于其受热膨胀的物理特性,离合器片会被挤压,进而实现前后两段传动轴近乎刚性的连接状态,前轮空转流失的动力便可被输送至后轮(受结构所限,这部分动力最多不会超过发动机输出动力的30%)。这种差速器最大的缺点是四驱系统迟滞性较明显,且这种动力传递方式效率不高,所以采用这种差速器的车辆其定位在于简单条件下的烂路行驶,高强度的非铺装路面通过性显然不是它的“专长”。此外,装配了这种粘性联轴节式限滑差速器的车辆是不允许采用将驱动轮抬起的方式进行拖车的,由于该装置不具备完全切断的能力,如果采用这种方式进行拖车,则刚好满足了它的限滑条件(前轮不动,后轮旋转),使得粘性联轴节将前后两段传动轴进行连接,由于前后传动轴与各自的车桥不具备差速的能力,所以,这会给四驱系统带来损伤。

    ***

    点击图书封面了解更多

    ed7e064f7af2d3c9a9aaa4810f0f41a1.png

    《视频图解汽车构造与原理

    作者:陈新亚

    售价:¥69.90

    上市时间:2020年6月

    89916584a83e4a6b39220ac9a7705781.png

          《视频图解汽车构造与原理》书中附有103个视频,配合400多幅精美结构图,精准的中文和英文图注说明,按照汽车专业教材的编排顺序,以一问一答的形式通俗讲解汽车的构造、原理及最新技术和配置等,并对新能源汽车、驾驶辅助技术等进行了特别介绍。

          书中所配视频以超清画面为主,大多配有中文字幕说明,直观、清晰、简洁、专业,非常适合汽车专业师生、汽车爱好者、车主和汽车行业从业人员观看阅读。

    89916584a83e4a6b39220ac9a7705781.png

    10ed9114786073fc38fe0b9849004ab8.png

    0452e0657afc797262a1c9f2f1fa5634.gif

    cb8dbf4d3db93d3bb6f191d4f3b118a3.png

    往期回看

    1105259f4130c36e2e14087fdf4cb69e.png

    万向传动装置的拆装

    车辆驱动形式及优缺点解析

    新能源汽车高压配电系统常见故障及排除方法

    解析汽车离合器的组成结构和工作原理

    燃料电池汽车的工作原理

    展开全文
  • C#线程介绍源码

    2014-03-12 08:43:25
    lock(Monitor.Enter和Monitor.Exit方法的封装),主要作用是锁定临界区,使临 界区代码只能被获得锁的线程执行。Monitor.Wait和Monitor.Pulse用于线程同步,类似信号操作,个人感觉使用比较复杂,容易造成死 锁。 ...
  • 摘要:本文论述了基于霍耳效应传感芯片电路设计原理和方法,并以0.5um, 双层金属,65V高压CMOS工艺实现,电路实现了包括磁滞,防相位死与自动重启动等功能,并注意了功率输出管电压钳位,以及减少尖峰电流...
  • JUC笔记(作用

    2021-02-01 21:50:44
    Java 语言包含两种内在同步机制:同步块(或方法)和 volatile 变量,相比于synchronized(synchronized通常称为重量级),volatile更轻量级,因为它不会引起线程上下文切换和调度。但是volatile 变量同步性...

    JUC笔记(作用)

    二、理解

    volatile

    volatile是Java提供的一种轻量级的同步机制。Java 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量,相比于synchronized(synchronized通常称为重量级锁),volatile更轻量级,因为它不会引起线程上下文的切换和调度。但是volatile 变量的同步性较差(有时它更简单并且开销更低),而且其使用也更容易出错。

    1. 保证可见性

      1. 线程A操作完后,会将工作内存的值刷新到主存中
    2. 不保证原子性

      1. 多个线程操作时会导致值覆盖,使得不能保持原子性问题
      2. 使用AtomicInteger保持原子性
    3. 禁止指令重排

      什么是 指令重排:你写的程序,计算机并不是按照你写的那样去执行的。
      源代码–>编译器优化的重排–> 指令并行也可能会重排–> 内存系统也会重排—> 执行
      处理器在进行指令重排的时候,考虑:数据之间的依赖性!

       指令重排:程序运行时的样子不是你所理想型的样子,在不影响程序结果的前提下,指令执行的顺序可以进行调换
         * 初始化参数:a、b、x、y都为0
         * 指令重排前的样子:
         * 线程A:x = a;b = 1* 线程B:y = b;a = 2* 结果:x = 0;y = 0* 指令重排后的样子:
         * 线程A:b = 1;x = a;
         * 线程B:a = 2;y = b;
         * 结果:x = 2;y = 1

    CAS机制

    CAS (Compare-And-Swap) 是一种硬件对并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问

    1. CAS是比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就
      一直循环!
    2. Java的CAS底层是使用Unsafe类操作内存地址值
    3. 缺点:
      1. 循环会耗时
      2. 一次性只能保证一个共享变量的原子性
      3. ABA问题:A值被另一个线程进行了操作形成了新的值

    原子引用:解决ABA问题,引用原子引用。乐观锁的思想

    1. 对操作的对象进行版本编号
    2. 每次线程操作时,进行版本号对比
      1. 成功:进行操作
      2. 失败:不进行操作

    阻塞队列

    阻塞队列,可以理解成生产者消费者模式,是一个支持两个附加操作的队列。

    • 当队列为满的时候,插入元素的线程被阻塞,直达队列不满。
    • 当队列为空的时候,获取元素的线程被阻塞,直到队列不空。
    • ArrayBlockingQueue:由数组结构组成的有界阻塞队列,按照先进先出原则,要求设定初始大小。
    • LinkedBlockingQueue:由链表结构组成的有界,按照先进先出原则,可以不设定初始大小,但是默认值大小为Integer,MAX_VALUE的阻塞队列。
    • PriorityBlockingQueue:支持优先级排序的无界阻塞队列。默认情况下,按照自然顺序,要么实现compareTo()方法,指
      定构造参数Comparator。
    • DelayQueue:一个使用优先级队列实现的无界阻塞队列,支持延时获取元素的阻塞队列,元素必须要实现Delayed接口。适用场景:实现自己的缓存系统,订单到期,限时支付等等。
    • SynchronousQueue:一个不存储元素的阻塞队列,没有容量,每一个put操作都要等待一个take操作,所以会产生生产者和消费者能力不匹
      配的问题。
    • LinkedTransferQueue:由链表结构组成的无界阻塞队列。transfer(),必须要消费者消费了以后方法才会返回,tryTransfer()
      无论消费者是否接收,方法都立即返回。
    • LinkedBlockingDeque:由链表结构组成的双向阻塞队列。可以从队列的头和尾插入和移除元素,实现工作密取,方法名带了First对头部操作,带了last从尾部操作,另外:add=addLast;
      remove=removeFirst; take=takeFirst。

    BlockingQueue方法有四种API形式

    抛出异常 返回值 阻塞 超时
    插入 add(Object obj) offer(Objject obj) put(Object obj) offer(Object obj,int time,unit)
    移除 remove() pull() take() pull(time,unit)
    检查 element() peek() 不可用 不可用

    线程池

    三大方法七大参数四种拒绝策略

    线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程

    好处:

    1. 降低资源的消耗
    2. 提高响应的速度
    3. 对线程方便管理

    三大方法

    // 单个线程
    ExecutorService threadPool01 = Executors.newSingleThreadExecutor();
    // 创建一个固定的线程池的大小
    ExecutorService threadPool02 = Executors.newFixedThreadPool(5);
    // 可伸缩的,遇强则强,遇弱则弱
    ExecutorService threadPool03 = Executors.newCachedThreadPool();
    

    阿里手册说明使用ThreadPoolExecutor来创建线程、规避资源耗尽的风险

    public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
    (new ThreadPoolExecutor(1, 1,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>()));
    }
    public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(5, 5,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>());
    }
    public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
    60L, TimeUnit.SECONDS,
    new SynchronousQueue<Runnable>());
    }
    

    七大方法:

    // 本质ThreadPoolExecutor()
    public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
    int maximumPoolSize, // 最大核心线程池大小
    long keepAliveTime, // 超时了没有人调用就会释放
    TimeUnit unit, // 超时单位
    BlockingQueue<Runnable> workQueue, // 阻塞队列
    ThreadFactory threadFactory, // 线程工厂:创建线程的,一般不用动
    RejectedExecutionHandler handle // 拒绝策略) {
                              
    if (corePoolSize < 0 ||
    maximumPoolSize <= 0 ||
    maximumPoolSize < corePoolSize ||
    keepAliveTime < 0)
    throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
    throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
    null :
    AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
    }
    

    四种拒绝策略就是对应阻塞队列的四种API

    Lock锁

    Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。它们允许更灵活的结构化,可能具有完全不同的属性,并且可以支持多个相关联的对象Condition

    通过Condition实现等待通知机制

    Condition类可以实现多路通知功能,就是Lock对象可以创建多个Condition,将线程对象注册在指定的Condition中,就可以实现有通知性地进行线程通知,在调度线程上更加灵活了。

    Synchronized 和 Lock 区别

    1. Synchronized 内置的Java关键字, Lock 是一个Java类
    2. Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
    3. Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
    4. Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下
      去;
    5. Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以
      自己设置);
    6. Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!

    各种锁的理解

    公平锁:从字面来看 "公平"两个字体现,线程获取锁的顺序是按照线程加锁的顺序来分配的,也就是队列中常说的先进先出的顺序。

    非公平锁:就是以一种抢占的方式来获取锁,是随机获得锁,这样靠抢占的方式肯定会出现有的线程会获不到锁,所以说叫非公平的。

    可重入锁:线程里面锁可以进行重复加锁操作。注意点lock锁每次都是一把新锁

    自旋锁: 它是为实现保护共享资源而提出一种锁机制

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IYFs1KOX-1612187380220)(13_Java中JUC.assets/1612186894377.png)]

    死锁:线程间抢夺对方的锁,导致出现死锁问题

    死锁测试:线程A锁抢夺线程B的锁,线程B的锁抢夺线程A的锁

    解决方案:

    1. 使用 jps -l 定位进程号
    2. 使用 jstack 进程号 找到死锁问题

    ForkJoin

    大任务可以拆成小任务,小任务还可以继续拆成更小的任务,最后把任务的结果汇总合并,得到最终结果,这种模型就是Fork/Join模型

    java.util.concurrent包中的以下四个类是学习fork/join框架的核心:

    • ForkJoinPool
    • ForkJoinTask
    • RecursiveAction
    • RecursiveTask

    ForkJoinPool类的一个实例表示一个线程池。 ForkJoinTask类的一个实例表示一个任务。

    ForkJoinTask类是一个抽象类。它有两个具体的子类:RecursiveAction和RecursiveTask。

    Java 8添加了一个称为CountedCompleter的ForkJoinTask类的抽象子类。

    该框架支持两种类型的任务:不产生结果的任务和产生结果的任务。

    RecursiveAction类的实例表示不产生结果的任务。 RecursiveTask类的实例表示产生结果的任务。

    CountedCompleter任务可能产生结果,也可能不产生结果。

    这两个类,RecursiveAction和RecursiveTask,提供了一个抽象的compute()方法。

    我们应该继承这些类之一,并为compute()方法提供一个实现。

    • 常用的辅助类

    1. CountDownLatch(减法计数器)

    允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。

    CountDownLatch一个有用的属性是,它不要求调用countDown线程等待计数到达零之前继续,它只是阻止任何线程通过await ,直到所有线程可以通过

    1. CyclicBarrier(加法计数器)

    允许一组线程全部等待彼此达到共同屏障点的同步辅助。 循环阻塞在涉及固定大小的线程方的程序中很有用,这些线程必须偶尔等待彼此。 屏障被称为循环 ,因为它可以在等待的线程被释放之后重新使用。

    等待所有的线程执行完成才会释放

    1. Semaphore(信号量)

    一个计数信号量。 在概念上,信号量维持一组许可证。 如果有必要,每个acquire()都会阻塞,直到许可证可用,然后才能使用它。 每个release()添加许可证,潜在地释放阻塞获取方。 但是,没有使用实际的许可证对象; Semaphore只保留可用数量的计数,并相应地执行

    一种限流操作:资源溢满时,等待。资源释放时,使用

    • 高并发下的集合类

    在高并发情况下,Java之前的集合类都是不安全。会出现java.util.ConcurrentModificationException 并发修改异常!

    1. List
      1. 使用原生线程安全的类:new Vector()
      2. 使用工具类创建线程安全:Collections.synchronizedList(new ArrayList<>());
      3. 使用JUC下的线程安全类:CopyOnWriteArrayList
        1. CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略;
        2. 多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
        3. 在写入的时候避免覆盖,造成数据问题!
    2. Set
      1. 使用工具类创建线程安全:Collections.synchronizedSet(new HashSet<>());
      2. 使用JUC下的线程安全类:CopyOnWriteArraySet
    3. Map
      1. 使用工具类创建线程安全:Collections.synchronizedMap(new HashMap<>())
      2. 使用JUC下的线程安全类:ConcurrentHashMap
    展开全文
  • Synchronized和ReentrantLock的作用都可以在多线程对于共同访问的资源进行加锁,它们的区别在这这里记录下。 1.锁的实现 ReentrantLock是JDK实现的 Synchronized 是JVM实现; 前者可以直接看到源码,后者实现难以...
  • 作用在于:当一个对象需要被多线程共享,并且频繁访问时,可以省略同步和等待时间,提高效率。正因为这个不可变性,在需要对字符串进行修改操作时,String对象总是会生成新对象,所以性能相对较。此时需要...
  • 反射的作用想必大家都知道了吧,少量属性的手动添加几下当然是没有问题的,但是属性数量较多的时候敲起这些繁锁的代码可就困了,再说对扩展性和维护性很,以下代码如不能直接使用请添加using System.Text;...
  • Oracle一般使用

    2020-08-23 15:42:40
    -增删改表数据----序列--解锁scott用户---函数的使用---条件表达式---分组查询---笛卡尔积---等值连接---内连接---外连接---子查询----oracle的分页进阶部分---视图---创建视图【必须有dba权限】---视图的作用?...
  • 内存处理是编程人员容易出现问题地方,忘记或者错误内存回收会导致程序或系统不稳定甚至崩溃,Java提供GC功能可以自动监测对象是否超过作用域从而达到自动回收内存目的,Java语言没有提供释放已分配内存...
  • DC-DC开关转换器的作用是将一个直流电压有效转换成另一个。高效率DC-DC转换器采用三项基本技术:降压、升压,以及降压/升压。 降压转换器用于产生低直流输出电压,升压转换器用于产生高直流输出电压,降压/升压转换器则...
  • flashmtv制作

    2011-11-17 18:15:11
    添加辅助线的作用:是为了防止导入到舞台的图片盖住舞台,看不到舞台大小。 三:导入图片和声音的方法和步骤: (1)选择图片的要求: 选择图片时,一定要选择小图放大后的图片保存,这样的图片才能使用。每个...
  • 数据库系统实现

    2013-05-12 13:09:11
    9.3 使用锁的可串行性实现 9.3.1 锁 9.3.2 封锁调度器 9.3.3 两阶段封锁 9.3.4 两阶段封锁发挥作用的原因 习题 9.4 用多种锁方式的封锁系统 9.4.1 共享锁与排他锁 9.4.2 相容性矩阵 9.4.3 锁的...
  • Android线程池

    千次阅读 2016-05-30 23:50:57
    概述作用为什么不能在主线程进行耗时操作? 因为会导致体验不流畅、卡顿,体验。 Handler出现为了解决在子线程操作完成了无法访问UI线程(主线程)矛盾。 为什么不能在子线程更新UI? 因为多线程...
  • 目录前言定义死锁代码解决方案:方案一 设立时间方案二 避免锁的嵌套方案三 设置标志位案例(面试提问)总结 前言 死锁不是一种正常的现象, 是在多线程程序需要避免的 定义 多个进程在执行过程,因争夺同类...
  • Handler消息机制

    2018-11-06 17:35:54
    Handler消息机制的作用: 将一个任务切换到指定线程(handler所在线程)运行,多用来在主线程更新UI 由来: 首先android ui线程不是线程安全的,如果不同线程都去访问UI那么会出现不可预期的下效果; ...
  • Android消息机制

    千次阅读 2016-06-02 00:40:25
    概述 作用 为什么不能在主线程进行耗时操作? 因为会导致体验不流畅、卡顿,体验。 Handler出现为了解决在子线程操作完成了无法...通过给UI控件访问加上 1. 加锁会让UI访问逻辑变得异常复杂 2.
  • 设备驱动程序的作用 内核功能划分 设备和模块的分类 安全问题 版本编号 许可证条款 加入内核开发社团 本书概要 第二章 构造和运行模块 设置测试系统 Hello world模块 核心模块与应用程序的对比 编译和装载 内核符号...
  • 岳维功 ortp-realease.pdf

    2020-12-24 04:00:17
    时间戳增量:相邻两个包之间时间(以时间戳单位为基准)。 采样频率:每秒钟抽取样本次薮,例如音频采样率一般为 帧率: 每秒传输或者显示帧数,例如 在协议并没有规定吋间戳粒度,这取决于有效载付类型。因此...
  • 2、液力变矩器单向离合器和止离合器的作用 3、辛普森轮系和拉维娜轮系的结构特点 4、自动变速器离合器、制动器、单向离合器的作用 5、主油路调压阀、手动阀、换档阀、OD开关、强制降档开关的作用 6、换档规律...
  • PLL与DLL锁相环介绍

    千次阅读 多人点赞 2018-02-08 10:29:09
    一、PLL锁相环:Phase-Locked ...形成压控振荡器(Vco)控制电压Uc,Uc作用于压控振荡器结果是把它输出震荡频率fo拉向环路输入信号频率fi,当二者相等时,环路被锁定,称为入。PLL并不是直接对晶振进行倍频...
  • 锁相环--PLL

    2014-08-19 16:26:58
    中的噪声和干扰成分被低通性质环路滤波器滤除,形成压控振荡器(VCO)控制电压Uc。Uc作用于压控振荡器结果是把它输出振荡频率fo拉向环路输入信号频率fi ,当二者相等时,环路被锁定 ,称为入。维持锁定...
  • java面试题

    2018-04-27 19:55:38
    答:应用程序域可以理解为一种轻量级的进程,起到安全的作用,占用资源小。 Class.forName作用? 答:调用该访问返回一个以字符串指定类名的类对象。 JDO? 答:JDO是java对象持久化的新的规范,为java data object...

空空如也

空空如也

1 2 3 4
收藏数 64
精华内容 25
关键字:

中差锁的作用