锁优化的建议

1、减小锁持有时间

它的核心思想就是不要在不必需要的代码上加锁,因为线程持有的锁时间过长,相对地,锁的竞争程度也就越激烈。

看下面的代码:

public synchronized void syncMethod() {
        othercode1();
        mutextMethod();
        othercode2();
    }

    public void syncMethod() {
        othercode1();
        synchronized (this) {
            mutextMethod();
        }
        othercode2();
    }

2、减小锁锁粒度

减小锁粒度也是一种削弱多线程锁竞争的有效手段。典型的使用场景就是ConcurrentHashMap。

对于HashMap来说,最重要的两个方法就是get和put方法。一种最自然的想法就是对整个HashMap加锁,必然可以得到一个线程安全的对象。但是这样做,加锁的粒度太大,对于ConcurrentHashMap,它内部进一步细分了若干个小的HashMap,称之为段 segment。默认情况下ConcurrentHashMap,被进一步细分为16个段。

如果需要在ConcurentHashMap中增加一个新的表项,并不是将整个HashMap加锁,而是首先根据hashcode得到该表项应该被存放到那个段中,然后对该段加锁,并完成put操作。在多线程环境中,如果多个线程同时进行put操作,只要被加入的表项不存放在同一个段中,则线程间便可以做到真正的并行。

@SuppressWarnings("unchecked")
    public V put(K key, V value) {
        Segment<K,V> s;
        if (value == null)
            throw new NullPointerException();
        int hash = hash(key);
        int j = (hash >>> segmentShift) & segmentMask;
        if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
             (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
            s = ensureSegment(j);
        return s.put(key, hash, value, false);
    }

3、 读写分离锁来替换独占锁

使用读写锁ReadWriteLock可以提供系统的性能。

4、 锁分离

如果将读写锁的思想做进一步的延伸,就是锁分离。典型的案例就是LinkedBlockingQueue的实现。

如果使用独占锁,则要求在两个操作进行时,获取当前队列的独占锁,那么take和put操作就不可能真正的并发,在运行时,它们会彼此等待对方释放锁资源。在这种情况下,锁竞争会相对比较激烈,从而影响程序在高并发的性能。

因此在JDK的实现中,并没有采用这样的方式,取而代之的是把两把不同的锁,分离了take和put操作。

  /** Lock held by take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Wait queue for waiting takes */
    private final Condition notEmpty = takeLock.newCondition();

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

    /** Wait queue for waiting puts */
    private final Condition notFull = putLock.newCondition();
/** Current number of elements */
    private final AtomicInteger count = new AtomicInteger(0);

下面来看下take的源码:

 public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();//只能有一个线程take元素。
        try {
            while (count.get() == 0) {// 没有数据时,线程进入等待状态。
                notEmpty.await();
            }
            x = dequeue(); // 获取一个元素  
            c = count.getAndDecrement();//元素的数量减一
            if (c > 1)
                notEmpty.signal();// 大于1,唤醒其他线程进行take操作。
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        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);
        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();//数量加一
            if (c + 1 < capacity)// 还有空间,唤醒其他的线程进行put操作。
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
    }

5 、锁粗化

虚拟机在遇到一连串连续地对同一锁不断进行请求和释放的操作时,便会把所有的锁操作整合成对锁的一次请求,从而减少对锁的请求同步次数,这个操作叫做锁的粗化。

看下面的代码就清楚了

public void syncMethod() {

        synchronized (this) {
            mutextMethod2();
        }
        synchronized (this) {
            mutextMethod2();
        }

    }
public void syncMethod() {

        synchronized (this) {
            mutextMethod1();
            mutextMethod2();
        }


    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值