日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
簡單聊一聊公平鎖和非公平鎖,Parallel并行流

大家好,我是哪吒。

創(chuàng)新互聯(lián)公司專注于企業(yè)網(wǎng)絡(luò)營銷推廣、網(wǎng)站重做改版、大安網(wǎng)站定制設(shè)計、自適應(yīng)品牌網(wǎng)站建設(shè)、H5建站、成都商城網(wǎng)站開發(fā)、集團公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計等建站業(yè)務(wù),價格優(yōu)惠性價比高,為大安等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。

上一章提到了一個關(guān)于 i++ 和 ++i 的面試題打趴了所有人,最終方案是在兩個方法上添加synchronized關(guān)鍵字,從而避免i++的線程安全問題,不過,這樣真的好嗎?在所有有線程安全的方法都添加synchronized?

答案是顯而易見的,不行。

synchronized會極大的降低程序的性能,導(dǎo)致整個程序幾乎只能支持單線程操作,性能顯著降低。

那么,如何解決呢?

一、降低鎖的粒度,將synchronized關(guān)鍵字不放在方法上了,改為synchronized代碼塊。

鎖的粒度更小了,也解決了這個問題,確實可以的。

package com.guor.thread;

public class SynchronizedTest2 {
    int a = 1;
    int b = 1;

    public void add() {
        System.out.println("add start");
        synchronized (this) {
            for (int i = 0; i < 10000; i++) {
                a++;
                b++;
            }
        }
        System.out.println("add end");
    }

    public synchronized void compare() {
        System.out.println("compare start");
        synchronized (this) {
            for (int i = 0; i < 10000; i++) {
                boolean flag = a < b;
                if (flag) {
                    System.out.println("a=" + a + ",b=" + b + "flag=" + flag + ",a < b = " + (a < b));
                }
            }
        }
        System.out.println("compare end");
    }

    public static void main(String[] args) {
        SynchronizedTest2 synchronizedTest = new SynchronizedTest2();
        new Thread(() -> synchronizedTest.add()).start();
        new Thread(() -> synchronizedTest.compare()).start();
    }
}

為了更好的優(yōu)化,有的時候可以將synchronized代碼塊變?yōu)閰^(qū)分讀寫場景的讀寫鎖,也可以考慮悲觀鎖和樂觀鎖的區(qū)分。

對于讀寫場景比較多的情況,可以使用ReentrantReadWriteLock區(qū)分讀寫,再次降低鎖的粒度,提高程序的性能。

ReentrantReadWriteLock 還可以選擇提供了公平鎖,在沒有明確必須使用公平鎖的情況下,盡量不要使用公平鎖,公平鎖會使程序性能降低很多很多。

二、先區(qū)分一下公平鎖和非公平鎖

  • 公平鎖:多個線程按照申請鎖的順序去獲得鎖,線程會直接進入隊列去排隊,永遠都是隊列的第一個得到鎖。
  • 非公平鎖:多個線程去獲取鎖的時候,會直接去嘗試獲取,獲取不到,進入等待隊列,如果能獲取到,就直接獲取到鎖。

簡單來說,公平鎖(誰先排隊,誰先執(zhí)行),非公平鎖(不用排隊,每個人都有機會)。

1、公平鎖

有一天早上,云韻、美杜莎、小醫(yī)仙結(jié)伴去買醬香拿鐵,到了咖啡店,先排隊,一個一個來。不一會,哪吒來了,也買醬香拿鐵,只能在末尾排隊。這個就是公平鎖。

2、非公平鎖

但是呢?第二天早上,哪吒又去買醬香拿鐵,上一次去晚了沒買到(線程被餓死了),這次急了,要插隊買,不講武德。終于喝上了心心念念的醬香拿鐵,這個就是非公平鎖。

3、公平鎖的優(yōu)缺點:

  • 優(yōu)點:所有線程都會獲取到鎖,只是一個時間的問題,不會出現(xiàn)有線程被餓死的情況;
  • 缺點:吞吐量會下降很多,隊列里只有第一個線程能獲取到鎖,其他的線程都會阻塞,cpu喚醒阻塞線程的開銷會增大。

4、非公平鎖的優(yōu)缺點:

  • 優(yōu)點:可以減少CPU喚醒線程的開銷,整體的吞吐效率會高點,CPU也不必取喚醒所有線程,會減少喚起線程的數(shù)量。
  • 缺點:如果運氣不好,會出現(xiàn)一致獲取不到鎖的情況,會被活活的餓死。

三、是否對癥下藥

我們都知道,靜態(tài)字段屬于類,類級別的鎖才能保護;非靜態(tài)字段屬于類實例,實例級別的鎖才能保護。

先看一下下面的代碼:

import lombok.Data;

import java.util.stream.IntStream;

@Data
public class LockTest {
    public static void main(String[] args) {
        IntStream.rangeClosed(1, 100000).parallel().forEach(i -> new LockTest().increase());
        System.out.println(time);
    }

    private static int time = 0;

    public synchronized void increase() {
        time++;
    }
}

在LockTest類中定義一個靜態(tài)變量time,定義一個非靜態(tài)方法increase(),實現(xiàn)time++自增。先累加10萬次,測試一下。看看是否有線程安全的問題。

這...不對啊,上一節(jié)在介紹高并發(fā)下i++線程安全問題的時候,synchronized 是好使的啊。

今天這是怎么了?再運行一次,結(jié)果依然如此,不等于100000

先來分析一下。

在非靜態(tài)的方法上加synchronized,只能確保多個線程無法執(zhí)行同一個實例的increase()方法,卻不能保證不同實例的increase()方法。靜態(tài)的變量time,在多個線程中共享,所以會出現(xiàn)線程安全的問題,synchronized失效了。

那么,將synchronized改為靜態(tài)方法是不是就可以了,試一下。

有兩種寫法,一種是直接將方法改為靜態(tài)方法,一種是使用synchronized代碼塊。

private static Object obj= new Object();
public void increase() {
    synchronized (obj) {
        time++;
    }
}

四、IntStream.rangeClosed是干嘛的?

很多小伙伴,可能會好奇,這個是干什么的,干了5年后端代碼開發(fā)了,沒見過這玩意兒。

IntStream是一種特殊的stream,用來提供對int相關(guān)的stream操作。

IntStream.rangeClosed:生成某個數(shù)字范圍內(nèi)的數(shù)字集合的stream。

比如上面代碼中的IntStream.rangeClosed(1, 100000).parallel().forEach(i -> new LockTest().increase());。

  • range:不包含10000
  • rangeClosed:包含10000

五、parallel是干嘛的?

1、parallel()是什么

Stream.parallel() 方法是 Java 8 中 Stream API 提供的一種并行處理方式。在處理大量數(shù)據(jù)或者耗時操作時,使用 Stream.parallel() 方法可以充分利用多核 CPU 的優(yōu)勢,提高程序的性能。

Stream.parallel() 方法是將串行流轉(zhuǎn)化為并行流的方法。通過該方法可以將大量數(shù)據(jù)劃分為多個子任務(wù)交由多個線程并行處理,最終將各個子任務(wù)的計算結(jié)果合并得到最終結(jié)果。使用 Stream.parallel() 可以簡化多線程編程,減少開發(fā)難度。

需要注意的是,并行處理可能會引入線程安全等問題,需要根據(jù)具體情況進行選擇。

2、舉一個簡單的demo

定義一個list,然后通過parallel() 方法將集合轉(zhuǎn)化為并行流,對每個元素進行i++,最后通過 collect(Collectors.toList()) 方法將結(jié)果轉(zhuǎn)化為 List 集合。

使用并行處理可以充分利用多核 CPU 的優(yōu)勢,加快處理速度。

public class StreamTest {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        System.out.println(list);
        List result = list.stream().parallel().map(i -> i++).collect(Collectors.toList());
        System.out.println(result);
    }
}

我勒個去,什么情況?

這是大部分開發(fā)人員都會犯的小錯誤,在上篇中提到過,i++ 返回原來的值,++i 返回加1后的值。這誰都知道,可是,寫的時候,就不一定了,因為你習(xí)慣了i++,寫順手了,寫的時候也是心不在焉,一蹴而就了。

i++改了++i即可。

3、parallel()的優(yōu)缺點

優(yōu)點:

  • 充分利用多核 CPU 的優(yōu)勢,提高程序的性能;
  • 可以簡化多線程編程,減少開發(fā)難度。

缺點:

  • 并行處理可能會引入線程安全等問題,需要根據(jù)具體情況進行選擇;
  • 并行處理需要付出額外的開銷,例如線程池的創(chuàng)建和銷毀、線程切換等,對于小數(shù)據(jù)量和簡單計算而言,串行處理可能更快。

4、何時使用parallel()?

在實際開發(fā)中,應(yīng)該根據(jù)數(shù)據(jù)量、計算復(fù)雜度、硬件等因素綜合考慮。

比如:

  • 數(shù)據(jù)量較大,有1萬個元素;
  • 計算復(fù)雜度過大,需要對每個元素進行復(fù)雜的計算;
  • 硬件夠硬,比如多核CPU。


新聞名稱:簡單聊一聊公平鎖和非公平鎖,Parallel并行流
文章位置:http://www.dlmjj.cn/article/dpgscse.html