欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的多線程一定就快嗎?

 更新時(shí)間:2020年09月09日 10:00:38   作者:崔笑顏  
這篇文章主要介紹了Java 多線程的相關(guān)資料,幫助大家是否選擇開(kāi)啟多線程,感興趣的朋友可以了解下

并發(fā)編程與多線程編程

要了解并發(fā)編程,首先要懂得與并行這個(gè)概念進(jìn)行區(qū)分。并行是指兩個(gè)事件同時(shí)進(jìn)行,并發(fā)是CPU切換速度快,看起來(lái)像是每個(gè)任務(wù)同時(shí)進(jìn)行一樣。多線程是實(shí)現(xiàn)并發(fā)編程的一種方式,假設(shè)一個(gè)場(chǎng)景,在廣州地鐵高峰時(shí)段,一群人涌進(jìn)地鐵里,在不同的閘機(jī)口刷卡進(jìn)去。在這個(gè)場(chǎng)景里,進(jìn)地鐵就是任務(wù),每個(gè)人可以看出是并發(fā)的,而多個(gè)刷卡閘機(jī)口就是多線程。

  并發(fā)編程的本質(zhì)目的是為了充分利用CPU,讓程序運(yùn)行得更快。然而,并不是啟動(dòng)更多的線程就能讓程序最大限度地并發(fā)執(zhí)行。在進(jìn)行并發(fā)編程時(shí),如果希望通過(guò)多線程執(zhí)行任務(wù)讓程序運(yùn)行得更快,會(huì)面臨非常多的挑戰(zhàn)。比如上下文切換的問(wèn)題、死鎖的問(wèn)題,以及受限于硬件和軟件的資源限制問(wèn)題,下面就來(lái)嘮嗑嘮嗑這些因素。

上下文切換

原理分析

正如上面所言,并發(fā)與并行最大的區(qū)別就是,并發(fā)只是看起來(lái)像是并行。實(shí)際上是,CPU通過(guò)給每個(gè)線程分配時(shí)間來(lái)執(zhí)行這個(gè)線程的程序,只是這個(gè)時(shí)間非常短,通常是幾十毫秒,我們根本無(wú)法觀察到變化,感覺(jué)它們都是同時(shí)執(zhí)行的一樣。

  CPU通過(guò)時(shí)間片分配算法來(lái)循環(huán)執(zhí)行任務(wù),當(dāng)前任務(wù)執(zhí)行一個(gè)時(shí)間片后會(huì)切換到下一個(gè)任務(wù)。但是,在切換前會(huì)保存上一個(gè)任務(wù)的狀態(tài),以便下次切換回這個(gè)任務(wù)時(shí),可以再加載這個(gè)任務(wù)的狀態(tài)。所以任務(wù)從保存到再加載的過(guò)程就是一次上下文切換。因此,不難得知,上下文切換需要耗費(fèi)不少時(shí)間。

  再來(lái)假設(shè)一個(gè)場(chǎng)景,一個(gè)人去火車(chē)站買(mǎi)票,買(mǎi)票的窗口有十來(lái)個(gè)那么多。買(mǎi)票的人并不知道哪個(gè)窗口可以買(mǎi)到票,只能挨個(gè)地問(wèn),最后終于在最后一個(gè)窗口買(mǎi)到了。這個(gè)場(chǎng)景,看似買(mǎi)票的過(guò)程很長(zhǎng),其實(shí)大部分時(shí)間都在切換窗口上,這也就是上下文切換的問(wèn)題所在。因此,并非線程數(shù)多就一定執(zhí)行得快,要選擇與任務(wù)相適應(yīng)的線程數(shù)才是最佳方案。

測(cè)試代碼

package Concurrency;

/**
 * @author RuiMing Lin
 * @date 2020-03-28 12:19
 */
public class Demo1 {
  public static void main(String[] args) {
    System.out.println("萬(wàn)級(jí)循環(huán):");
    concurrency(10000);
    serial(10000);
    System.out.println("--------------------------華麗分隔符--------------------------------");
    System.out.println("十萬(wàn)級(jí)循環(huán):");
    concurrency(100000);
    serial(100000);
    System.out.println("--------------------------華麗分隔符--------------------------------");
    System.out.println("百萬(wàn)級(jí)循環(huán):");
    concurrency(1000000);
    serial(1000000);
    System.out.println("--------------------------華麗分隔符--------------------------------");
    System.out.println("千萬(wàn)級(jí)循環(huán):");
    concurrency(10000000);
    serial(10000000);
    System.out.println("--------------------------華麗分隔符--------------------------------");
    System.out.println("億級(jí)循環(huán):");
    concurrency(100000000);
    serial(100000000);
  }

  private static void concurrency(long count){
    // 開(kāi)啟三個(gè)線程執(zhí)行三個(gè)循環(huán)
    long start = System.currentTimeMillis();
    new Thread(new Runnable() {
      @Override
      public void run() {
        int a = 0;
        for (long i = 0; i < count; i++) {
          a++;
        }
      }
    }).start();
    new Thread(new Runnable() {
      @Override
      public void run() {
        int b = 0;
        for (long i = 0; i < count; i++) {
          b++;
        }
      }
    }).start();
    new Thread(new Runnable() {
      @Override
      public void run() {
        int c = 0;
        for (long i = 0; i < count; i++) {
          c++;
        }
      }
    }).start();
    long end = System.currentTimeMillis();
    long time = end - start;
    System.out.println("并行執(zhí)行花費(fèi)時(shí)間為:" + time + "ms");
  }

  private static void serial(long count){
    // 三個(gè)循環(huán)順序執(zhí)行
    long start = System.currentTimeMillis();
    int a = 0;
    int b = 0;
    int c = 0;
    for (int i = 0; i < count; i++) {
      a++;
    }
    for (int i = 0; i < count; i++) {
      b++;
    }
    for (int i = 0; i < count; i++) {
      c++;
    }
    long end = System.currentTimeMillis();
    long time = end - start;
    System.out.println("串行執(zhí)行花費(fèi)時(shí)間為:" + time + "ms");
  }
}

結(jié)果輸出:

萬(wàn)級(jí)循環(huán): 并行執(zhí)行花費(fèi)時(shí)間為:4ms 串行執(zhí)行花費(fèi)時(shí)間為:1ms

--------------------------華麗分隔符--------------------------------

十萬(wàn)級(jí)循環(huán): 并行執(zhí)行花費(fèi)時(shí)間為:1ms 串行執(zhí)行花費(fèi)時(shí)間為:4ms

--------------------------華麗分隔符--------------------------------

百萬(wàn)級(jí)循環(huán): 并行執(zhí)行花費(fèi)時(shí)間為:1ms 串行執(zhí)行花費(fèi)時(shí)間為:10ms

--------------------------華麗分隔符--------------------------------

千萬(wàn)級(jí)循環(huán): 并行執(zhí)行花費(fèi)時(shí)間為:1ms 串行執(zhí)行花費(fèi)時(shí)間為:36ms

--------------------------華麗分隔符--------------------------------

億級(jí)循環(huán): 并行執(zhí)行花費(fèi)時(shí)間為:1ms 串行執(zhí)行花費(fèi)時(shí)間為:357ms

分析結(jié)果:

當(dāng)數(shù)量級(jí)在萬(wàn)級(jí)時(shí),串行是比并發(fā)要快的,當(dāng)數(shù)量級(jí)來(lái)到十萬(wàn)以后,串行便顯得力不從心了。所以,可以認(rèn)為當(dāng)程序執(zhí)行量不夠大時(shí),是沒(méi)必要開(kāi)啟多線程的。

如何減少上下文切換

減少上下文切換的方法有無(wú)鎖并發(fā)編程、CAS算法、使用最少線程和使用協(xié)程。

  1. 無(wú)鎖并發(fā)編程。多線程競(jìng)爭(zhēng)鎖時(shí),會(huì)引起上下文切換,所以多線程處理數(shù)據(jù)時(shí),可以用一些辦法來(lái)避免使用鎖,如將數(shù)據(jù)的ID按照Hash算法取模分段,不同的線程處理不同段的數(shù)據(jù)。
  2. CAS算法。Java的Atomic包使用CAS算法來(lái)更新數(shù)據(jù),而不需要加鎖。
  3. 使用最少線程。避免創(chuàng)建不需要的線程,比如任務(wù)很少,但是創(chuàng)建了很多線程來(lái)處理,這樣會(huì)造成大量線程都處于等待狀態(tài)。
  4. 協(xié)程:在單線程里實(shí)現(xiàn)多任務(wù)的調(diào)度,并在單線程里維持多個(gè)任務(wù)間的切換。

死鎖

原理分析

 死鎖,是指多個(gè)線程在運(yùn)行過(guò)程中因爭(zhēng)奪相同資源而造成的一種僵局,當(dāng)進(jìn)程處于這種僵持狀態(tài)時(shí),它們都將無(wú)法再向前推進(jìn),此時(shí)程序就處于癱瘓狀態(tài),無(wú)法執(zhí)行。 通常情況下,是多個(gè)線程共同競(jìng)爭(zhēng)同一把鎖對(duì)象,而其中一個(gè)線程獲得鎖之后發(fā)生異常等未來(lái)得及釋放鎖,導(dǎo)致其它線程一直在等待,無(wú)法運(yùn)行。

測(cè)試代碼

package Concurrency;

/**
 * @author RuiMing Lin
 * @date 2020-03-28 13:14
 */
public class Demo2 {
  private static String str1 = "A";
  private static String str2 = "B";

  public static void main(String[] args) {
    new Thread(new Runnable() {
      @Override
      public void run() {
        synchronized (str1){
          System.out.println("第一個(gè)線程獲得str1");
          try {
            Thread.currentThread().sleep(2000);
          }catch (InterruptedException e){
            e.printStackTrace();
          }
          synchronized (str2){
            System.out.println("第一個(gè)線程獲得str2");
          }
        }
      }
    }).start();

    new Thread(new Runnable() {
      @Override
      public void run() {
        synchronized (str2){
          System.out.println("第二個(gè)線程獲得str2");
          try {
            Thread.currentThread().sleep(2000);
          }catch (InterruptedException e){
            e.printStackTrace();
          }
          synchronized (str1){
            System.out.println("第二個(gè)線程獲得str1");
          }
        }
      }
    }).start();
  }
}

結(jié)果輸出:

如何解決死鎖

  1. 避免一個(gè)線程同時(shí)獲取多個(gè)鎖。
  2. 避免一個(gè)線程在鎖內(nèi)同時(shí)占用多個(gè)資源,盡量保證每個(gè)鎖只占用一個(gè)資源。
  3. 嘗試使用定時(shí)鎖,使用lock.tryLock(timeout)來(lái)替代使用內(nèi)部鎖機(jī)制。
  4. 對(duì)于數(shù)據(jù)庫(kù)鎖,加鎖和解鎖必須在一個(gè)數(shù)據(jù)庫(kù)連接里,否則會(huì)出現(xiàn)解鎖失敗的情況。

總結(jié)

并發(fā)程序并不是簡(jiǎn)單的程序,編寫(xiě)的時(shí)候應(yīng)該嚴(yán)謹(jǐn)一些。復(fù)雜的代碼容易引起死鎖,因此,建議多使用JDK并發(fā)包提供的并發(fā)容器和工具類(lèi)來(lái)解決并發(fā)問(wèn)題。同時(shí),也要注重新能上的問(wèn)題,既要考慮到程序執(zhí)行任務(wù)量,也要考慮CPU性能等等,不要一昧地增加線程數(shù)。

以上就是Java中的多線程一定就快嗎?的詳細(xì)內(nèi)容,更多關(guān)于Java 多線程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java內(nèi)存模型之重排序的相關(guān)知識(shí)總結(jié)

    Java內(nèi)存模型之重排序的相關(guān)知識(shí)總結(jié)

    重排序是指編譯器和處理器為了優(yōu)化性能而對(duì)指令序列進(jìn)行重新排序的一種手段,文中詳細(xì)介紹了Java重排序的相關(guān)知識(shí),需要的朋友可以參考下
    2021-06-06
  • Java語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單FTP軟件 輔助功能模塊FTP站點(diǎn)管理實(shí)現(xiàn)(12)

    Java語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單FTP軟件 輔助功能模塊FTP站點(diǎn)管理實(shí)現(xiàn)(12)

    這篇文章主要為大家詳細(xì)介紹了Java語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單FTP軟,輔助功能模塊FTP站點(diǎn)管理的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • Spring中數(shù)據(jù)訪問(wèn)對(duì)象Data Access Object的介紹

    Spring中數(shù)據(jù)訪問(wèn)對(duì)象Data Access Object的介紹

    今天小編就為大家分享一篇關(guān)于Spring中數(shù)據(jù)訪問(wèn)對(duì)象Data Access Object的介紹,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-01-01
  • mybatis-plus enum實(shí)現(xiàn)枚舉類(lèi)型自動(dòng)轉(zhuǎn)換

    mybatis-plus enum實(shí)現(xiàn)枚舉類(lèi)型自動(dòng)轉(zhuǎn)換

    本文主要介紹了mybatis-plus enum實(shí)現(xiàn)枚舉類(lèi)型自動(dòng)轉(zhuǎn)換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07
  • SpringMVC 數(shù)據(jù)校驗(yàn)實(shí)例解析

    SpringMVC 數(shù)據(jù)校驗(yàn)實(shí)例解析

    這篇文章主要介紹了SpringMVC 數(shù)據(jù)校驗(yàn)實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • 詳解Spring AOP的實(shí)現(xiàn)方式

    詳解Spring AOP的實(shí)現(xiàn)方式

    AOP是一種思想,是對(duì)某一類(lèi)事情的集中處理,切面就是指某一類(lèi)特定的問(wèn)題,所以AOP可以理解為面向特定方法編程,這篇文章主要介紹了Spring AOP的實(shí)現(xiàn)方式,需要的朋友可以參考下
    2024-02-02
  • 微信支付java版本之獲取Access_token

    微信支付java版本之獲取Access_token

    這篇文章主要介紹了微信支付java版本之獲取Access_token,java如何獲取Access_token,感興趣的小伙伴們可以參考一下
    2016-08-08
  • springboot集成mqtt超級(jí)詳細(xì)步驟

    springboot集成mqtt超級(jí)詳細(xì)步驟

    這篇文章主要介紹了springboot集成mqtt超級(jí)詳細(xì)步驟,本文分步驟結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-06-06
  • java枚舉類(lèi)型-Enum

    java枚舉類(lèi)型-Enum

    本文詳細(xì)介紹了 Java1.5 引入的新特性枚舉中的關(guān)鍵字enum,運(yùn)用大量的代碼加以解釋?zhuān)嘈趴梢詭椭秸趯W(xué)習(xí)該知識(shí)的小伙伴,大家可以參考一下
    2021-08-08
  • Java中List根據(jù)條件刪除元素的幾種方式

    Java中List根據(jù)條件刪除元素的幾種方式

    java List刪除指定元素有四種方法,分別是普通for循環(huán),增強(qiáng)for循環(huán),CopyOnWriteArrayList以及原生的Iterator迭代器循環(huán)來(lái)刪除list中指定的某個(gè)元素,非常的簡(jiǎn)單,并通過(guò)代碼示例講解的非常詳細(xì),需要的朋友可以參考下
    2024-04-04

最新評(píng)論