如果提高锁的性能

一.如何提高锁性能
1.减少持有锁时间
    减少锁的持有时间有助于降低锁冲突的可能性。
2.减小锁粒度
    对于HashMap来说,最重要的两个方法就是get()和put()。一种最自然的方法就是对整个HashMap加锁,必然可以得到一个线程安全的对象(锁粒度过大)。对于ConcurrentHashMap
,它内部细分为了若干个小的HashMap,称之为段(segment)。默认情况下,被分为了16个segment。如果需要在ConcurrentHashMap中增加一个新的表项,并不是将整个HashMap加锁,
而是根据hashcode得到该表项应该被存放在那个段中,然后对该段加锁,并完成put()操作。在多线程环境中,如果多个线程同时进行put()操作,只要被加入的表项不是在同一个段中,则线
程间便可以做到真正的并行。

注意:当减少锁粒度将会引入一个新的问题,当系统需要获取全局锁时,其消耗的资源会比较多。例如当访问ConcurrentHashMap的全局信息时,就需要同时取得所有段的锁,size()方法就是很好的例子,但是ConcurrentHashMap并不是总会这样执行的,size()方法会先使用无锁的方式求和,如果失败了才会尝试这种通过加锁的方式获取信息。

3.读写分离锁来替代独占锁
    适当的使用读写锁ReadWriteLock可以提高系统的性能(读多写少的情况下)。
4.锁分离
    将读写锁的功能衍生,就是锁分离。读写锁根据读写操作上的不同,进行了有效的锁分离。最典型的例子就是LinkedBlockingQueue的实现,take()函数和put()函数分别实现
了从队列中取得数据和往队列中增加数据的功能,虽然两个函数都对当前队列进行了修改操作,但由于LinkedBlockingQueue是基于链表的,因此两个操作分别作用于队列的前端和尾
端,在JDK的实现中通过两把不同的锁分离了take()和put()操作。
5.锁粗化
    通常情况下,为了保证多线程的有效并发,会要求每个线程持有锁的时间尽量短,即在使用完公共资源后,应该立即释放锁。只有这样,等待在这个锁上的其他线程才能尽早的获得
资源执行任务。但是如果对同一个锁不停的进行请求、同步和释放,其本身也会消耗系统宝贵的资源,反而不利于系统的优化。
    为此,当虚拟机在遇见一连串连续的对同一锁不断请求和释放的操作时,便会将所有的锁操作整合成对锁的一次请求,从而减少对锁的请求同步次数,这个操作称之为锁粗化,例如代码段
    for(int i = 0; i < 10; i++){
        synchronized(lock){
            //doSomething
        }
    }
    可改为:
    synchronized(lock){
        for(int i = 0; i < 10; i++){
            //doSomething
        }
    }
二.Java虚拟机对锁的优化
1.锁偏向
    如果一个线程获得了锁,那么锁就进入了偏向模式,当这个线程再次请求锁时,无需再做任何同步操作。
2.轻量级锁
    如果锁偏向失败,虚拟机并不会立即挂起线程,它还会使用一种成为轻量级锁的优化手段。通过对象头部作为指针,指向持有锁线程堆栈内部,来判断一个对象是否持有锁。如果线
程获取轻量级锁成功,则顺利进入到临界区。如果失败,锁被其他线程所抢到,那么当前线程的锁请求就会膨胀为重量级锁。
3. 自旋锁
    锁膨胀后,虚拟机为了避免线程真实地在操作系统层面挂起,虚拟机还会在做最后的努力--自旋锁。由于当前线程无法获得锁,但是在什么时候获得所又是个未知数。也许在几个
CPU时钟周期后,就可以得到锁。系统将会进行一次赌注:它会假设在不久的将来可以获取到这把锁,因此虚拟机将会让当前线程做几个空循环,在经过若干次循环后,如果得到锁,则顺
利进入到临界区,否则,将会将该线程在操作系统层面挂起。
4.锁清除
    Java虚拟机在JIT编译时,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁。通过对锁的清除,可以节省毫无意义的请求锁时间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值