動態

詳情 返回 返回

Netty源碼解析-鎖機制 - 動態 詳情

為了提高性能,Netty對鎖也做了大量優化

1、鎖優化技術

Netty大量使用了鎖優化技術:

  • 1.1 減小鎖粒度
  • 1.2 減少鎖對象的空間佔用
  • 1.3 提高鎖的性能
  • 1.4 根據不同業務場景選擇合適鎖
  • 1.5 能不用鎖則不用鎖

1.1 減小鎖粒度

在Netty4.1.15.Final版本中ServerBootstrap.init方法中有兩個地方對對象加鎖,而不是在方法上加一個大鎖,縮小了鎖範圍,如下圖

1.2 減少鎖對象的空間佔用

源碼ChannelOutboundBuffer類,如下圖:

totalPendingSize是用來統計待發送字節數的,上面的TOTAL\_PENDING\_SIZE\_UPDATER是AtomicLongFieldUpdater類型的,它實現對ChannelOutboundBuffer的totalPendingSize屬性進行加鎖累加,實現一個類似AtomicLong的功能。(下面的unwritable一樣的道理)

那麼為什麼要這麼做呢?為什麼不直接使用AtomicLong來定義totalPendingSize?

為了節省空間

AtomicLong  VS  long + AtomicLongFieldUpdater(幫助long完成原子操作)
類型 佔用空間
AtomicLong 對象頭16B + 8B數據 + 8引用 =至少32B
long 8B
直接使用long,節省20多個字節,雖然很少,但是作為一個網絡工具,在大流量的情況下可以節省出很多空間,還是很有意義的

1.3 提高鎖性能

1.3.1 我們看一下PlatformDependent.LongCounter方法如何做的?

源碼PlatformDependent,這個類裏面有很多類似代碼

該方法提供了一個Long類型的線程安全累加器,針對java版本8以後和8以前的提供的累加器不一樣

1.8及後  LongAdder   VS  AtomicLong(1.8前)

因為LongAdder是1.8版本開始增加的新的Long累加器,在高併發是性能要優於AtomicLong,所以1.8版本以後使用LongAdder

1.3.2 LongAdder和AtomicLong

  • AtomicLong 對Long類型進行原子讀寫
  • LongAdder將Long的值value分成若干個cell,高併發是對某個cell的值累加,可以同時對多個cell值進行累加,能支持更高的併發。需要取到value就對所有cell進行一次sum就可以了

1.3.3 我們做一個簡單的測試看一下LongAdder和AtomicLong的性能:

public class LongAdderTest {

    public static void main(String[] args) {
        testAtomicLongVSLongAdder(10, 10000);
        System.out.println("==================");
        testAtomicLongVSLongAdder(10, 200000);
        System.out.println("==================");
        testAtomicLongVSLongAdder(100, 200000);
    }
    //AtomicLong與LongAdder多線程併發模擬及耗時統計
    static void testAtomicLongVSLongAdder(final int threadCount, final int times) {
        try {
            long start = System.currentTimeMillis();
            testLongAdder(threadCount, times);
            long end = System.currentTimeMillis() - start;
           // System.out.println("條件>>>>>>線程數:" + threadCount + ", 單線程操作" + times);
            System.out.println("LongAdder--count" + (threadCount * times) + ",time:" + end);

            long start2 = System.currentTimeMillis();
            testAtomicLong(threadCount, times);
            long end2 = System.currentTimeMillis() - start2;
            System.out.println("Atomic--count" + (threadCount * times) + ",time:" + end2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //使用AtomicLong模擬i++多線程併發:threadCount線程數、times每個線程運行多少次
    static void testAtomicLong(final int threadCount, final int times) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(threadCount);//發令槍:確保多線程同時運行
        AtomicLong atomicLong = new AtomicLong();
        for (int i = 0; i < threadCount; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < times; j++) {
                        atomicLong.incrementAndGet(); //++操作
                    }
                    countDownLatch.countDown();
                }
            }, "my-thread" + i).start();
        }
        countDownLatch.await();
    }
    //使用LongAdder模擬i++多線程併發:threadCount線程數、times每個線程運行多少次
    static void testLongAdder(final int threadCount, final int times) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(threadCount);
        LongAdder longAdder = new LongAdder();
        for (int i = 0; i < threadCount; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < times; j++) {
                        longAdder.add(1);//是原子操作,多線程安全  //++操作
                    }
                    countDownLatch.countDown();
                }
            }, "my-thread" + i).start();
        }

        countDownLatch.await();
    }
}

運行結果

如下圖,高併發情況下LongAdder性能顯著高於AtomicLong

1.4 根據不同的業務場景選擇合適的鎖

SingleTreadEventExecutor中定義了Atomic...類型、CountDownLatch形式的鎖在不同的地方使用

1.5 能不用鎖就不用鎖

我們Netty源碼的Recycler類裏面有一個屬性threadLocal,他是FastThreadLocal類型,該來對jdk提高的ThreadLocal做了一層包裝,該類有一個虛方法onRemoval,使用該類必須實現這個方法,避免內存泄露。

ThreadLocal是線程私有的,使用這個東西可以避免線程操作共享變量的併發競爭。

總結

從上面的討論的五種鎖優化技術可以看出來,Netty對鎖的優化可以説做到極致,各種場景下都對鎖做了優化,這些值得我們學習在項目中使用。

Add a new 評論

Some HTML is okay.