锁的优化及注意事项(读书笔记)

Darcy_wang Darcy_wang     2022-08-19     763

关键词:

有助于提高锁性能的几点建议
  • 减少锁的持有时间 真正需要同步加锁的时候在加锁,减少锁的持有时间有助于减低锁冲突的可能性,进而提升系统的并发能力,
  • 减少颗粒度,所谓减少颗粒度就是指缩小锁定对象的范围,从而减少锁冲突的可能性,进而提升系统的并发能力,问题在于类似于size()获取全局信息的方法调用并不频繁时,这种减少颗粒度的方法才能真正意义上提高系统吞吐量.(分割数据结构实现)
  • 读写分离锁替换独占锁,读写锁是对系统功能点的分割 ReadWriteLock 在读多写少的场合,读写锁对系统性能是很有好吃的,因为如果系统在读写数据时均只使用独占锁,那么读操作和写操作间,读操作和读操作间,写操作和写操作间均不能做到真正的并发,并且需要互相等待.而读操作本身不会影响数据的完整性和一致性,因此 理论上讲,在大部分情况下,应该允许多线程同时读,
     锁分离
          如果将读写锁的思想做进一步的延伸,就是锁分离,读写锁根据读写操作功能上的不同,进行了有效的锁分离,依据应用程序的功能特点,使用类似的分离思想,也可以对独占锁进行分离,一个典型案例就是java.util.concurrent.LinkedBlockingQueue的实现.
          在LinkedBlockingQueue的实现中,take()函数和put()函数分别实现了从队列中取得数据和往队列中增加数据的功能,虽然两个函数都对当前队列进行了修改操作,但由于LinkedBlockingQueue是基于链表的,因此,两个操作分别作用于队列的前端和尾端,从理论上讲 是不冲突的.
          如果使用独占锁,则要求两个操作进行时获取当前队列的独占锁,那么take()和put()操作就不可能真正的并发,在运行时,他们会彼此等待对方释放资源,在这种情况下,锁的竞争会相对比较激烈,从而影响程序在高并发时的性能!
          因此在JDK的实现中,并没有采用这种方式,取而代之的是两把不同的锁,分离了take()和put()操作.
/** Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();//take锁

/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();//生成一个take锁的绑定Condition实例

/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();//put锁

/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();//put锁
     以上的代码片段,定义了takeLock和putLock,他们分别在take()操作和put()操作中使用,因此,take()函数和put()函数就此互相独立,他们之间不存在锁竞争关系,只需要在take()和take()间,put()和put()间分别对takeLock和putLock进行竞争,从而 削弱了锁竞争的可能性.
     函数take()的实现下:
public E take() throws InterruptedException {
    E x;
    int c = -1;
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();//加锁,可以响应中断的锁
    try {
        while (count.get() == 0) {//如果没有数据,一直等待
            notEmpty.await();//等待put()操作的通知
        }
        x = dequeue();//取得第一个数据
        c = count.getAndDecrement();//数量减1,原子操作,因为会和put()函数同时访问count.注意:变量c是count减1前的值
        if (c > 1)
            notEmpty.signal();//通知其他take操作
    } finally {
        takeLock.unlock();//释放锁
    }
    if (c == capacity)
        signalNotFull();//通知put()操作,已有空余空间
    return x;
}
     方法put()的实现如下:
public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    // Note: convention in all put/take/etc is to preset local var
    // holding count negative to indicate failure unless set.
    int c = -1;
    Node<E> node = new Node<E>(e);
    final ReentrantLock putLock = this.putLock;
    final AtomicInteger count = this.count;
    putLock.lockInterruptibly();//加锁,可以相应中断的锁
    try {
        /*
         * Note that count is used in wait guard even though it is
         * not protected by lock. This works because count can
         * only decrease at this point (all other puts are shut
         * out by lock), and we (or some other waiting put) are
         * signalled if it ever changes from capacity. Similarly
         * for all other uses of count in other wait guards.
         */
        while (count.get() == capacity) {//当数据满了时候
            notFull.await();//等待
        }
        enqueue(node);//插入数据
        c = count.getAndIncrement();//更新数据,变量c是count加1前的值
        if (c + 1 < capacity)
            notFull.signal();//有足够空间,通知其他线程
    } finally {
        putLock.unlock();//释放锁
    }
    if (c == 0)
        signalNotEmpty();//插入成功后,通知take操作取数据
}
     通过takeLock和putLock两把锁,LinedBlockingQueue实现了取数据和写数据的分离,是两者在真正意义上成为可并发的操作
  • 锁粗化
     通常情况下 为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽量的短,就是使用完公共资源后,立即释放锁,只有这样,等待爱这个锁上的其他线程才能今早的获得资源执行任务,但是,凡是都要有一个度,如果对用一个锁不停的请求,同步,释放,其本身也会消耗系统宝贵的资源,反而不利于性能的优化,
     为此,虚拟机在遇到一连串的连续第对同一个锁不断进行请求和释放的操作时,便会把所有的锁操作整合成对锁的一次请求,从而减少对锁的请求同步次数,这个操作叫做锁的粗化,
     在开发过程中,我们应该有意识地在合适的场合进行锁的粗化,尤其是在循环内请求锁时,
性能优化就是根据运行时的真实情况对各个资源点进行权衡折中的过程,锁粗化的思想和减少所持有时间是相反的,但在不同的场合,他们的效果并不相同,所以大家需要根据实际情况,进行权衡. 

锁的优化及注意事项

1、减少锁的持有时间   比如用synchronized,最好使用synchronized(this)去修饰还需要同步的方法,减少锁持有的时间。2、减少所得粒度  比如ConcurrentHashMap,在其内部分为16个(默认)Segment。如果需要在ConcurrentHashMap... 查看详情

4.1锁的优化及注意事项(代码片段)

一:减少锁的持有时间:  例如:  publicsynchronizedvoidall()aaa();bbb();ccc();如果就bbb方法存在并发情况,需要加锁,那么我们只需要给bbb方法添加同步方法即可:publicvoidall()aaa();synchronized(this)bbb();ccc();二:减小锁的粒度... 查看详情

详解synchronized锁的各种用法及注意事项(代码片段)

...来阐述synchronized锁的各种用法以及使用synchronized锁的相关注意事项,记录下来同时也方便自己记忆。synchronized锁是jvm内置的锁,不同于Reentra 查看详情

重入锁的好搭档:condition条件(读书笔记)

 Condition结构提供了基本方法如下:voidawait()throwsInterruptedException;voidawaitUninterruptibly();longawaitNanos(longnanosTimeout)throwsInterruptedException;booleanawait(longtime,TimeUnitunit)throwsInterrup 查看详情

synchronized的功能拓展:重入锁(读书笔记)

...可以完全代替synchronized关键字.在JDK5.0的早期版本中,重入锁的性能远远好于synchronized,但是从JDK6.0开始.JDK在synchronized上做了大量的优化.使得两者的性能差距不大,publicclassReenterLockimplementsRunnable{publicstaticReentrantLocklock=newReentrant 查看详情

锁的优化和注意事项

锁优化分为代码层面的优化和jvm层面的优化1.代码层面的锁优化的思路和方法一旦用到锁,就说明这是阻塞式的,所以在并发度上一般来说都会比无锁的情况低一点。这里提到的锁优化,是指在阻塞式的情况下,如何让性能不要... 查看详情

《深入理解java虚拟机》读书笔记:晚期(运行期)优化

文章目录正文一、HotSpot虚拟机内的即时编译器1、解释器与编译器(1)解释器、编译器(2)C1、C2编译器(3)混合模式、解释模式与编译模式(4)分层编译2、编译对象与触发条件(1)热点... 查看详情

《go语言精进之路》读书笔记|了解go语言控制语句惯用法及使用注意事项(代码片段)

...一条中我们就来了解一下Go语言控制语句的惯用法及使用注意事项。19.1使用if控制语句时应遵循“快乐路径”原则所谓“快乐路径”即成功逻辑的代码执行路径,这个原则要求:当出现错误时,快速返回;成功逻... 查看详情

java程序优化(读书笔记)

--From:JAVA程序性能优化(葛一鸣,清华大学出版社,2012/10第一版) 1.java性能调优概述  1.1性能概述     程序性能:执行速度,内存分配,启动时间,负载承受能力。    性能指标:执行时... 查看详情

understandingoptimizerstatistics(理解优化器统计信息)读书笔记(代码片段)

本文为白皮书UnderstandingOptimizerStatistics(理解优化器统计信息)的读书笔记。此白皮书的后续第二部分为BestPracticesforGatheringOptimizerStatistics。此白皮书的19c版本参考这里。简介最初,决定如何执行SQL语句由RBO(RuleBasedOptimizer&#... 查看详情

实战java高并发程序设计-读书笔记(代码片段)

实战Java高并发程序设计-读书笔记第一章死锁、饥饿、活锁的概念。并发级别:阻塞、饥饿、无障碍、无锁、无等待。无障碍:是一种最弱的非阻塞调度。两个线程如果是无障碍的执行,那么他们不会因为临界区的问... 查看详情

图解性能优化读书笔记

1.部分性能优化用的命令sar-f 获取一段时间内的服务器信息。vmstat 5 5每五秒钟显示五次的命令。ps-elf显示正在运行的进程信息。netstat-rnetstat-a显示网络路由信息和网络所有的端口信息---windows可以用|findstr"1521‘linux使... 查看详情

《android群英传》---读书笔记9(代码片段)

...笔记9标签():android中级读书笔记Android性能优化知识点:布局优化内存优化使用各种工具来进行分析,优化10.1布局优化在Android系统中,系统通过VSYNC信号触发对UI的渲染,重绘,其间隔时间为16ms,... 查看详情

android性能优化读书笔记bitmap解码(代码片段)

在项目中有如下的代码,使用BitmapFractory.decodeFile来对Bitmap进行解码。如下代码所示:publicstaticBitmapgetSmallBitmap(StringfilePath)finalBitmapFactory.Optionsoptions=newBitmapFactory.Options();options 查看详情

android性能优化读书笔记bitmap解码(代码片段)

在项目中有如下的代码,使用BitmapFractory.decodeFile来对Bitmap进行解码。如下代码所示:publicstaticBitmapgetSmallBitmap(StringfilePath)finalBitmapFactory.Optionsoptions=newBitmapFactory.Options();options 查看详情

android性能优化读书笔记bitmap解码(代码片段)

在项目中有如下的代码,使用BitmapFractory.decodeFile来对Bitmap进行解码。如下代码所示:publicstaticBitmapgetSmallBitmap(StringfilePath)finalBitmapFactory.Optionsoptions=newBitmapFactory.Options();options 查看详情

[读书笔记]机器学习:实用案例解析

第7章 优化:密码破译优化简介:最优点(optimum),优化(optimization)本章研究的问题:构建一个简单的密码破译系统,把解密一串密文当做一个优化问题。优化方法:网格搜索(gridsearch),主要问题是1、步长的选择;2、... 查看详情

android深度探索——第十章读书笔记及心得

嵌入式linux的调用技术——第10章读书笔记及心得通过本章的学习了解了printk函数。该函数与printf函数类似,用于打印内核调试信息。只是前者运行在内核空间,后者运行在用户空间。即linux驱动这样的linux内核程序只能使... 查看详情