環境資訊中心綜合外電;姜唯 編譯;林大利 審校
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※超省錢租車方案
※別再煩惱如何寫文案,掌握八大原則!
※回頭車貨運收費標準
※教你寫出一流的銷售文案?
CS-LogN思維導圖:記錄CS基礎 面試題
開源地址:https://github.com/FISHers6/CS-LogN
線程池相關類
能獲取子線程的運行結果
互斥同步(鎖)
非互斥同(原子類)
併發容器
無同步與不可變方案
降低資源消耗
提高響應速度
提高線程的可管理性
corePoolSize、maximumPoolSize、keepAliveTime、workQueue、threadFactory、handler
圖示
線程添加規則
1.如果線程數量小於corePoolSize,即使工作線程處於空閑狀態,也會創建一個新線程來運行新任務,創建方法是使用threadFactory
2.如果線程數量大於corePoolSize但小於maximumPoolSize,則將任務放入隊列
3.如果workQueue隊列已滿,並且線程數量小於maxPoolSize,則開闢一個非核心新線程來運行任務
4.如果隊列已滿,並且線程數大於或等於maxPoolSize,則拒絕該任務,執行handler
圖示(分別與3個參數比較)
常用線程池
newFixedThreadPool
newSingleThreadExecutor
newCachedThreadPool
newScheduledThreadPool
如何設置初始化線程池的大小?
可根據線程池中的線程
處理任務的不同進行分別估計
CPU 密集型任務
IO 密集型任務
使用線程池的注意事項
線程池的狀態
線程池停止
shutdown
shutdownNow
線程池的組成
1.線程池管理器
2.工作線程
3.任務隊列:無界、有界、直接交付隊列
4.任務接口Task
圖示
Executor家族
Executor頂層接口,只有一個execute方法
ExecutorService繼承了Executor,增加了一些新的方法,比如shutdown擁有了初步管理線程池的功能方法
Executors工具類,來創建,類似Collections
圖示
線程池實現任務復用的原理
線程池對線程作了包裝,不需要啟動線程,不需要重複start線程,只是調用已有線程固定數量的線程來跑傳進來的任務run方法
添加工作線程
重複利用線程執行不同的任務
每個 Thread 維護着一個 ThreadLocalMap 的引用;ThreadLocalMap 是 ThreadLocal 的內部類,用 Entry 來進行存儲;key就對應一個個ThreadLocal
get方法:取出當前線程的ThreadLocalMap,然後調用map.getEntry方法,把ThreadLocal作為key參數傳入,取出對應的value
set方法:往 ThreadLocalMap 設置ThreadLocal對應值
initalValue方法:延遲加載,get的時候設置初始化
圖示
value內存泄漏
原因:ThreadLocal 被 ThreadLocalMap 中的 entry 的 key 弱引用。如果 ThreadLocal 沒有被強引用, 那麼 GC 時 Entry 的 key 就會被回收,但是對應的 value 卻不會回收,就會造成內存泄漏
解決方案:每次使用完 ThreadLocal,都調用它的 remove () 方法,清除value數據。
源碼圖示
引入目的
解決Runnable的缺陷
是什麼如何使用
引入目的
常用方法
使用場景
注意點
Callable和Future的關係
Future相當於一個存儲器,它存儲未來call()任務方法的返回值結果
可以用Future.get方法來獲取Callable接口的執行結果,在call()未執行完畢之前沒調用get的線程會被阻塞
線程池傳入Callable,submit返回Future,get獲取值
FutureTask
FutureTask是一種包裝器,可以把Callable轉化成Future和Runnable,它同時實現了二者的接口。所以既可以作為Runnable任務被線程執行,又可以作為Future得到Callable的返回值
圖示
final修飾變量
被final修飾的變量,意味着值不能被修改。
如果變量是對象,那麼對象的引用不能變,但是對象自身的內容依然可以變化。
賦值時機
屬性被聲明為final后,該變量則只能被賦值一次。且一旦被賦值,final的變量就不能再被改變,如論如何也不會變。
區分為3種
final instance variable(類中的final屬性)
final static variable(類中的static final屬性)
final local variable(方法中的final變量)
為什麼規定時機
final修飾方法(構造方法除外)
final修飾類
ABA問題
CAS+自旋,導致自旋時間過長
改進:通過版本號的機制來解決。每次變量更新的時候,版本號加 1,如AtomicStampedReference的compareAndSet ()
簡介
Lock和Synchronized的異同點
相同點
不同點
Lock 有比 synchronized 更精確的線程語義和更好的性能;高級功能
1 實現原理不同
2 靈活性不同
3 等待時是否可以中斷
可見性
悲觀鎖(互斥同步鎖)
思想
實例
缺點
樂觀鎖
思想
實例
優缺點對比
對比
什麼是可重入
可重入的好處
可重入鎖ReentrantLock與非可重入鎖ThreadPoolExecutor的Worker類對比
公平鎖
介紹
優點
缺點
非公平鎖
介紹
優點
缺點
優缺點對比
源碼分析
排他鎖
介紹
共享鎖
介紹
ReentrantReadWriteLock
讀寫鎖的作用
讀寫鎖的規則
一把鎖兩種方式鎖定
讀線程插隊策略(非公平下)
鎖升級
引入場景
策略
適合場景
阻塞鎖
思想
開銷缺陷
自旋鎖
思想
開銷缺陷
源碼分析
atomic包下的類基本都是自旋鎖的實現
AtomicInteger的實現:自旋鎖實現原理是CAS,Atomic調用Unsafe進行自增add的源碼中的do-while循環就是一個自旋操作,使用CAS如果修改過程中遇到其它線程修改導致沒有秀嘎四成功,就在while里死循環,直至修改成功
圖示
適用場景
介紹
使用場景
JDK1.6 后對synchronized鎖的優化
JDK1.6 對鎖的實現引入了大量的優化,如偏向鎖、輕量級鎖、自旋鎖、適應性自旋鎖、鎖消除、鎖粗化等技術來減少鎖操作的開銷。
偏向鎖
輕量級鎖
重量級鎖
自旋鎖
自適應自旋鎖
鎖消除
鎖粗化
寫代碼時的優化
常用方法
實現原理
缺點
引入目的/改進思想
設計思想
集合類歷史
為什麼需要
為什麼不用HashMap
為什麼不用Collection.synchronizedMap
數據結構與併發策略
JDK1.7
JDK1.8
1.7到1.8改變後有哪些優點
注意事項
引入目的
適合場景
讀寫規則
實現原理
缺點
為什麼使用隊列
併發隊列關係圖示
BlockingQueue阻塞隊列
阻塞隊列是局由自動阻塞功能的隊列,線程安全;take方法移除隊頭,若隊列無數據則阻塞直到有數據;put方法插入元素,如果隊列已滿就無法繼續插入則阻塞直到隊列里有了空閑空間
ArrayBlockQueue
LinkedBlockingQueue
PriorityBlockingQueue
SynchronousQueue
DelayQueue
非阻塞隊列
ConcurrentLinkedQueue
選擇合適的隊列
邊界上看
內存上看
吞吐量上看
控制併發流程的工具類,作用是幫助程序員更容易讓線程之間相互配合,來滿足業務邏輯
併發工具類圖示
作用(事件)
常用方法
作用
常用方法
作用
常用方法
作用(線程)
常用方法
Exclusive(獨佔)
Share(共享)
核心三要素
1.sate
2.控制線程搶鎖和配合的FIFO隊列
3.期望協作工具類去實現的“獲取/釋放”等喚醒分配的方法策略
AQS的用法
AQS在CountDownLatch的應用
內部類Sync繼承AQS
1.state表示門閂倒數的count數量,對應getCount方法獲取
2.釋放方法,countDown方法會讓state減1,直到減為0時就喚醒所有線程。countDown方法調用releaseShared,它調用sync實現的tryReleaseShared,其使用CAS+自旋鎖,來實現安全的計數-1
3.阻塞方法,await會調用sync提供的aquireSharedInterruptly方法,當state不等於0時,最終調用LockUpport的park,它利用Unsafe的park,native方法,把線程加入阻塞隊列
總結
AQS在Semphore的應用
state表示信號量允許的剩餘許可數量
tryAcquire方法,判斷信號量大於0就成功獲取,使用CAS+自旋改變state狀態。如果信號量小於0了,再請求時tryAcquireShared返回負數,調用aquireSharedInterruptly方法就進入阻塞隊列
release方法,調用sync實現的releaseShared,會利用AQS去阻塞隊列喚醒一個線程
總結
AQS在ReentrantLock的應用
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
生產者與消費者問題是個非常典型的多線程問題,涉及到的對象包括“生產者”、“消費者”、“倉庫”和“產品”。他們之間的關係如下:
①、生產者僅僅在倉儲未滿時候生產,倉滿則停止生產。
②、消費者僅僅在倉儲有產品時候才能消費,倉空則等待。
③、當消費者發現倉儲沒產品可消費時候會通知生產者生產。
④、生產者在生產出可消費產品時候,應該通知等待的消費者去消費。
生產者消費者模型具體來講,就是在一個系統中,存在生產者和消費者兩種角色,他們通過內存緩衝區進行通信,生產者生產消費者需要的資料,消費者把資料做成產品。生產消費者模式如下圖。
在日益發展的服務類型中,譬如註冊用戶這種服務,它可能解耦成好幾種獨立的服務(賬號驗證,郵箱驗證碼,手機短信碼等)。它們作為消費者,等待用戶輸入數據,在前台數據提交之後會經過分解併發送到各個服務所在的url,分發的那個角色就相當於生產者。消費者在獲取數據時候有可能一次不能處理完,那麼它們各自有一個請求隊列,那就是內存緩衝區了。做這項工作的框架叫做消息隊列。
下面通過生產包子的例子及wait()/notify()方式實現該模型(後面學習線程池相關內容之後,再通過其它方式實現生產/者消費者模型)。
public class Bread { private int capacity; // 麵包的容量 private int size; // 麵包的實際數量 public Bread(int capacity) { this.capacity = capacity; this.size = 0; } // 生產麵包 public synchronized void produce(int val) { try { // left 表示“想要生產的數量”(有可能生產量太多,需多此生產) int left = val; while (left > 0) { // 庫存已滿時,等待“消費者”消費產品。 while (size >= capacity) wait(); // 獲取“實際生產的數量”(即庫存中新增的數量) // 如果“庫存”+“想要生產的數量”>“總的容量”,則“實際增量”=“總的容量”-“當前容量”。(此時填滿倉庫) // 否則“實際增量”=“想要生產的數量” int inc = (size+left)>capacity ? (capacity-size) : left; size += inc; left -= inc; System.out.printf("%s produce(%3d) --> left=%3d, inc=%3d, size=%3d\n", Thread.currentThread().getName(), val, left, inc, size); // 通知“消費者”可以消費了。 notifyAll(); } }catch(InterruptedException e) { e.printStackTrace(); } } // 消費麵包 public synchronized void consume(int val) { try { // left 表示“客戶要消費數量”(有可能消費量太大,庫存不夠,需多此消費) int left = val; while (left > 0) { // 庫存為0時,等待“生產者”生產產品。 while (size <= 0) wait(); // 獲取“實際消費的數量”(即庫存中實際減少的數量) // 如果“庫存”<“客戶要消費的數量”,則“實際消費量”=“庫存”; // 否則,“實際消費量”=“客戶要消費的數量”。 int dec = (size<left) ? size : left; size -= dec; left -= dec; System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d\n", Thread.currentThread().getName(), val, left, dec, size); notifyAll(); } } catch (InterruptedException e) { e.printStackTrace(); } } }
public class Producer{ Bread bread; public Producer(Bread bread) { this.bread = bread; } public void produce(final int val) { new Thread(() -> { bread.produce(val); }).start();; } }
public class Customer { private Bread bread; public Customer(Bread bread) { this.bread = bread; } public void consume(final int val) { new Thread(() -> { bread.consume(val); }).start();; } }
public class Demo { public static void main(String[] args) { Bread bread = new Bread(100); Producer producer = new Producer(bread); Cunstomer customer = new Customer(bread); producer.produce(60); producer.produce(120); consumer.consume(90); consumer.consume(150); producer.produce(110); } }
// 運行結果 Thread-1 produce( 60) --> left= 0, inc= 60, size= 60 Thread-5 produce(110) --> left= 70, inc= 40, size=100 Thread-4 consume(150) <-- left= 50, dec=100, size= 0 Thread-2 produce(120) --> left= 20, inc=100, size=100 Thread-3 consume( 90) <-- left= 0, dec= 90, size= 10 Thread-4 consume(150) <-- left= 40, dec= 10, size= 0 Thread-5 produce(110) --> left= 0, inc= 70, size= 70 Thread-4 consume(150) <-- left= 0, dec= 40, size= 30 Thread-2 produce(120) --> left= 0, inc= 20, size= 50
說明:
①、Producer是“生產者”類,它與“麵包(bread)”關聯。當調用“生產者”的produce()方法時,它會新建一個線程並向“麵包類”中生產產品。
②、Customer是“消費者”類,它與“麵包(bread)”關聯。當調用“消費者”的consume()方法時,它會新建一個線程並消費“麵包類”中的產品。
③、Bread是麵包類,記錄“麵包的產量(capacity)”以及麵包當前實際數目(size)”。
麵包類的生產方法produce()和消費方法consume()方法都是synchronized方法,進入synchronized方法體,意味着這個線程獲取到了該“麵包”對象的同步鎖。這也就是說,同一時間,生產者和消費者線程只能有一個能運行。通過同步鎖,實現了對“殘酷”的互斥訪問。
對於生產方法 produce() 而言:當麵包量滿時,生產者線程等待,需要等待消費者消費產品之後,生產線程才能生產;生產者線程生產完麵包之後,會通過 notifyAll() 喚醒同步鎖上的所有線程,包括“消費者線程”,即我們所說的“通知消費者進行消費”。
對於消費方法consume()而言:當倉庫為空時,消費者線程等待,需要等待生產者生產產品之後,消費者線程才能消費;消費者線程消費完產品之後,會通過 notifyAll() 喚醒同步鎖上的所有線程,包括“生產者線程”,即我們所說的“通知生產者進行生產”。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※南投搬家公司費用需注意的眉眉角角,別等搬了再說!
※新北清潔公司,居家、辦公、裝潢細清專業服務
※教你寫出一流的銷售文案?
二哥,最近我剛進了一家公司,之前跟你說過,培訓出身剛剛畢業,打算在北京打拚。最近進公司,給安排了工作,今天第一次沒人帶,自己上手搞代碼,搞不出,明明挺簡單的功能,自己還是做不出,不知道從哪裡學習,想趕快熟悉工作,可是自己的能力不行,在地鐵上常看二哥原創的作品,平常积極在看,超級希望能自己學到本事,但自己的能力真的有點問題,工作搞不完,害怕被問,害怕任務完不成被辭退。
以上是讀者西瓜向我提的一個問題,我覺得挺具有代表性的,所以決定拉出來單獨寫一篇文章答疑解惑一下。
可以肯定的一點是,任何時候都要說自己不行啊,尤其是男性同胞,可以認慫,但是“不行”這個兩個字千萬不要輕易說出口,為什麼?你懂吧?
人的能力各有不同,但如果你自己都不自信,那又能做好什麼事情呢?心理建設非常重要。
記得之前看一個短片,一個小男孩跳了無數次,都無法越過障礙物,但是呢,他身邊的同學一直為他加油吶喊,小男孩呢,也從來沒有放棄的打算,最後的結果我都快看哭了,他真的跳過去了,他出色地完成了自我挑戰。
他的成功,離不開同學們的鼓勵,但更重要的是他鍥而不舍的精神,心裏素質比一般的成年人都要強大。
我現在已經為人父了,雖然我一直標榜自己只有 18 歲,但叫二叔的讀者真的越來越多,我已經逆來順受了。在我的教育觀念里,我覺得我家女兒最優秀的一點品質,就是,如果她喜歡一件事,她就會主動去鑽研,去摸索,在沒有任何外人的幫助下。
你比如說,現在比較流行的平衡車,就是不帶腳踏板和鏈條的自行車。一開始,我想給她報個班,至少有個老師教教,對吧?
但是,她的表現完全出乎我的意料,我只要把車買回來,放到她的面前,怎麼騎,完全靠她自己去體驗。一開始小心翼翼,很保本,但她不滿足於現狀,就找一些小坡騎,然後是再大一點的坡,就這樣,挑戰一次又一次,自己就完全掌握了騎行的技術。
對於我來說,我沒有騎平衡車的經驗,小時候也沒有這玩意。我能做的除了買車,就是給她鼓勵,摔倒了沒事,哭了也沒事,有些事情,痛苦的同時,伴隨着挑戰和突破。
對於我們成年人來說,其實道理都懂的,但人與人之間的差距之所以拉開,除了選擇的正確有否,最大的因素我想就是,你有沒有自己主動去做。
西瓜說自己明明很簡單的功能,就是做不出來。這種感覺我也有過,即便是現在有了十年多的編程經驗,仍然在某些時刻感到举手無措,無從下手。
對,這就是為什麼人要終身學習的原因啊。我們做不出來,除了思維上、認知上的局限性,另外一個重要的點就在於,你有沒有經驗。
對於新人來說,經驗肯定是欠缺的,這點毫無疑問,對吧?但是只要公司招你進去了,無論是不是培訓班出身,負責任的公司都會給你充足的時間和空間去進步,就看你自己有沒有主動。
我大三出去實習的時候,公司要求我做一個計算器,那時候覺得好難啊,因為加減乘除,再帶上小括號,運算是有優先級的,還要考慮到小括號的自動補齊,對於那時候菜得一筆的我來說,真特么難啊。
但能怎麼辦?做不出來就意味着要被辭退,那只有一個辦法,就是上網搜,找別人的例子模仿,拆分,融化,把它變成是自己的。
那時候,我還不會玩 GitHub、碼雲和開源中國,私下里主動學習的地方只有一個,好像是叫編程入門網,現在已經沒有了。我就是照着上面的例子,一個個手敲,當你例子敲多了,很多編程知識就融會貫通了。
現在好了,優秀的案例數不勝數。我的兩個好朋友,macrozheng 開源了他的電商平台 mall,江南一點雨開源了他的微人事系統 vhr,這兩個開源項目我一直強烈推薦新手去下載到本地,去學習。
很多時候,對於編程天賦一般的我們來說,不需要主動去造輪子,我們只需要去發現輪子,對吧?
我在一開始做 Web 管理系統的時候,找了一個企業級的開源系統,叫做 DWZ,不知道有沒有讀者朋友用過,當年非常火,我們公司的後台管理系統現在還在用,雖然說界面已經很古董了,但對於我們公司來說,足夠用了。
這套 DWZ 就封裝了很多前端組件,對於我一個 Java 程序員來說,非常友好,直接可以上手操作,如果一些組件不滿足,我就去改造。改造的過程中,就積攢了大把解決問題的實戰經驗,這是彌足珍貴的。
我在《Web全棧開發進階之路》這本書里,就借鑒了不少 DWZ 的優秀思想。不要覺得不會造輪子是可恥的,會用輪子也是真本領啊。
就西瓜來說,平常喜歡看我的原創文,那我文章涉及到的例子有沒有去敲呢?如果你敲了,你就會發現,文章里涉及到的例子能解決大部分新人在工作中遇到的問題,直接把這些作為自己的工具包,下次遇到拿來即用就可以了。
對於 Java 程序員來說,JDK 的原生 API 不能滿足需求的話,還有很多第三方的類庫,比如說 Apache 的,封裝了大量常用的工具類和方法。前提條件是,你必須得知道有這些東西,如果不知道的話,那就無從下手了,對吧?
那怎麼見多識廣呢?這就回到了之前所說的,你得去練,動手去練,無論是書本里的,還是文章里的,還是開源項目里的,你得去手操一遍,不要眼高手低,敲多了,自然就形成了自己解決問題的思路和方法。
擔心自己被辭退是一件好事,這會督促我們前進,對吧?有的人,有自驅力,不需要外力的干預就能奮發圖強,有的人,就需要一條看不見的鞭子抽打着,才會有前進的動力。
別懷疑自己,真的,人嘛,總是有能力強弱之分的,要學會接納自己,像二哥一樣自信點,腳踏實地,一點一點去進步,當你取得一點成績的時候就把這些當做是里程碑,隨着時間的推移,你就會發現,自己變禿了,不不不,變強了。
加油,西瓜!
如果覺得文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀。
本文已收錄 GitHub,傳送門~ ,裏面更有大廠面試完整考點,歡迎 Star。
我是沉默王二,一枚有顏值卻靠才華苟且的程序員。關注即可提升學習效率,別忘了三連啊,點贊、收藏、留言,我不挑,嘻嘻。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※新北清潔公司,居家、辦公、裝潢細清專業服務
※別再煩惱如何寫文案,掌握八大原則!
※教你寫出一流的銷售文案?
※超省錢租車方案
我們都知道,線性表的存儲結構分為兩種,順序存儲結構和鏈式存儲結構,線性表的分類可以參考下圖來學習記憶。今天我們主要來學習一下鏈式存儲結構。
“鏈式存儲結構,地址可以連續也可以不連續的存儲單元存儲數據元素”——來自定義。
其實,你可以想象這樣一個場景,你想找一個人(他的名字叫小譚),於是你首先去問 A , A 說他不知道,但是他說 B 可能知道,並告訴了你 B 在哪裡,於是你找到 B ,B 說他不知道,但是他說 C 可能知道,並告訴了你 C 的地址,於是你去找到 C ,C 真的知道小譚在何處。
上面場景其實可以幫助我們去理解鏈表,其實每一個鏈表都包含多個節點,節點又包含兩個部分,一個是數據域(儲存節點含有的信息),一個是指針域(儲存下一個節點或者上一個節點的地址),而這個指針域就相當於你去問B,B知道C的地址,這個指針域就是存放的 C 的地址。
鏈表下面其實又細分了3種:單鏈表、雙向鏈表和循環鏈表。今天我們先講單鏈表。
什麼是單鏈表呢?單鏈表就是每一個節點只有一個指針域的鏈表。如下圖所示,就是一個帶頭節點的單鏈表。下面我們需要知道什麼是頭指針,頭節點和首元節點。
頭指針:指向鏈表節點的第一個節點的指針
頭節點:指在鏈表的首元節點之前附設的一個節點
首元節點:指在鏈表中存儲第一個實際數據元素的節點(比如上圖的 a1 節點)
單鏈表的創建有兩種方式,分別是頭插法和尾插法。
頭插法,顧名思義就是把新元素插入到頭部的位置,每次新加的元素都作為鏈表的第一個節點。那麼頭插入法在Java中怎麼實現呢。首先我們需要定義一個節點,如下
public class ListNode {
public int val; //數據域
public ListNode next;//指針域
}
然後我們就創建一個頭指針(不帶頭節點)
//元素個數
int n = 5;
//創建一個頭指針
ListNode headNode = new ListNode();
//頭插入法
headNode= createHead(headNode, n);
然後創建一個私有方法去實現頭插法,這裏我們插入5個新元素,頭插入的核心是要先斷開首元節點和頭指針的連接,也就是需要先將原來首元節點的地址存放到新節點的指針域里,也就是 newNode.next = headNode.next,然後再讓頭指針指向新的節點 headNode.next = newNode,這兩步是頭插入的核心,一定要理解。
/**
* 頭插法
* 新的節點放在頭節點的後面,之前的就放在新節點的後面
* @param headNode 頭指針
* @return
*/
private static ListNode createHead(ListNode headNode, int n) {
//插入5個新節點
for (int i = 1; i <= n; i++) {
ListNode newNode = new ListNode();
newNode.val = i;
//將之前的所有節點指向新的節點(也就是新節點指向之前的所有節點)
newNode.next = headNode.next;
//將頭指針指向新的節點
headNode.next = newNode;
}
return headNode;
}
最後我把鏈表打印輸出一下(其實也是單鏈表的遍歷),判斷條件就是只有當指針域為空的時候才是最後一個節點。
private static void printLinkedList(ListNode headNode) {
int countNode = 0;
while (headNode.next != null){
countNode++;
System.out.println(headNode.next.val);
headNode = headNode.next;
}
System.out.println("該單鏈表的節點總數:" +countNode);
}
最後的輸出結果顯然是逆序,因為沒一個新的元素都是從頭部插入的,自然第一個就是最後一個,最後一個就是第一個:
尾插法,顧名思義就是把新元素插入到尾部的位置(也就是最後一個位置),每次新加的元素都作為鏈表的第最後節點。那麼尾插法在 Java 中怎麼實現呢,這裏還是採用不帶頭節點的實現方式,頭節點和頭指針和頭插入的實現方式一樣,這裏我就直接將如何實現:
/**
* 尾插法
* 找到鏈表的末尾結點,把新添加的數據作為末尾結點的後續結點
* @param headNode
*/
private static ListNode createByTail(ListNode headNode, int n) {
//讓尾指針也指向頭指針
ListNode tailNode = headNode;
for (int i = 1; i <= n; i++) {
ListNode newNode = new ListNode();
newNode.val = i;
newNode.next = null;
//插入到鏈表尾部
tailNode.next = newNode;
//指向新的尾節點,tailer永遠存儲最後一個節點的地址
tailNode = newNode;
}
return headNode;
}
和頭插入不同的是,我們需要聲明一個尾指針來輔助我們實現,最開始,尾指針指向頭指針,每插入一個元素,尾指針就后移一下,這裏我們來講一下原理:每次往末尾新加一個節點,我們就需要把原來的連接斷開,那怎麼斷開呢,我們首先需要讓尾指針指向新的節點,也就是 tailNode.next = newNode; 然後再讓尾指針后移一個位置,讓尾指針指向最後一個節點。也就是尾指針始終指向最後一個節點,最後將頭指針返回,輸出最後結果:
既然單鏈表創建好了,怎麼在鏈表裡面刪除元素呢,單鏈表的刪除,我分為了兩種情況刪除,分別是刪除第i個節點和刪除指定元素的節點。
我們可以先來理一下思路:在單鏈表裡,節點與節點之間都是通過指針域鏈接起來的,所以如果我們想實現刪除的操作,實際上是需要我們去改變相應指針域對應得地址的。當想去刪除第i個元素的時候,比如要刪除上圖的第3個元素(也就是3),實際上我們要做的就是要讓2號元素指向4號元素(其實就是需要修改2號元素的指針域,讓2號元素的指針域存儲4號元素)。那麼怎麼做才能實現這一步呢?很顯然,要實現這個步驟,我們必須要找到4號元素和2號元素,但是再仔細想一下,其實我們只需要找到2號元素就可以了,因為4號元素的地址存儲再2號的下一個元素的指針域裏面。
所以綜上所述分析我們可以得出刪除的兩個核心步驟:
1.刪除第i個節點,需要先找到第 i-1 個個節點,也就是第i個節點的前一個節點;
2.然後讓第 i-1 個節點指向第 i-1 個節點的下下個節點
下面的代碼具體實現了怎麼刪除第i個元素。
/**
* 刪除第i個節點
* 1,2 4,4,5
* 刪除之後應該是1,2,4,5
* @param headNode
* @param index
* @return
*/
public static ListNode deleteNodeByIndex(ListNode headNode, int index) {
int count = 1;
//將引用給它
ListNode preNode = headNode;
//看計數器是不是到了i-1,如果到了i-1,就找到了第i-1個節點
while (preNode.next != null && count <= index -1){
//尋找要刪除的當前節點的前一個節點
count++;
preNode = preNode.next;
}
if (preNode != null){
preNode.next = preNode.next.next;
}
return headNode;
}
刪除指定元素節點的實現方法有兩種,第一種就是先找到指定元素對應的鏈表的位置( index ),然後再按照刪除第 i 個節點的思路刪除即可。實現方法如下圖所示:
/**
* 刪除鏈表指定數值的節點
* @param headNode
* @param val
* @return
*/
private static ListNode deleteNodeByNum(ListNode headNode, int val) {
ListNode deleteOne = headNode;
int countByDeleteOne = 1;
while (deleteOne.next != null){
if (deleteOne.next.val == val){
deleteOne = deleteOne.next;
break;
}
countByDeleteOne ++;
deleteOne = deleteOne.next;
}
return deleteNodeByIndex(headNode, countByDeleteOne);
}
第二種方法的實現就很精妙(前提是此節點不是尾節點)
public void deleteNode(ListNode node) {
//刪除node即通過將後面的值賦給node,然後更改node的指針指向下下一個結點即可
node.val = node.next.val;
node.next = node.next.next;
}
單鏈表的查詢實現很簡單,就是遍歷當前單鏈表,然後用一個計數器累加到當前下標,那麼當前的這個節點就是要查詢的那個節點,然後再返回即可,當然需要判斷傳過來的這個下標是否合法。當然如果需要修改,就需要把當前找到的節點的數據域重新賦上需要修改的值即可,這裏就不上代碼了。具體實現如下:
private static ListNode searchLinkedList(ListNode headNode, int index) {
//如果下標是不合法的下標就表示找不到
if (index < 1 || index > getLinkedListLength(headNode)){
return null;
}
for (int i = 0; i < index; i++) {
headNode = headNode.next;
}
return headNode;
}
獲取單鏈表的長度(注意我這裏定義的 headNode 是頭指針不是頭節點)
/**
* 求單鏈表長度
* @param headNode
* @return
*/
private static int getLinkedListLength(ListNode headNode) {
int countNode = 0;
while (headNode.next != null){
countNode++;
headNode = headNode.next;
}
return countNode;
}
單鏈表的相關操作就講解完了,其實通過上面對單鏈表的相關操作,我們不難發現,單鏈表的刪除和插入其實很方便,只需要改變指針的指向就可以完成,但是查找元素的時候就比較麻煩,因為在查找的時候,需要把整個鏈表從頭到尾遍歷一次。
公眾號:良許Linux
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※新北清潔公司,居家、辦公、裝潢細清專業服務
※別再煩惱如何寫文案,掌握八大原則!
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※超省錢租車方案
※教你寫出一流的銷售文案?
Docker 是一個開發、發布和運行應用程序的開放平台。Docker使您能夠將應用程序與基礎架構分離,以便快速交付軟件。有了 Docker,你可以像管理應用程序一樣管理你的基礎設施。通過利用 Docker 快速發布、測試和部署代碼的方法,您可以顯著減少編寫代碼和在生產環境中運行它之間的延遲。
Docker 提供了在鬆散隔離的環境(稱為容器)中打包和運行應用程序的能力。隔離和安全性允許您在給定的主機上同時運行多個容器。容器是輕量級的,因為它們不需要額外的hypervisor負載,而是直接在主機的內核中運行。這意味着您可以在給定的硬件組合上運行比使用虛擬機時更多的容器。你甚至可以在實際是虛擬機的主機中運行 Docker 容器!
Docker 提供了工具和平台來管理容器的生命周期:
Docker 引擎是一個 客戶端-服務器 應用程序,具有以下主要組件:
dockerd 命令)的長時間運行程序。docker命令)。 CLI 使用Docker REST API通過腳本或直接CLI命令控制Docker守護進程或與之交互。
許多其他Docker應用程序使用底層API和CLI。
這個守護進程創建和管理 Docker 對象,如鏡像、容器、網絡和卷(images, containers, networks, and volumes)。
注意: Docker使用的是開源 Apache 2.0 許可證。
有關更多細節,請參閱下面的 Docker 架構。
快速、一致地交付應用程序
Docker 允許開發人員使用提供應用程序和服務的本地容器,在標準化的環境中工作,從而簡化了開發生命周期。容器對於持續集成和持續交付(CI/CD)工作流非常有用。
考慮以下示例場景:
響應式部署和擴展
Docker 的基於容器的平台允許高度可移植的工作負載。Docker 容器可以運行在開發人員的本地筆記本電腦上、數據中心的物理或虛擬機上、雲提供商上或在混合的環境中。
Docker 的可移植性和輕量級性質也使得它可以很容易地動態管理工作負載,根據業務需要,在接近實時的情況下擴展或拆除應用程序和服務。
在相同硬件上運行更多工作負載
Docker 是輕量級和快速的。它為基於管理程序的虛擬機提供了一種可行的、經濟有效的替代方案,因此您可以使用更多的計算能力來實現業務目標。Docker 非常適合高密度環境和中小型部署,在這些環境中,您需要用更少的資源做更多的事情。
Docker 使用客戶端-服務器架構。Docker 客戶端與 Docker 守護進程通信,後者負責構建、運行和分發Docker 容器等繁重的工作。Docker 客戶端和守護進程可以運行在同一個系統上,或者您可以將一個 Docker 客戶端連接到一個遠程 Docker 守護進程。Docker 客戶端和守護進程通過 UNIX 套接字或網絡接口使用 REST API 進行通信。
Docker 守護進程(dockerd)偵聽 Docker API 請求並管理 Docker 對象,如鏡像、容器、網絡和卷。
守護進程還可以與其他守護進程通信來管理 Docker 服務。
Docker 客戶端(docker)是許多 Docker 用戶與 Docker 交互的主要方式。當您使用諸如docker run之類的命令時,客戶端將這些命令發送給dockerd, dockerd 會執行這些命令。docker 命令使用 Docker API。Docker 客戶端可以與多個守護進程通信。
Docker 註冊表存儲 Docker 鏡像。
Docker Hub 是一個任何人都可以使用的公共註冊表,默認情況下 Docker 被配置為在 Docker Hub 上尋找鏡像。您甚至可以運行自己的私有註冊表。如果您使用 Docker 數據中心(DDC),它包括 Docker 可信註冊表(DTR)。
當您使用 docker pull 或 docker run 命令時,所需的鏡像將從配置的註冊表中拉取。當您使用 docker push 命令時,您的鏡像將被推送到您配置的註冊表中。
當您使用 Docker 時,您正在創建和使用鏡像、容器、網絡、卷、插件和其他對象。本節簡要介紹其中一些對象。
鏡像是一個只讀模板,帶有創建 Docker 容器的指令。鏡像通常基於另一個鏡像,並進行一些額外的定製。例如,您可以構建基於 ubuntu 鏡像的鏡像,但是安裝了 Apache web server 和您的應用程序,以及運行應用程序所需的配置細節。
您可以創建自己的鏡像,也可以只使用其他人創建併發布在註冊表中的鏡像。要構建自己的鏡像,需要創建一個 Dockerfile,其中包含一個簡單的語法,用於定義創建鏡像並運行它所需的步驟。Dockerfile 中的每條指令都會在鏡像中創建一個層。當你改變 Dockerfile 並重建鏡像時,只有那些已經改變的層才會重建。這是使鏡像與其他虛擬化技術相比如此輕量級、小巧和快速的原因之一。
容器是鏡像的可運行實例。您可以使用 Docker API 或 CLI 創建、啟動、停止、移動或刪除容器。您可以將一個容器連接到一個或多個網絡,將存儲附加到該容器,甚至基於其當前狀態創建一個新鏡像。
默認情況下,容器與其他容器及其主機相對隔離良好。您可以控制容器的網絡、存儲或其他底層子系統與其他容器或主機的隔離程度。
容器是由它的鏡像以及創建或啟動它時提供給它的任何配置選項定義的。當刪除容器時,對其狀態的任何未存儲在持久存儲中的更改都會消失。
docker run 命令示例下面的命令運行一個 ubuntu 容器,以交互方式連接到本地命令行會話,並運行 /bin/bash。
$ docker run -i -t ubuntu /bin/bash
當你運行這個命令時,會發生以下情況(假設你使用默認的註冊表配置):
ubuntu 鏡像,Docker會從你配置的註冊表中拉取它,就像你已經手動運行 docker pull ubuntu 一樣。docker container create 命令一樣。/bin/bash。由於容器以交互方式運行並連接到你的終端(由於有-i和-t標誌),所以可以將輸出記錄到終端,同時你可以使用鍵盤提供輸入。exit 終止 /bin/bash 命令時,容器將停止,但不會被刪除。您可以重新啟動或刪除它。服務允許您跨多個 Docker 守護進程擴展容器,這些守護進程組成一個集群,多個管理者和工作者一起工作。一個集群的每個成員都是一個 Docker 守護進程,所有的守護進程都使用 Docker API 進行通信。服務允許您定義所需的狀態,例如在任何給定時間必須可用的服務副本的數量。默認情況下,服務在所有工作節點之間進行負載均衡。對於消費者來說,Docker 服務看起來像一個單獨的應用程序。Docker 引擎在 Docker 1.12 及更高的版本支持集群模式。
Docker 是用 Go 編寫的,並利用 Linux 內核的幾個特性來實現其功能。
Docker 使用名為命名空間的技術來提供稱為容器的隔離工作區。當您運行一個容器時,Docker 為該容器創建一組命名空間。
這些命名空間提供了一個隔離層。容器的每個方面都在一個單獨的命名空間中運行,其訪問權限僅限於該命名空間。
Docker 引擎在 Linux 上使用如下命名空間:
pid 命名空間: 進程隔離 (PID: 進程ID)。net 命名空間: 管理網絡接口 (NET: Networking)。ipc 命名空間: 管理對 IPC 資源的訪問 (IPC: 進程間通信)。mnt 命名空間: 管理文件系統掛載點 (MNT: Mount)。uts 命名空間: 隔離內核標識符和版本標識符 (UTS: Unix分時系統)。Linux 上的 Docker 引擎還依賴於另一種稱為控制組(cgroups)的技術。cgroup 將應用程序限製為特定的資源集。控制組允許 Docker 引擎將可用的硬件資源共享給容器,並可以選擇強制限制和約束。例如,可以限制特定容器的可用內存。
聯合文件系統,或 UnionFS,是通過創建層來操作的文件系統,使其非常輕便和快速。Docker 引擎使用 UnionFS 為容器提供構建塊。Docker 引擎可以使用多種 UnionFS 變體,包括 AUFS、btrfs、vfs 和 DeviceMapper。
Docker 引擎將命名空間、控制組和 UnionFS 組合到一個稱為容器格式的包裝器中。默認的容器格式是 libcontainer。未來,Docker 可能會通過與 BSD Jails 或 Solaris Zones 等技術集成來支持其他容器格式。
作者 : Docker 官網
譯者 : 技術譯民
出品 : 技術譯站
鏈接 : 英文原文
公眾號:技術譯站
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※教你寫出一流的銷售文案?
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※回頭車貨運收費標準
※別再煩惱如何寫文案,掌握八大原則!
※超省錢租車方案
C++和C#的前自增++n和后自增n++,都是先自增后取值和先取值后自增的含義,但在複雜一點的賦值語句中,我發現細節上有很大的差異。
發現這個問題主要是一個無聊的晚上,我想搞清楚后自增是什麼時候結算,自己搗鼓了一下之後我把我自己的測試結果告訴了朋友,結果學java的朋友和我爭論了半天,最後發現同樣的代碼,大家輸出是不一樣的。之後我又用了C#寫了同樣的測試代碼,發現輸出和他java是一樣的,這讓我豁然開朗,遂寫下這篇博客希望分享我的結論,如果有錯誤的話,歡迎評論區指正。
C++:i++遇到順序點(逗號,分號)之後i才自增,即我一直以來認為的一條語句結束后結算。但++i前自增的值會影響賦值語句所有i的值。
C#:C#不允許使用‘,’逗號運算符,但也並非到’;’分號才結算,賦值語句是從左到右,按序取值,i++在取完i的值之後馬上就自增了,但不影響之前取的i的值,隻影響後續i的取值。
心得:實際開發還是盡量自增單行寫,第一個是可讀性好,第二個是不容易出問題。(應該也就只有筆試會出現以下實驗的代碼了)
最簡單常見的版本:
int arr[] = { 0,0,0 };
int n = 0;
arr[n] = n++;//語句a
for (int num : arr)
{
cout << num << ' ';
}
//輸出是0 0 0,語句a結束后n自增到1
上面這段代碼其實在C#也是一樣的輸出,但如果下面這樣寫,結果就不一樣了。
C++版:
int arr[] = { 0,0,0 };
int n = 0;
arr[n++] = n;//語句a
for (int num : arr)
{
cout << num << ' ';
}
//輸出是0 0 0,語句a結束后n自增到1,C++結果和上面的一樣
C#版:
int[] arr = { 0, 0, 0 };
int n = 0;
arr[n++] = n;//語句a
foreach (int num in arr)
{
Console.Write(num + " ");
}
//輸出是1 0 0,現在應該能理解結論了,從左到右,先取n作為索引值,然後自增之後影響到了等號右邊的n的值
//語句a則為arr[0] = 1;
就是這樣的差異開始引申出了問題,也是讓我迷惑的開始,接下來我會通過更複雜的例子和反彙編的指令來解釋和證明我的結論,先列舉C++的部分,之後再C#,感興趣的可以往下看。
C++部分:
//代碼01
int arr[] = { 0,0,0 };
int n = 0;
arr[n++] = n + (n++) + ++n;
for (int num : arr)
{
cout << num << ' ';
}
//輸出為0 3 0,具體操作流程為語句先結算了++n,使得n自增到1.而出現的兩個n++都在賦值語句結束后結算
//所以編譯結果為arr[1] = 1 + 1 + 1; 然後i自增兩次到3
以下為反彙編的結果:
一開始看到這個我有點懵逼,但是通過比對下面這段代碼的反彙編指令,就能看出來了。
//代碼02
int[] arr = { 0, 0, 0 };
int n = 0;
arr[++n] = n + (n++) + ++n;//把索引處的n++改為了++n
foreach (int num in arr)
{
Console.Write(num + " ");
}
//輸出為0 0 6,具體操作流程為語句先結算兩次++n,使得n自增到2.而n++在賦值語句結束后結算
//所以編譯結果為arr[2] = 2 + 2 + 2; 然後i自增到3
//這下應該發現n在這條語句中取值的時候都是同樣的值了
以下為上面代碼02的反彙編結果:
通過和上面的指令比對可以看出來了,具體差異在00251F3A這段指令,arr[++n]這段代碼02在累加前多進行了一次對n的自增,然後將自增后的值賦給了n,然後開始進行累加,而上面arr[n++]那段代碼01是在累加結束之後,在01161F4A那條指令對n++進行自增的結算,所以就算看不懂全部的指令,也應該能通過比對這兩段代碼看出他們的差異,從而證明了C++對於后自增的處理,是在語句結束之後結算。
說到語句結束,我之前寫了一篇關於逗號運算符的博客,可以結合今天這個結論看看下面這段代碼的輸出結果。
int arr[] = { 0,0,0 };
int n = 0;
arr[n] = (n++, n + (n++) + ++n);//在逗號左邊添加了n++的語句
for (int num : arr)
{
cout << num << ' ';
}
//輸出為0 0 6
//原因為逗號也屬於一條語句結束的標誌,所以結算了n++,n=1,然後執行新的語句n + (n++) + ++n
//逗號右邊的語句先結算了++n,n=2,所以最後賦值語句為arr[2] = 2 + 2 + 2; 然後n++到3
至此C++的部分結束。
接下來C#的測試結果將顛覆上面的結論,如果不用java或者C#的話,下面的內容可能沒有用(朋友java的輸出結果和我C#一樣,不過也是希望大家自己試試,我自己沒有試過)
C#部分:
//代碼01 C#版
int[] arr = { 0, 0, 0 };
int n = 0;
arr[n++] = n + (n++) + ++n;
foreach (int num in arr)
{
Console.Write(num + " ");
}
//輸出為5 0 0 和VC++完全不一樣對吧
//具體原因為C#的賦值語句是從左到右,先取了n的值作為索引,然後馬上對n自增,n=1
//來到等號右邊,第一個n取值為1,第二個n取值為1,然後自增到2,第三個n先自增到3,然後取值為3
//所以最終編譯結果為arr[0] = 1 + 1 + 3; 即5 0 0的原因
//為了節省篇幅,我直接告訴你arr[++n] = n + (n++) + ++n的結果為0 5 0,如果你上面看懂了這個應該也懂
貼一下C#這段代碼的反彙編結果:
可以看到第一行是取n的值,然後把n作為索引值,之後inc指令對n自增1。證明了上面的結論。
即:C#的賦值語句為從左到右,按序取值,n++在取完n的值之後馬上自增,但不影響之前取的值,隻影響後續n的取值。
所以++n 先自增后取值和n++ 先取值后自增是能直接套在C#上的,而對於VC++來說,后自增的結算是非常“緩慢”的,到語句結束才結算。
感謝您的觀看。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※超省錢租車方案
※別再煩惱如何寫文案,掌握八大原則!
※回頭車貨運收費標準
※教你寫出一流的銷售文案?
特斯拉(Tesla)明星執行長 Elon Musk 爆料,德國豪華車品牌 BMW 與特斯拉曾洽商合作,範圍涵蓋電池與輕量化車體零件。 Musk 在接受德國媒體 Der Spiegel 專訪時表示,對 BMW 以碳纖維材料強化車體感到非常有興趣,認為相當具成本效益,雙方在非正式場合會面時,有討論過合作的可能,但尚未達成決議。 據路透社報導,BMW 為發展碳纖維原料而成立合資公司 SGL,i3 電動車與 i8 油電混合動力跑車的駕駛艙與後蓋零件均採用到碳纖維材料。除此之外,Musk 還透露有意與 BMW 一起搞電池科技,以及電動車充電站,他甚至於計畫 5~6 年內在德國蓋一座電池廠。
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※南投搬家公司費用需注意的眉眉角角,別等搬了再說!
※新北清潔公司,居家、辦公、裝潢細清專業服務
※教你寫出一流的銷售文案?
時間:2015年5月16-18日
地點:廣州琶洲保利世貿博覽館
主辦單位:廣州中汽國際展覽有限公司
展會介紹
在能源匱乏的時代,綠色、節能、環保成為經濟發展的核心主題,新能源汽車具有良好的環保性能和燃料經濟性好、運行成本低等優勢,既可以保護環境,又可以緩解能源的短缺並能調整能源的結構,保障能源的安全。
21世紀是一個面臨能源和環境巨大挑戰的世紀,傳統燃油汽車將向高效低排放的電動汽車及混合動力車方向發展。大力發展新能源汽車是能源與環境的必然要求,而且,中國發展新能源汽車的壓力更為緊迫。根據國家《汽車與新能源汽車產業發展規劃》(2011-2020),將在“十二五”期間重點發展清潔能源汽車,未來十年僅中央財政就投入上千億元用來支持以純電動車、混合動力汽車為代表的節能與新能源汽車的研發與推廣。2014年多地出臺補貼政策,2014年5月24日上午,國家主席習近平在上海汽車集團考察時強調,發展新能源汽車是我國從汽車大國邁向汽車強國的必由之路,要加大研發力度,認真研究市場,用好用活政策,開發適應各種需求的產品,使之成為一個強勁的增長點。可以預見,未來我國新能源汽車將會快速發展。
廣州是廣東省會,改革開放前沿城市,中國對外貿易的重要視窗,經濟實力雄厚,市場潛力巨大。廣東是泛珠三角經濟區域的中心,毗鄰港、澳、台,輻射東南亞,海、陸、空交通便利,市場輻射面廣,經濟發達。隨著CEPA的實施,粵、港、澳以及泛珠三角(9+2)區域合作與發展的良性互動,必將給新能源汽車及配套設施產業創造無限美好的發展前景。為順應高速發展的新能源汽車及配套設施行業,廣州中汽國際展覽有限公司聯合行業權威機構定于2015年6月8-10日在廣州琶洲保利世貿博覽館舉辦“2015第三屆廣州國際新能源汽車工業展覽會”(NEA CHINA 2015),展會將深化活動內涵,秉乘推動行業發展、為企業服務的宗旨,為商家提供一個拓展業務、技術交流、展示實力、獲取資訊、結交客戶、推廣新產品、尋找合作夥伴的國際商貿平臺。
我們將以“突出品牌、開拓創新、注重實效、強化服務”的辦展宗旨,憑藉獨特的創意,科學的組織管理和卓越的服務,以全新的理念為廣大中外參展商提供一個“高水準、高品味、高品質”的展示交流平臺,為全球新能源汽車及配套設施行業提供更多的合作機會,有力推動中國新能源汽車及配套設施產品全面進入全球採購體系,與世界各國新能源汽車及配套設施產業協調合作、互利共贏、共同發展進步。
展品範圍
目標觀眾
主辦單位將重點邀請的目標觀眾包括:
1、商務部、發改委、科技部、工信部、國家環保局等各局、司、中心、所領導;
2、全國各省市主管部門領導、大型企事業、機關單位領導;
3、全國各高校、科研單位、設計院、研究院、協(學)會領導;
4、公交、出租、環衛、郵政等單位負責人;車站、機場、碼頭、房地產、大型物業公司、高爾夫球場、旅遊景點、公園、體育場館、大專院校、醫院、療養院、度假村等單位負責人;
5、國內外著名生產、代理、經銷商、貿易公司等業內人士參觀、參展、技術交流。
展會日程
報到布展:2015年5月14-15日
展出時間:2015年5月16-18日
撤展時間:2015年5月18日下午
歡迎業界同仁踴躍報名參展,現正接受申請,請速與組委會聯繫,索取參展申請表及展位平面圖!
充分利用NEA CHINA 2015,鞏固您的市場地位!
Official website/大會官方網站:
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※新北清潔公司,居家、辦公、裝潢細清專業服務
※別再煩惱如何寫文案,掌握八大原則!
※教你寫出一流的銷售文案?
※超省錢租車方案