小師妹學JVM之:深入理解JIT和編譯優化-你看不懂系列

目錄

  • 簡介
  • JIT編譯器
  • Tiered Compilation分層編譯
  • OSR(On-Stack Replacement)
  • Deoptimization
  • 常見的編譯優化舉例
    • Inlining內聯
    • Branch Prediction分支預測
    • Loop unswitching
    • Loop unrolling展開
    • Escape analysis逃逸分析
  • 總結

簡介

小師妹已經學完JVM的簡單部分了,接下來要進入的是JVM中比較晦澀難懂的概念,這些概念是那麼的枯燥乏味,甚至還有點惹人討厭,但是要想深入理解JVM,這些概念是必須的,我將會盡量嘗試用簡單的例子來解釋它們,但一定會有人看不懂,沒關係,這個系列本不是給所有人看的。

更多精彩內容且看:

  • 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
  • Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
  • Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
  • java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程

JIT編譯器

小師妹:F師兄,我的基礎已經打牢了嗎?可以進入這麼複雜的內容環節了嗎?

小師妹不試試怎麼知道不行呢?了解點深入內容可以幫助你更好的理解之前的知識。現在我們開始吧。

上次我們在講java程序的處理流程的時候,還記得那通用的幾步吧。

小師妹:當然記得了,編寫源代碼,javac編譯成字節碼,加載到JVM中執行。

對,其實在JVM的執行引擎中,有三個部分:解釋器,JIT編譯器和垃圾回收器。

解釋器會將前面編譯生成的字節碼翻譯成機器語言,因為每次都要翻譯,相當於比直接編譯成機器碼要多了一步,所以java執行起來會比較慢。

為了解決這個問題,JVM引入了JIT(Just-in-Time)編譯器,將熱點代碼編譯成為機器碼。

Tiered Compilation分層編譯

小師妹你知道嗎?在JDK8之前,HotSpot VM又分為三種。分別是 client VM, server VM, 和 minimal VM,分別用在客戶端,服務器,和嵌入式系統。

但是隨着硬件技術的發展,這些硬件上面的限制都不是什麼大事了。所以從JDK8之後,已經不再區分這些VM了,現在統一使用VM的實現來替代他們。

小師妹,你覺得Client VM和Server VM的本質區別在哪一部分呢?

小師妹,編譯成字節碼應該都是使用javac,都是同樣的命令,字節碼上面肯定是一樣的。難點是在執行引擎上面的不同?

說的對,因為Client VM和Server VM的出現,所以在JIT中出現了兩種不同的編譯器,C1 for Client VM, C2 for Server VM。

因為javac的編譯只能做少量的優化,其實大量的動態優化是在JIT中做的。C2相對於C1,其優化的程度更深,更加激進。

為了更好的提升編譯效率,JVM在JDK7中引入了分層編譯Tiered compilation的概念。

對於JIT本身來說,動態編譯是需要佔用用戶內存空間的,有可能會造成較高的延遲。

對於Server服務器來說,因為代碼要服務很多個client,所以磨刀不誤砍柴工,短暫的延遲帶來永久的收益,聽起來是可以接受的。

Server端的JIT編譯也不是立馬進行的,它可能需要收集到足夠多的信息之後,才進行編譯。

而對於Client來說,延遲帶來的性能影響就需要進行考慮了。和Server相比,它只進行了簡單的機器碼的編譯。

為了滿足不同層次的編譯需求,於是引入了分層編譯的概念。

大概來說分層編譯可以分為三層:

  1. 第一層就是禁用C1和C2編譯器,這個時候沒有JIT進行。
  2. 第二層就是只開啟C1編譯器,因為C1編譯器只會進行一些簡單的JIT優化,所以這個可以應對常規情況。
  3. 第三層就是同時開啟C1和C2編譯器。

在JDK7中,你可以使用下面的命令來開啟分層編譯:

-XX:+TieredCompilation

而在JDK8之後,恭喜你,分層編譯已經是默認的選項了,不用再手動開啟。

OSR(On-Stack Replacement)

小師妹:F師兄,你剛剛講到Server的JIT不是立馬就進行編譯的,它會等待一定的時間來搜集所需的信息,那麼代碼不是要從字節碼轉換成機器碼?

對的,這個過程就叫做OSR(On-Stack Replacement)。為什麼叫OSR呢?我們知道JVM的底層實現是一個棧的虛擬機,所以這個替換實際上是一系列的Stack操作。

上圖所示,m1方法從最初的解釋frame變成了後面的compiled frame。

Deoptimization

這個世界是平衡的,有陰就有陽,有優化就有反優化。

小師妹:F師兄,為什麼優化了之後還要反優化呢?這樣對性能不是下降了嗎?

通常來說是這樣的,但是有些特殊的情況下面,確實是需要進行反優化的。

下面是比較常見的情況:

  1. 需要調試的情況

如果代碼正在進行單個步驟的調試,那麼之前被編譯成為機器碼的代碼需要反優化回來,從而能夠調試。

  1. 代碼廢棄的情況

當一個被編譯過的方法,因為種種原因不可用了,這個時候就需要將其反優化。

  1. 優化之前編譯的代碼

有可能出現之前優化過的代碼可能不夠完美,需要重新優化的情況,這種情況下同樣也需要進行反優化。

常見的編譯優化舉例

除了JIT編譯成機器碼之外,JIT還有一下常見的代碼優化方式,我們來一一介紹。

Inlining內聯

舉個例子:

int a = 1;
int b = 2;
int result = add(a, b);
...
public int add(int x, int y) { return x + y; }
int result = a + b; //內聯替換

上面的add方法可以簡單的被替換成為內聯表達式。

Branch Prediction分支預測

通常來說對於條件分支,因為需要有一個if的判斷條件,JVM需要在執行完畢判斷條件,得到返回結果之後,才能夠繼續準備後面的執行代碼,如果有了分支預測,那麼JVM可以提前準備相應的執行代碼,如果分支檢查成功就直接執行,省去了代碼準備的步驟。

比如下面的代碼:

// make an array of random doubles 0..1
double[] bigArray = makeBigArray();
for (int i = 0; i < bigArray.length; i++)
{
 double cur = bigArray[i];
 if (cur > 0.5) { doThis();} else { doThat();}
}

Loop unswitching

如果我們在循環語句裏面添加了if語句,為了提升併發的執行效率,可以將if語句從循環中提取出來:

  int i, w, x[1000], y[1000];
  for (i = 0; i < 1000; i++) {
    x[i] += y[i];
    if (w)
      y[i] = 0;
  }

可以改為下面的方式:

  int i, w, x[1000], y[1000];
  if (w) {
    for (i = 0; i < 1000; i++) {
      x[i] += y[i];
      y[i] = 0;
    }
  } else {
    for (i = 0; i < 1000; i++) {
      x[i] += y[i];
    }
  }

Loop unrolling展開

在循環語句中,因為要不斷的進行跳轉,所以限制了執行的速度,我們可以對循環語句中的邏輯進行適當的展開:

 int x;
 for (x = 0; x < 100; x++)
 {
     delete(x);
 }

轉變為:

 int x; 
 for (x = 0; x < 100; x += 5 )
 {
     delete(x);
     delete(x + 1);
     delete(x + 2);
     delete(x + 3);
     delete(x + 4);
 }

雖然循環體變長了,但是跳轉次數變少了,其實是可以提升執行速度的。

Escape analysis逃逸分析

什麼叫逃逸分析呢?簡單點講就是分析這個線程中的對象,有沒有可能會被其他對象或者線程所訪問,如果有的話,那麼這個對象應該在Heap中分配,這樣才能讓對其他的對象可見。

如果沒有其他的對象訪問,那麼完全可以在stack中分配這個對象,棧上分配肯定比堆上分配要快,因為不用考慮同步的問題。

我們舉個例子:

  public static void main(String[] args) {
    example();
  }
  public static void example() {
    Foo foo = new Foo(); //alloc
    Bar bar = new Bar(); //alloc
    bar.setFoo(foo);
  }
}

class Foo {}

class Bar {
  private Foo foo;
  public void setFoo(Foo foo) {
    this.foo = foo;
  }
}

上面的例子中,setFoo引用了foo對象,如果bar對象是在heap中分配的話,那麼引用的foo對象就逃逸了,也需要被分配在heap空間中。

但是因為bar和foo對象都只是在example方法中調用的,所以,JVM可以分析出來沒有其他的對象需要引用他們,那麼直接在example的方法棧中分配這兩個對象即可。

逃逸分析還有一個作用就是lock coarsening。

為了在多線程環境中保證資源的有序訪問,JVM引入了鎖的概念,雖然鎖可以保證多線程的有序執行,但是如果實在單線程環境中呢?是不是還需要一直使用鎖呢?

比如下面的例子:

public String getNames() {
     Vector<String> v = new Vector<>();
     v.add("Me");
     v.add("You");
     v.add("Her");
     return v.toString();
}

Vector是一個同步對象,如果是在單線程環境中,這個同步鎖是沒有意義的,因此在JDK6之後,鎖只在被需要的時候才會使用。

這樣就能提升程序的執行效率。

總結

本文介紹了JIT的原理和一些基本的優化方式。後面我們會繼續探索JIT和JVM的秘密,敬請期待。

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/jvm-jit-in-detail/

本文來源:flydean的博客

歡迎關注我的公眾號:程序那些事,更多精彩等着您!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

精美圖文講解Java AQS 共享式獲取同步狀態以及Semaphore的應用

| 好看請贊,養成習慣

  • 你有一個思想,我有一個思想,我們交換后,一個人就有兩個思想

  • If you can NOT explain it simply, you do NOT understand it well enough

現陸續將Demo代碼和技術文章整理在一起 Github實踐精選 ,方便大家閱讀查看,本文同樣收錄在此,覺得不錯,還請Star

看到本期內容這麼少,是不是心動了呢?

前言

上一篇萬字長文 Java AQS隊列同步器以及ReentrantLock的應用 為我們讀 JUC 源碼以及其設計思想做了足夠多的鋪墊,接下來的內容我將重點說明差異化,如果有些童鞋不是能很好的理解文中的一些內容,強烈建議回看上一篇文章,搞懂基礎內容,接下來的閱讀真會輕鬆加愉快

AQS 中我們介紹了獨佔式獲取同步狀態的多種情形:

  • 獨佔式獲取鎖
  • 可響應中斷的獨佔式獲取鎖
  • 有超時限制的獨佔式獲取鎖

AQS 提供的模版方法裏面還差共享式獲取同步狀態沒有介紹,所以我們今天來揭開這個看似神秘的面紗

AQS 中的共享式獲取同步狀態

獨佔式是你中沒我,我中沒你的的一種互斥形式,共享式顯然就不是這樣了,所以他們的唯一區別就是:

同一時刻能否有多個線程同時獲取到同步狀態

簡單來說,就是這樣滴:

我們知道同步狀態 state 是維護在 AQS 中的,拋開可重入鎖的概念,我在上篇文章中也提到了,獨佔式和共享式控制同步狀態 state 的區別僅僅是這樣:

所以說想了解 AQS 的 xxxShared 的模版方法,只需要知道它是怎麼控制 state 的就好了

AQS共享式獲取同步狀態源碼分析

為了幫助大家更好的回憶內容,我將上一篇文章的兩個關鍵內容粘貼在此處,幫助大家快速回憶,關於共享式,大家只需要關注【騷紫色】就可以了

自定義同步器需要重寫的方法

AQS 提供的模版方法

故事就從這裏說起吧 (你會發現和獨佔式驚人的相似),關鍵代碼都加了註釋

    public final void acquireShared(int arg) {
      	// 同樣調用自定義同步器需要重寫的方法,非阻塞式的嘗試獲取同步狀態,如果結果小於零,則獲取同步狀態失敗
        if (tryAcquireShared(arg) < 0)
          	// 調用 AQS 提供的模版方法,進入等待隊列
            doAcquireShared(arg);
    }

進入 doAcquireShared 方法:

    private void doAcquireShared(int arg) {
      	// 創建共享節點「SHARED」,加到等待隊列中
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
          	// 進入“自旋”,這裏並不是純粹意義上的死循環,在獨佔式已經說明過
            for (;;) {
              	// 同樣嘗試獲取當前節點的前驅節點
                final Node p = node.predecessor();
              	// 如果前驅節點為頭節點,嘗試再次獲取同步狀態
                if (p == head) {
                  	// 在此以非阻塞式獲取同步狀態
                    int r = tryAcquireShared(arg);
                  	// 如果返回結果大於等於零,才能跳出外層循環返回
                    if (r >= 0) {
                      	// 這裡是和獨佔式的區別
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

上面代碼第 18 行我們提到和獨佔式獲取同步狀態的區別,貼心的給大家一個更直觀的對比:

差別只在這裏,所以我們就來看看 setHeadAndPropagate(node, r) 到底幹了什麼,我之前說過 JDK 源碼中的方法命名絕大多數還是非常直觀的,該方法直譯過來就是 【設置頭並且傳播/繁衍】。獨佔式只是設置了頭,共享式除了設置頭還多了一個傳播,你的疑問應該已經來了:

啥是傳播,為什麼會有傳播這個設置呢?

想了解這個問題,你需要先知道非阻塞共享式獲取同步狀態返回值的含義:

這裏說的傳播其實說的是 propagate > 0 的情況,道理也很簡單,當前線程獲取同步狀態成功了,還有剩餘的同步狀態可用於其他線程獲取,那就要通知在等待隊列的線程,讓他們嘗試獲取剩餘的同步狀態

如果要讓等待隊列中的線程獲取到通知,需要線程調用 release 方法實現的。接下來,我們走近 setHeadAndPropagate 一探究竟,驗證一下

  // 入參,node: 當前節點
	// 入參,propagate:獲取同步狀態的結果值,即上面方法中的變量 r
	private void setHeadAndPropagate(Node node, int propagate) {
    		// 記錄舊的頭部節點,用於下面的check
        Node h = head; 
    		// 將當前節點設置為頭節點
        setHead(node);
        
    		// 通過 propagate 的值和 waitStatus 的值來判斷是否可以調用 doReleaseShared 方法
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
          	// 如果後繼節點為空或者後繼節點為共享類型,則進行喚醒後繼節點
    				// 這裏後繼節點為空意思是只剩下當前頭節點了,另外這裏的 s == null 也是判斷空指針的標準寫法
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

上面方法的大方向作用我們了解了,但是代碼中何時調用 doReleaseShared 的判斷邏輯還是挺讓人費解的,為什麼會有這麼一大堆的判斷,我們來逐個分析一下:

這裏的空判斷有點讓人頭大,我們先挑出來說明一下:

排除了其他判斷條件的干擾,接下來我們就專註分析 propagate 和 waitStatus 兩個判斷條件就可以了,這裏再將 waitStatus 的幾種狀態展示在這裏,幫助大家理解,【騷粉色】是我們一會要用到的:

propagate > 0

上面已經說過了,如果成立,直接短路後續判斷,然後根據 doReleaseShared 的判斷條件進行釋放

propagate > 0 不成立, h.waitStatus < 0 成立 (注意這裏的h是舊的頭節點)

什麼時候 h.waitStatus < 0 呢?拋開 CONDITION 的使用,只剩下 SIGNAL 和 PROPAGATE,想知道這個答案,需要提前看一下 doReleaseShared() 方法了:

    private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                  	// CAS 將頭節點的狀態設置為0                
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    // 設置成功后才能跳出循環喚醒頭節點的下一個節點
                  	unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         // 將頭節點狀態CAS設置成 PROPAGATE 狀態
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

doReleaseShared() 方法中可以看出:

  • 如果讓 h.waitStatus < 0 成立,只能將其設置成 PROPAGATE = -3 的情況,設置成功的前提是 h 頭節點 expected 的狀態是 0;

  • 如果 h.waitStatus = 0,是上述代碼第 8 行 CAS 設置成功,然後喚醒等待中的線程

所以猜測,當前線程執行到 h.waitStatus < 0 的判斷前,有另外一個線程剛好執行了 doReleaseShared() 方法,將 waitStatus 又設置成PROPAGATE = -3

這個理解有點繞,我們還是來畫個圖理解一下吧:

可能有同學還是不太能理解這麼寫的道理,我們一直說 propagate <> = 0 的情況,propagate = 0 代表的是當時/當時/當時 嘗試獲取同步狀態沒成功,但是之後可能又有共享狀態被釋放了,所以上面的邏輯是以防這種萬一,你懂的,嚴謹的併發就是要防止一切萬一,現在結合這個情景再來理解上面的判斷你是否豁然開朗了呢?

繼續向下看,

前序條件不成立,(h = head) == null || h.waitStatus < 0 注意這裏的h是新的頭節點)

有了上面鋪墊,這個就直接畫個圖就更好理解啦,其實就是沒有那麼巧有另外一個線程摻合了

相信到這裏你應該理解共享式獲取同步狀態的全部過程了吧,至於非阻塞共享式獲取同步狀態帶有超時時間獲取同步狀態,結合本文講的 setHeadAndPropagate 邏輯和獨佔式獲取同步狀態的實現過程過程來看,真是一毛一樣,這裏就不再累述了,趕緊打開你的 IDE 去驗證一下吧

我們分析了AQS 的模版方法,還一直沒說 tryAcquireShared(arg) 這個方法是如何被重寫的,想要了解這個,我們就來看一看共享式獲取同步狀態的經典應用 Semaphore

Semaphore 的應用及源碼分析

Semaphore 概念

Semaphore 中文多翻譯為 【信號量】,我還特意查了一下劍橋辭典的英文解釋:

其實就是信號標誌(two flags),比如紅綠燈,每個交通燈產生兩種不同行為

  • Flag1-紅燈:停車
  • Flag2-綠燈:行車

在 Semaphore 裏面,什麼時候是紅燈,什麼時候是綠燈,其實就是靠 tryAcquireShared(arg) 的結果來表示的

  • 獲取不到共享狀態,即為紅燈
  • 獲取到共享狀態,即為綠燈

所以我們走近 Semaphore ,來看看它到底是怎麼應用 AQS 的,又是怎樣重寫 tryAcquireShared(arg) 方法的

Semaphore 源碼分析

先看一下類結構

看到這裏你是否有點跌眼鏡,和 ReentrantLock 相似的可怕吧,如果你有些陌生,再次強烈建議你回看上一篇文章 Java AQS隊列同步器以及ReentrantLock的應用 ,這裏直接提速對比看公平和非公平兩種重寫的 tryAcquireShared(arg) 方法,沒有意外,公平與否,就是判斷是否有前驅節點

方法內部只是計算 state 的剩餘值,那 state 的初始值是多少怎麼設置呢?當然也就是構造方法了:

		public Semaphore(int permits) {
      	// 默認仍是非公平的同步器,至於為什麼默認是非公平的,在上一篇文章中也特意說明過
        sync = new NonfairSync(permits);
    }
    
    NonfairSync(int permits) {
    		super(permits);
    }

super 方法,就會將初始值給到 AQS 中的 state

也許你發現了,當我們把 permits 設置為1 的時候,不就是 ReentrantLock 的互斥鎖了嘛,說的一點也沒錯,我們用 Semaphore 也能實現基本互斥鎖的效果


static int count;
//初始化信號量
static final Semaphore s 
    = new Semaphore(1);
//用信號量保證互斥    
static void addOne() {
  s.acquire();
  try {
    count+=1;
  } finally {
    s.release();
  }
}

But(英文聽力中的重點),Semaphore 肯定不是為這種特例存在的,它是共享式獲取同步狀態的一種實現。如果使用信號量,我們通常會將 permits 設置成大於1的值,不知道你是否還記得我曾在 為什麼要使用線程池? 一文中說到的池化概念,在同一時刻,允許多個線程使用連接池,每個連接被釋放之前,不允許其他線程使用。所以說 Semaphore 可以允許多個線程訪問一個臨界區,最終很好的做到一個限流/限流/限流 的作用

雖然 Semaphore 能很好的提供限流作用,說實話,Semaphore 的限流作用比較單一,我在實際工作中使用 Semaphore 並不是很多,如果真的要用高性能限流器,Guava RateLimiter 是一個非常不錯的選擇,我們後面會做分析,有興趣的可以提前了解一下

關於 Semaphore 源碼,就這麼三下五除二的結束了

總結

不知你有沒有感覺到,我們的節奏明顯加快了,好多原來分散的點在被瘋狂的串聯起來,如果按照這個方式來閱讀 JUC 源碼,相信你也不會一頭扎進去迷失方向,然後沮喪的退出 JUC 吧,然後面試背誦答案,然後忘記,然後再背誦?

跟上節奏,關於共享式獲取同步狀態,Semaphore 只不過是非常經典的應用,ReadWriteLock 和 CountDownLatch 日常應用還是非常廣泛的,我們接下來就陸續聊聊它們吧

靈魂追問

  1. Semaphore 的 permits 設置成1 “等同於” 簡單的互斥鎖實現,那它和 ReentrantLock 的區別還是挺大的,都有哪些區別呢?
  2. 你在項目中是如何使用 Semaphore 的呢?

參考

  1. Java 併發實戰
  2. Java 併發編程的藝術
  3. https://blog.csdn.net/anlian523/article/details/106319294

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

java併發編程 –併發問題的根源及主要解決方法

目錄

  • 併發問題的根源在哪
    • 緩存導致的可見性
    • 線程切換帶來的原子性
    • 編譯器優化帶來的有序性
  • 主要解決辦法
    • 避免共享
    • Immutability(不變性)
    • 管程及其他工具

併發問題的根源在哪

首先,我們要知道併發要解決的是什麼問題?併發要解決的是單進程情況下硬件資源無法充分利用的問題。而造成這一問題的主要原因是CPU-內存-磁盤三者之間速度差異實在太大。如果將CPU的速度比作火箭的速度,那麼內存的速度就像火車,而最慘的磁盤,基本上就相當於人雙腿走路。

這樣造成的一個問題,就是CPU快速執行完它的任務的時候,很長時間都會在等待磁盤或是內存的讀寫。

計算機的發展有一部分就是如何重複利用資源,解決硬件資源之間效率的不平衡,而後就有了多進程,多線程的發展。並且演化出了各種為多進程(線程)服務的東西:

  • CPU增加緩存機制,平衡與內存的速度差異
  • 增加了多個概念,CPU時間片,程序計數器,線程切換等,用以更好得服務併發場景
  • 編譯器的指令優化,希望在內部充分利用硬件資源

但是這樣一來,也會帶來新的併發問題,歸結起來主要有三個。

  • 由於緩存導致的可見性問題
  • 線程切換帶來的原子性問題
  • 編譯器優化帶來的有序性問題

我們分別介紹這幾個:

緩存導致的可見性

CPU為了平衡與內存之間的性能差異,引入了CPU緩存,這樣CPU執行指令修改數據的時候就可以批量直接讀寫CPU緩存的內存,一個階段后再將數據寫回到內存。

但由於現在多核CPU技術的發展,各個線程可能運行在不同CPU核上面,每個CPU核各有各自的CPU緩存。前面說到對變量的修改通常都會先寫入CPU緩存,再寫回內存。這就會出現這樣一種情況,線程1修改了變量A,但此時修改后的變量A只存儲在CPU緩存中。這時候線程B去內存中讀取變量A,依舊只讀取到舊的值,這就是可見性問題。

線程切換帶來的原子性

為了更充分得利用CPU,引入了CPU時間片時間片的概念。進程或線程通過爭用CPU時間片,讓CPU可以更加充分得利用。

比如在進行讀寫磁盤等耗時高的任務時,就可以將寶貴的CPU資源讓出來讓其他線程去獲取CPU並執行任務。

但這樣的切換也會導致問題,那就是會破壞線程某些任務的原子性。比如java中簡單的一條語句count += 1。

映射到CPU指令有三條,讀取count變量指令,變量加1指令,變量寫回指令。雖然在高級語言(java)看來它就是一條指令,但實際上確是三條CPU指令,並且這三條指令的原子性無法保證。也就是說,可能在執行到任意一條指令的時候被打斷,CPU被其他線程搶佔了。而這個期間變量值可能會被修改,這裏就會引發數據不一致的情況了。所以高併發場景下,很多時候都會通過鎖實現原子性。而這個問題也是很多併發問題的源頭。

編譯器優化帶來的有序性

因為現在程序員編寫的都是高級語言,編譯器需要將用戶的代碼轉成CPU可以執行的指令。

同時,由於計算機領域的不斷髮展,編譯器也越來越智能,它會自動對程序員編寫的代碼進行優化,而優化中就有可能出現實際執行代碼順序和編寫的代碼順序不一樣的情況。

而這種破壞程序有序性的行為,在有些時候會出現一些非常微妙且難以察覺的併發編程bug。

舉個簡單的例子,我們常見的單例模式是這樣的:

public class Singleton {
 
 private Singleton() {}

 private static Singleton sInstance;

 public static Singleton getInstance() {

    if (sInstance == null) {	//第一次驗證是否為null
      synchronized (Singleton.class) {   //加鎖
        if (sInstance == null) {	  //第二次驗證是否為null
          sInstance = new Singleton();  //創建對象
                 }
             }
         }
    return sInstance;
    }

}

即通過兩段判斷加鎖來保證單例的成功生成,但在極小的概率下,可能會出現異常情況。原因就出現在sInstance = new Singleton();這一行代碼上。這行代碼,我們理解的執行順序應該是這樣:

  1. 為Singleton象分配一個內存空間。
  2. 在分配的內存空間實例化對象。
  3. 把Instance 引用地址指向內存空間。

但在實際編譯的過程中,編譯器有可能會幫我們進行優化,優化完它的順序可能變成如下:

  1. 為Singleton對象分配一個內存空間。
  2. 把instance 引用地址指向內存空間。
  3. 在分配的內存空間實例化對象。

按照優化完的順序,當併發訪問的時候,可能會出現這樣的情況

  1. A線程進入方法進行第1次instance == null判斷。
  2. 此時A線程發現instance 為null 所以對Singleton.class加鎖。
  3. 然後A線程進入方法進行第2次instance == null判斷。
  4. 然後A線程發現instance 為null,開始進行對象實例化。
  5. 為對象分配一個內存空間。
    6.把Instance 引用地址指向內存空間(而就在這個指令完成后,線程B進入了方法)。
  6. B線程首先進入方法進行第1次instance == null判斷。
  7. B線程此時發現instance 不為null ,所以它會直接返回instance (而此時返回的instance 是A線程還沒有初始化完成的對象)

最終線程B拿到的instance 是一個沒有實例化對象的空內存地址,所以導致instance使用的過程中造成程序錯誤。解決辦法很簡單,可以給sInstance對象加上一個關鍵字,volatile,這樣編譯器就不會亂優化,有關volatile的具體內容後續再細說。

主要解決辦法

通過上面的介紹,其實可以歸納無論是CPU緩存,線程切換還是編譯器優化亂序,出現問題的核心都是因為多個線程要併發讀寫某個變量或併發執行某段代碼。那麼我們可以控制,一次只讓一個線程執行變量讀寫就可以了,這就是互斥

而在某些時候,互斥還不夠,還需要一定的條件。比如一個生產者一個消費者併發,生產者向隊列存東西,消費者向隊列拿東西。那麼生產者寫的時候要保證存的時候隊列不是滿的,消費者要保證拿的時候隊列非空。這種線程與線程間需要通信協作的情況,稱為同步同步可以說是更複雜的互斥

既然知道了併發編程的根源以及同步和互斥,那我們來看看有哪些解決的思路。其實一共也就三種:

  • 避免共享
  • Immutability(不變性)
  • 管程及其他工具

下面我們分別說說這三種方案的優缺點

避免共享

我們先來說說避免共享,其實避免共享說是線程本地存儲技術,在java中指的一般就是Threadlocal。ThreadLocal會為每個線程提供一個本地副本,每個線程都只會修改自己的ThreadLocal變量。這樣一來就不會出現共享變量,也就不會出現衝突了。

其實現原理是在ThreadLocal內部維護一個ThreadLocalMap,每次有線程要獲取對應變量的時候,先獲取當前線程,然後根據不同線程取不同的值,典型的以空間換時間。

所以ThreadLocal還是比較適用於需要共享資源,且資源佔用空間不大的情況。比如一些連接的session啊等等。但是這種模式應用場景也較為有限,比如需要同步情況就難以勝任。

Immutability(不變性)

Immutability在函數式中用得比較多,函數式編程的一個主要目的是要寫出無副作用的代碼,有關什麼是無副作用可以參考我以前的文章Scala函數式編程指南(一) 函數式思想介紹。而無副作用的一個主要特點就是變量都是Immutability即不可變的,即創建對象后不會再修改對象,比如scala默認的變量和數據結構都是不可變的。而在java中,不變性變量即通過final修飾的變量,如String,Long,Double等類型都是Immutability的,它們的內部實現都是基於final關鍵字的。

那這又和併發編程有什麼關係呢?其實啊,併發問題很大部分原因就是因為線程切換破壞了原子性,這又導致線程隨意對變量的讀寫破壞了數據的一致性。而不變性就不必擔心這個問題,因為變量都是不變,不可寫只能讀的。在這種編程模式下,你要修改一個變量,那麼只能新生成一個。這樣做的好處很明顯,但壞處也是顯而易見,那就是引入了額外的編程複雜度,喪失了代碼的可讀性和易用性。

因為如此,不變性的併發解決方案其實相對而已沒那麼廣泛,其中比較有代表性的算是Actor併發編程模型,我以前也有討論過,有興趣可以看看Actor模型淺析 一致性和隔離性,這種編程模型和常規併發解決方案有很顯著的差異。按我的了解,Acctor模式多用在分佈式系統的一些協調功能,比如維持集群中多個機器的心跳通信等等。如果在單機併發環境下,還是下面要介紹的管程類工具才是利器。

管程及其他工具

其實最早的操作系統中,解決併發問題用的是信號量,信號量通過兩個原子操作wait(S),和signal(S)(俗稱P,V操作)來實現訪問資源互斥和同步。比如下面這個小例子:

//整型信號量定義
int S;

//P操作
wait(S){
  while(S<=0);
  S--;
}

//V操作
signal(S){
  S++;
}

雖然信號量方便有效,但信號量要對每個共享資源都實現對應的P和V操作,這使得併發編程中可能要出現大量的P,V操作,並且這部分內容難以抽象出來。

為了更好地實現同步互斥,於是就產生了管程(即Monitor,也有翻譯為監視器),值得一提的是,管程也有幾種模型,分別是:Hasen模型,Hoare模型和MESA模型。其中MESA模型應用最廣泛,java也是參考自MESA模型。這裏簡單介紹下管程的理論知識,這部分內容參考自進程同步機制—–為進程併發執行保駕護航,希望了解更多管程理論知識的童鞋可以看看。

我們來通過一個經典的生產-消費隊列來解釋,如下圖

我們先解釋下圖中右半部分的內容,右上角有一個等待調用的線程隊列,管程中每次只能有一個線程在執行任務,所以多個任務需要等待。然後是各個名詞的意思,生產-消費需要往隊列寫入和取出東西,這裏的隊列就是共享變量對共享資源進行操作稱之為過程(入隊和出隊兩個過程)。而向隊列寫入和取出是有條件的,寫入的時候隊列必須是非滿的,取出的時候隊列必須是非空的,這兩個條件被稱為條件變量

然後再來看看左半部分的內容,假設線程T1讀取共享變量(即隊列),此時發現隊列為空(條件變量之一),那麼T1此時需要等待,去哪裡等呢?去條件變量隊列不能為空對應的隊列中去等待。此時另一個線程T2向共享變量隊列寫數據,通過了條件變量隊列不能滿,那麼寫完后就會通知線程T1。但因為管程的限制,管程中只能有一個線程在執行,所以T1線程不能立即執行,它會回到右上角的線程等待隊列等待(不同的管程模型在這裡是有分歧的,比如Hasen模型是立即中斷T2線程讓隊列中下一個線程執行)。

解釋完這個圖,管程的概念也就呼之欲出了,

hansen對管程的定義如下:一個管程定義了一個數據結構和能力為併發進程所執行(在該數據結構上)的一組操作,這組操作能同步進程和改變管程中的數據。

本質上,管程是對共享資源以及對共享資源的操作抽象成變量和方法,要操作共享變量僅能通過管程提供的方法(比如上面的入隊和出隊)間接訪問。所以你會發現管程其實和面向對象的理念是十分相近的,在java中,主要提供了低層次了synchronized關鍵字和wait(),notify()等方法。同時還提供了高層次的ReenTrantLock和Condition來實現管程模型。

以上~

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

特斯拉將調整全球超級充電站充電價格,車主錯愕

根據國外專門報導電動車產業消息的《Electrek》網站報導,電動車大廠特斯拉(Tesla)正式終止任何形式的免費充電計畫之後,準備將全球超級充電站(Supercharger)充電價格平均提高 33%,這舉動令車主錯愕。

自 2018 年 11 月以來,特斯拉所有新款電動車都必須遵守新超級充電站充電付費計畫,雖然沒有擴及 2018 年 11 月前購買特斯拉電動車的車主,特斯拉仍舊對這些車主提供有限度的免費充電服務。不過,這項優惠措施到 2019 年 1 月底為止,也就是之後再也不會有任何特斯拉車主有免費充電服務;新付費方式將以每度(小時千瓦;1kWh),或是部分地區每分鐘來計算充電費用。

觀察特斯拉的新充電費率,將以不同地區、甚至每個充電站的使用需求計價。特斯拉還希望根據當地電價,訂定更合理、更全面的價格。換句話說,這會造成大多數地區的超級充電樁價格大幅上漲。

在 2018 年,特斯拉已提高美國超級充電站的充電價格。調漲後多數地區的充電價格漲幅為 20%~40%,部分地區漲幅甚至高達 100%。以紐約市為例,過去是每度 0.24 美元,現在則是 0.32 美元,上漲 33%。加州地區,過去每度為 0.26 美元,調整之後是 0.32 到 0.36 美元不等。這次特斯拉全球充電價格調漲,美國市場已是第 2 次漲價。

歐洲方面,雖然大多數市場充電價格仍維持每度 0.28~0.32 歐元,以特斯拉在歐洲最重要的市場和超級充電站最密集的挪威來說,充電價格預計從每小時千瓦 1.4 挪威克朗,上升到 1.86 挪威克朗,幾乎漲了 33%。相信未來其他地區也會是類似漲幅。

特斯拉一直聲稱超級充電站「永遠不會成為利潤中心」。漲價計畫決定後,面對記者的詢問,特斯拉還是重申這點,並表示正在調整超級充電站的充電價格,希望更能反映當地電力成本,以及場地使用情況的差異。隨著特斯拉電動車增多,未來也繼續每週開設新超級充電站,讓更多消費者可長途行駛,並享受到比汽油價格低的充電價格,達到零排碳量的目標。未來,還希望利用超級充電站獲得的收入,建立更多充電站。

目前特斯拉全世界共有 1,422 個超級充電站,共有 12,011 個充電樁,而特斯拉的目標,是在 2019 年將這個數字倍增。

(合作媒體:。首圖來源: CC BY 2.0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

電動機車今年累計掛牌數已突破 4 萬輛,年成長達 2 倍

在政府鼓勵使用電動機車、車廠推出新車款及民眾環保意識抬頭等多方因素推波助瀾之下,近幾年電動機車掛牌數量逐年倍增;據經濟部工業局統計,105 年掛牌數量約 2 萬輛,106 年掛牌數量約 4.4 萬輛,107 年截至 8 月份已突破 4 萬輛,較去年同期大幅成長 2 倍。

工業局也預期,由於光陽、中華及三陽等下半年均有新車發表計畫,預期今年全年整體掛牌數仍會大幅成長。而工業局樂見民眾對電動機車產品的響應支持,也同步調整補助預算規則,經費將會優先補助民眾購買電動機車,並輔以補助設置能源補充設施。

隨著暑假到來,各車廠下半年將陸續推出新車款,也針對年輕學子與機車首購族祭出購車優惠。例如光陽推出兩款 New Many 110 EV,配合電池月租 99 元預購方案積極搶市,並將廣布 Ionex 充換電站;而睿能則推出 10 款 Gogoro 2 系列,搭配平均日付約 66 元銅板購車方案,並於 8 月 10 日前進宜蘭設置換電站以服務當地使用者;另中華汽車也於 8 月 3 日在宜蘭羅東開幕全新 emoving 專賣店;配合中央與地方政府購車補助,電動機車已成為民眾購買機車的優先考慮選項。

工業局統計,今年補助數量與去年同期相比,成長近 2.5 倍,其中重型等級佔比約 86.9%、輕型等級約 8.2%,小型輕型等級則約 4.9%。另據統計分析,目前電動機車的消費族群,男女比例各半,36-40 歲年齡層族群為購買主力,其次為 31-35 歲族群。而 40 歲以下的電動機車消費者,則呈現男性多於女性的現象。數量多集中於六都,銷售量依序為桃園市、新北市、高雄市、台中市、台南市與台北市等,數量總計超過全台之 86%。

此外,工業局也指出,未來更將積極與車廠透過建置能源補充設施及行銷方案,持續推動其他縣市消費市場,進一步落實電動機車推動政策。

(本文內容由 授權使用。首圖來源:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

新型液流電池可發電與供氫,電動車與氫燃料車都適用

電池電動車與氫燃料電池車都有望成為新一代交通主力,但這兩種車款各採用不同的「充電方式」,一種是電力、一種是補充氫氣,各國得針對兩種不同的車系分別打造管線與「加油站」,而近日英國格拉斯哥大學打造新型液流電池,不僅可讓汽車在幾秒內完成燃料補充,還可以釋放電力與氫氣、完美解決兩種電動車系統不相容的狀況。

液流電池(flow battery)由兩個電解質槽組成,充放電時電解質會被幫補到中間的發電室,而發電室也會以薄膜隔開兩種溶液、形成兩個電極,最後產生離子交換來發電,而由於兩種電解質是分開存放,不會有電解質相互滲漏與自身放電等安全性問題,是一種良好的儲能生力軍。

只是液流電池體積龐大,即使該電池具有安全性與穩定性高等優點,仍不適合用於 3C 產品與電動車,目前大多研究團隊都是想把液流電池用在再生能源儲能系統。

而格拉斯哥大學這次想將突破以往液流電池無法用在汽車的印象,並成功透過奈米粒子溶液打造新型液流電池,其中團隊所用的電解質是一種奈米懸浮液(suspension),每個奈米粒子都可以當成一顆小型電池,儲存能量更是一般液流電池的 10 倍,還能以電力或氫氣的形式釋放。

且由於液流電池主要以液態電解質驅動,團隊更指出,新型液流電池可在幾秒內完成移除舊液體、補充新電解質,因此該系統的「充電」時間可跟一般汽油車一樣,大大縮短電動車的充電速度。

格拉斯哥大學 Regius Chair of Chemistry 教授 Leroy(Lee)Cronin 表示,若再生能源想要有效運作,還需要儲存容量、靈活性高的儲能系統來幫忙解決間歇性能源與電力尖峰等問題,而團隊提出的新型化學充電方式,除了可用在儲能系統,還可應用在電動車中。

該研究將有助於未來液流電池的應用,有望縮短液流電池與大規模商業化的距離。團隊也指出,新型液流電池的能量密度相當高,可提升將來電動車的續航里程與儲能系統的儲存容量,目前研究已發表在《》。

(首圖來源:。文/DaisyChuang)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

工研院新創公司菲國參展,大推電動車充電自主技術

電動車輛取代燃油車已是不可逆的趨勢,工研院表示,旗下新創公司起而行綠能(eTreego)為搶進廣大東南亞市場,16 日於菲律賓馬尼拉舉行的「菲律賓國際汽機車零配件展」(Automechanic Philippines),發表以先進充電控制模組打造出的汽機車充電樁,符合歐美日等先進國家 4 種不同規格,溫度運作範圍從 -40℃~70℃,加上全方位充電解決方案,獲得到場參觀廠商與買家的高詢問度,成為本次展會亮點之一。

歐洲主要國家與台灣已設定 2040 年起停售燃油車為目標,大舉刺激全球電動車的銷售。工研院 IEK 統計,2017 年全球電動車市場銷售量約 266 萬輛,今年銷售量可望達到 317 萬輛,成長率達 19%。電動機車預估今年全球銷售約 54.9 萬輛,較去年約 33.3 萬輛規模,成長 64% 左右。而全球電動機車市場,亞洲地區佔比就高達 76.3%,以機車為主要代步工具的東南亞地區,發展潛力更是驚人。

為響應政府新南向政策,積極推動產業創新的新藍海,起而行綠能總經理簡金品表示,此次進軍菲律賓參展,除了拓展起而行綠能在國際市場的知名度,也是看好東南亞電動車市場潛力,希望能以公司創新研發能量,協助當地電動車發展。

起而行綠能為工研院新創公司,掌握電動車充電關鍵技術的充電模組,產品包括充電控制模組、充電機、充電站、電能管理,提供電動汽車與機車全方位充電方案。其產品優勢包括可對應美國汽車工程師學會 SAE、國際電工委員會 IEC、日本 CHAdeMO、中國 GB 等 4 項國際主要標準;運作溫度範圍彈性大,可在 -40℃~70℃ 之間正常使用,遠優於市面其他產品僅 0℃~60℃ 區間。

簡金品指出,起而行綠能團隊在工研院時期,曾參與制定台灣電動汽機車充電標準。2010 年台中市政府與裕日車的電動車合作案,其中所打造的台灣第一支汽車充電椿,就是來自起而行團隊。目前台灣的汽車充電椿則有一半以上是來自起而行。

起而行綠能表示,目前公司客戶包括台灣國內的裕隆、中華汽車等,海外市場包含大中華區等國際車廠與相關充電設備業者也都正在進行合作與供貨洽談。同時,起而行也與台灣多家機車廠合作發展符合國內電動機車共通充電產業標準之充電產品。除這些既有充電系統,起而行亦積極投入開發小型化、雙向充電、無線充電、快速充電與全功能的聯網系統,因應即將快速爆發的電動車商機,為市場提供全方面的充電解決方案。

據了解,菲律賓國際汽機車零配件展 2017 年有來自 11 國、280 廠商參展,參觀人數逾 7,000 人。參觀者與買家包括汽車品牌廠、零組件製造商、通路商、零售商與運輸服務商。

(本文內容由 授權使用。首圖來源:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

中國研擬汽車投資管理等新規,新能源智慧車料續受惠

經濟參考報報導,中國 2018 年前 7 個月新能源汽車年增 68.6%,保持持續增長態勢,並成為拉動整體汽車市場成長的重要力量。據中國國家發改委等部門瞭解,目前包括新的汽車投資管理規定等多項政策正在加緊推進,鼓勵技術、模式等創新,未來新能源智慧化汽車將迎來更多利多。

中國發改委表示,目前新的汽車產業投資管理規定已完成向社會公開徵求意見,正在加緊對徵求意見稿進行進一步完善,以期儘快發布。

據了解,中國此次準備發佈的新汽車投資管理辦法被稱為「最嚴燃油車產業政策」,未來新建獨立燃油車項目將被禁止,而現有汽車企業擴大燃油汽車生產能力也要同時滿足上兩個年度汽車產能利用率均高於全行業平均水準、上兩個年度新能源汽車產量佔比均高於全行業平均水準等四個條件。

在此同時,中國官方對於新能源汽車的准入門檻也大幅提高,要求新建的獨立純電動汽車企業專案要有純電動汽車持續開發能力,純電動乘用車建設規模不低於 10 萬輛,以及純電動商用車不低於 5,000 輛;此外,對新建新能源汽車企業的股東也提出了要求。

多位專家和業內人士表示,這將大大推動未來新能源汽車市場的發展,並提高了廠商、投資者,以及消費者對新能源汽車市場的信心。同時,對新能源汽車市場准入門檻的提高也將進一步提升新能源汽車市場的發展品質,推進企業加大在產品和技術方面的投入,實現優勝汰劣。

中國發改委產業協調司副司長蔡榮華表示,未來將進一步推動新能源智慧化汽車產業發展,發改委也將積極推動新能源智慧化汽車創新發展戰略儘快推出,鼓勵技術創新和模式創新,努力打造有利於新能源智慧汽車發展的生態系統和環境。

(本文內容由 授權使用。首圖來源:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

中國推新能源車結合再生能源應用,拚減碳逾 20 萬噸

新華社報導,中國新能源汽車和再生能源綜合應用商業化推廣專案正式啟動,據中國汽車工程學會常務副理事長兼秘書長張進華表示,整個專案營運期間將直接減排二氧化碳超過 20 萬噸。

張進華指出,新能源汽車和再生能源綜合應用商業化推廣專案的實施,是落實中國汽車產業中長期發展規劃的重要工作之一。據瞭解,為促進中國新能源汽車與再生能源產業綜合應用,充分發揮新能源汽車全生命週期的節能減排效益,減少溫室氣體排放,中國工信部和聯合國工業發展組織共同向全球環境基金申報此項目並獲得批准,中國汽車工程學會將全面承擔專案實施與組織工作,實施週期為 3 年。

據瞭解,該專案具體內容包括組織制定新能源汽車和再生能源綜合應用的政策和標準法規,展開智慧充電系統、再生能源微電網、車網互動、移動充電系統、退役電池梯次利用等技術示範和驗證工作。目前,已確定將中國上海市、江蘇省鹽城市和如皋市作為示範城市,展開新能源汽車和再生能源綜合應用的技術示範和驗證。

(本文內容由 授權使用。首圖來源:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

挪威帶頭衝,歐洲電動車銷售量突破百萬大關

根據 EV volumes 資料顯示,歐洲上半年電動車(含純電池動力與插電式混合動力車)銷售量猛增,歷史累計銷售量已在 6 月正式突破百萬輛大關。

2018 上半年,歐洲電動車銷售量每月至少增加 5,000 輛,累計銷售量達 19.5 萬輛,較去年成長 42%,其中純電池動力車占 51%,其餘為混合動力車。

據 EV volumes 估計,歐洲 2018 全年電動車銷售量將達 43 萬輛,對照全球預估來到 135 萬輛。

挪威目前是歐洲電動車銷售量最高的國家,不過德國在後猛追,EV volumes 預期今年底德國電動車銷售量就將超越挪威。

中國電動車銷售量更早突破 100 萬輛,幾乎比歐洲快了一年,且需求持續成長,預估 2018 年底就會超越兩百萬輛。

(本文內容由 授權使用。首圖來源:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!