自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

簡(jiǎn)單聊一聊公平鎖和非公平鎖,Parallel并行流

開發(fā) 后端
Synchronized會(huì)極大的降低程序的性能,導(dǎo)致整個(gè)程序幾乎只能支持單線程操作,性能顯著降低。那么,如何解決呢?

大家好,我是哪吒。

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

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

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

那么,如何解決呢?

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

鎖的粒度更小了,也解決了這個(gè)問題,確實(shí)可以的。

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)化,有的時(shí)候可以將synchronized代碼塊變?yōu)閰^(qū)分讀寫場(chǎng)景的讀寫鎖,也可以考慮悲觀鎖和樂觀鎖的區(qū)分。

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

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

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

  • 公平鎖:多個(gè)線程按照申請(qǐng)鎖的順序去獲得鎖,線程會(huì)直接進(jìn)入隊(duì)列去排隊(duì),永遠(yuǎn)都是隊(duì)列的第一個(gè)得到鎖。
  • 非公平鎖:多個(gè)線程去獲取鎖的時(shí)候,會(huì)直接去嘗試獲取,獲取不到,進(jìn)入等待隊(duì)列,如果能獲取到,就直接獲取到鎖。

簡(jiǎn)單來(lái)說(shuō),公平鎖(誰(shuí)先排隊(duì),誰(shuí)先執(zhí)行),非公平鎖(不用排隊(duì),每個(gè)人都有機(jī)會(huì))。

1、公平鎖

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

2、非公平鎖

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

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

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

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

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

三、是否對(duì)癥下藥

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

先看一下下面的代碼:

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類中定義一個(gè)靜態(tài)變量time,定義一個(gè)非靜態(tài)方法increase(),實(shí)現(xiàn)time++自增。先累加10萬(wàn)次,測(cè)試一下??纯词欠裼芯€程安全的問題。

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

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

先來(lái)分析一下。

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

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

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

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

四、IntStream.rangeClosed是干嘛的?

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

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

IntStream.rangeClosed:生成某個(gè)數(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ù)或者耗時(shí)操作時(shí),使用 Stream.parallel() 方法可以充分利用多核 CPU 的優(yōu)勢(shì),提高程序的性能。

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

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

2、舉一個(gè)簡(jiǎn)單的demo

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

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

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

我勒個(gè)去,什么情況?

這是大部分開發(fā)人員都會(huì)犯的小錯(cuò)誤,在上篇中提到過,i++ 返回原來(lái)的值,++i 返回加1后的值。這誰(shuí)都知道,可是,寫的時(shí)候,就不一定了,因?yàn)槟懔?xí)慣了i++,寫順手了,寫的時(shí)候也是心不在焉,一蹴而就了。

i++改了++i即可。

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

優(yōu)點(diǎn):

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

缺點(diǎn):

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

4、何時(shí)使用parallel()?

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

比如:

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

責(zé)任編輯:姜華 來(lái)源: 哪吒編程
相關(guān)推薦

2022-12-26 00:00:04

公平鎖非公平鎖

2022-05-09 07:37:04

Java非公平鎖公平鎖

2022-07-12 08:56:18

公平鎖非公平鎖Java

2024-12-03 00:35:20

2019-01-04 11:18:35

獨(dú)享鎖共享鎖非公平鎖

2018-07-31 15:05:51

Java公平鎖線程

2020-08-24 08:13:25

非公平鎖源碼

2022-02-21 15:01:45

MySQL共享鎖獨(dú)占鎖

2021-08-20 07:54:20

非公平鎖 Java多線編程

2022-12-08 17:15:54

Java并發(fā)包

2019-12-12 14:52:10

數(shù)據(jù)庫(kù)腳本

2023-05-09 12:46:00

linuxlock

2024-10-08 09:10:03

JDK通信并發(fā)

2020-02-02 13:59:59

MySQL數(shù)據(jù)庫(kù)線程

2021-03-11 08:55:47

JavaUser對(duì)象

2021-06-30 14:56:12

Redisson分布式公平鎖

2021-07-02 08:51:09

Redisson分布式鎖公平鎖

2022-03-08 16:10:38

Redis事務(wù)機(jī)制

2018-01-10 14:13:04

測(cè)試矩陣API測(cè)試

2021-07-01 09:42:08

Redisson分布式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)