進程間通訊方式有哪些java
1)管道 管道分爲有名管道和無名管道 無名管道是一種半雙工的通訊方式,數據只能單向流動,並且只能在具備親緣關係的進程間使用.進程的親緣關係通常指的是父子關係。無明管道通常用於兩個不一樣進程之間的通訊。當一個進程建立了一個管道,並調用fork建立本身的一個子進程後,父進程關閉讀管道端,子進程關閉寫管道端,這樣提供了兩個進程之間數據流動的一種方式。 有名管道也是一種半雙工的通訊方式,可是它容許無親緣關係進程間的通訊。 2)信號量 信號量是一個計數器,能夠用來控制多個線程對共享資源的訪問.,它不是用於交換大批數據,而用於多線程之間的同步.它常做爲一種鎖機制,防止某進程在訪問資源時其它進程也訪問該資源.所以,主要做爲進程間以及同一個進程內不一樣線程之間的同步手段. 3)信號 信號是一種比較複雜的通訊方式,用於通知接收進程某個事件已經發生. 4)消息隊列 消息隊列是消息的鏈表,存放在內核中並由消息隊列標識符標識.消息隊列克服了信號傳遞信息少,管道只能承載無格式字節流以及緩衝區大小受限等特色.消息隊列是UNIX下不一樣進程之間可實現共享資源的一種機制,UNIX容許不一樣進程將格式化的數據流以消息隊列形式發送給任意進程.對消息隊列具備操做權限的進程均可以使用msget完成對消息隊列的操做控制.經過使用消息類型,進程能夠按任何順序讀信息,或爲消息安排優先級順序. 5)共享內存 共享內存就是映射一段能被其餘進程所訪問的內存,這段共享內存由一個進程建立,但多個進程均可以訪問.共享內存是最快的IPC(進程間通訊)方式,它是針對其它進程間通訊方式運行效率低而專門設計的.它每每與其餘通訊機制,如信號量,配合使用,來實現進程間的同步與通訊. 6)套接字:可用於不一樣及其間的進程通訊
每次執行任務建立線程 new Thread()比較消耗性能,建立一個線程是比較耗時、耗資源的。 調用 new Thread()建立的線程缺少管理,被稱爲野線程,並且能夠無限制的建立,線程之間的相互競爭會致使過多佔用系統資源而致使系統癱瘓,還有線程之間的頻繁交替也會消耗不少系統資源。 接使用new Thread() 啓動的線程不利於擴展,好比定時執行、按期執行、定時按期執行、線程中斷等都不便實現。
使用Executor線程池框架的優勢
一、能複用已存在並空閒的線程從而減小線程對象的建立從而減小了消亡線程的開銷。
二、可有效控制最大併發線程數,提升系統資源使用率,同時避免過多資源競爭。
三、框架中已經有定時、按期、單線程、併發數控制等功能。
綜上所述使用線程池框架Executor能更好的管理線程、提供系統資源使用率。數據庫
原子操做(atomic operation)意爲」不可被中斷的一個或一系列操做」 。 處理器使用基於對緩存加鎖或總線加鎖的方式來實現多處理器之間的原子操做。 在Java中能夠經過鎖和循環CAS的方式來實現原子操做。 CAS操做——Compare & Set,或是 Compare & Swap,如今幾乎全部的CPU指令都支持CAS的原子操做。 原子類:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference 原子數組:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray 原子屬性更新器:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater 解決ABA問題的原子類:AtomicMarkableReference(經過引入一個boolean來反映中間有沒有變過),AtomicStampedReference(經過引入一個int來累加來反映中間有沒有變過) ---------------------
Lock接口比同步方法和同步塊提供了更具擴展性的鎖操做。
他們容許更靈活的結構,能夠具備徹底不一樣的性質,而且能夠支持多個相關類的條件對象。
它的優點有:
可使鎖更公平
可使線程在等待鎖的時候響應中斷
可讓線程嘗試獲取鎖,並在沒法獲取鎖的時候當即返回或者等待一段時間
能夠在不一樣的範圍,以不一樣的順序獲取和釋放鎖
總體上來講Lock是synchronized的擴展版,Lock提供了無條件的、可輪詢的(tryLock方法)、定時的(tryLock帶參方法)、可中斷的(lockInterruptibly)、可多條件隊列的(newCondition方法)鎖操做。另外Lock的實現類基本都支持非公平鎖(默認)和公平鎖,synchronized只支持非公平鎖,固然,在大部分狀況下,非公平鎖是高效的選擇。
---------------------
阻塞隊列(BlockingQueue)是一個支持兩個附加操做的隊列。
這兩個附加的操做是:在隊列爲空時,獲取元素的線程會等待隊列變爲非空。當隊列滿時,存儲元素的線程會等待隊列可用。
阻塞隊列經常使用於生產者和消費者的場景,生產者是往隊列裏添加元素的線程,消費者是從隊列裏拿元素的線程。阻塞隊列就是生產者存放元素的容器,而消費者也只從容器裏拿元素。
JDK7提供了7個阻塞隊列。分別是:
ArrayBlockingQueue :一個由數組結構組成的有界阻塞隊列。
LinkedBlockingQueue :一個由鏈表結構組成的有界阻塞隊列。
PriorityBlockingQueue :一個支持優先級排序的無界阻塞隊列。
DelayQueue:一個使用優先級隊列實現的無界阻塞隊列。
SynchronousQueue:一個不存儲元素的阻塞隊列。
LinkedTransferQueue:一個由鏈表結構組成的無界阻塞隊列。
LinkedBlockingDeque:一個由鏈表結構組成的雙向阻塞隊列。
Java 5以前實現同步存取時,可使用普通的一個集合,而後在使用線程的協做和線程同步能夠實現生產者,消費者模式,主要的技術就是用好,wait ,notify,notifyAll,sychronized這些關鍵字。而在java 5以後,可使用阻塞隊列來實現,此方式大大簡少了代碼量,使得多線程編程更加容易,安全方面也有保障。
BlockingQueue接口是Queue的子接口,它的主要用途並非做爲容器,而是做爲線程同步的的工具,所以他具備一個很明顯的特性,當生產者線程試圖向BlockingQueue放入元素時,若是隊列已滿,則線程被阻塞,當消費者線程試圖從中取出一個元素時,若是隊列爲空,則該線程會被阻塞,正是由於它所具備這個特性,因此在程序中多個線程交替向BlockingQueue中放入元素,取出元素,它能夠很好的控制線程之間的通訊。
阻塞隊列使用最經典的場景就是socket客戶端數據的讀取和解析,讀取數據的線程不斷將數據放入隊列,而後解析線程不斷從隊列取數據解析。
線程同步是指線程之間所具備的一種制約關係,一個線程的執行依賴另外一個線程的消息,當它沒有獲得另外一個線程的消息時應等待,直到消息到達時才被喚醒。
線程互斥是指對於共享的進程系統資源,在各單個線程訪問時的排它性。當有若干個線程都要使用某一共享資源時,任什麼時候刻最多隻容許一個線程去使用,其它要使用該資源的線程必須等待,直到佔用資源者釋放該資源。線程互斥能夠當作是一種特殊的線程同步。
線程間的同步方法大致可分爲兩類:用戶模式和內核模式。顧名思義,內核模式就是指利用系統內核對象的單一性來進行同步,使用時須要切換內核態與用戶態,而用戶模式就是不須要切換到內核態,只在用戶態完成操做。
用戶模式下的方法有:原子操做(例如一個單一的全局變量),臨界區。內核模式下的方法有:事件,信號量,互斥量。
使用共享變量的方式
在這種方式中,之因此引入共享變量,是由於該變量能夠被多個執行相同任務的線程用來做爲是否中斷的信號,通知中斷線程的執行。
使用interrupt方法終止線程
若是一個線程因爲等待某些事件的發生而被阻塞,又該怎樣中止該線程呢?
這種狀況常常會發生,好比當一個線程因爲須要等候鍵盤輸入而被阻塞,或者調用Thread.join()方法,或者Thread.sleep()方法,在網絡中調用ServerSocket.accept()方法,或者調用了DatagramSocket.receive()方法時,都有可能致使線程阻塞,使線程處於處於不可運行狀態時,
即便主程序中將該線程的共享變量設置爲true,但該線程此時根本沒法檢查循環標誌,固然也就沒法當即中斷。這裏咱們給出的建議是,不要使用stop()方法,而是使用Thread提供的interrupt()方法,由於該方法雖然不會中斷一個正在運行的線程,可是它可使一個被阻塞的線程拋出一箇中斷異常,從而使線程提早結束阻塞狀態,退出堵塞代碼。
悲觀鎖:老是假設最壞的狀況,每次去拿數據的時候都認爲別人會修改,因此每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖。傳統的關係型數據庫裏邊就用到了不少這種鎖機制,好比行鎖,表鎖等,讀鎖,寫鎖等,都是在作操做以前先上鎖。再好比Java裏面的同步原語synchronized關鍵字的實現也是悲觀鎖。 樂觀鎖:顧名思義,就是很樂觀,每次去拿數據的時候都認爲別人不會修改,因此不會上鎖,可是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可使用版本號等機制。樂觀鎖適用於多讀的應用類型,這樣能夠提升吞吐量,像數據庫提供的相似於write_condition機制,其實都是提供的樂觀鎖。在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現方式CAS實現的。 樂觀鎖的實現方式: 1、使用版本標識來肯定讀到的數據與提交時的數據是否一致。提交後修改版本標識,不一致時能夠採起丟棄和再次嘗試的策略。 2、java中的Compare and Swap即CAS ,當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程並不會被掛起,而是被告知此次競爭中失敗,並能夠再次嘗試。 CAS 操做中包含三個操做數 —— 須要讀寫的內存位置(V)、進行比較的預期原值(A)和擬寫入的新值(B)。若是內存位置V的值與預期原值A相匹配,那麼處理器會自動將該位置值更新爲新值B。不然處理器不作任何操做。 CAS缺點: 1. ABA問題: 好比說一個線程one從內存位置V中取出A,這時候另外一個線程two也從內存中取出A,而且two進行了一些操做變成了B,而後two又將V位置的數據變成A,這時候線程one進行CAS操做發現內存中仍然是A,而後one操做成功。儘管線程one的CAS操做成功,但可能存在潛藏的問題。從Java1.5開始JDK的atomic包裏提供了一個類AtomicStampedReference來解決ABA問題。 2、循環時間長開銷大: 對於資源競爭嚴重(線程衝突嚴重)的狀況,CAS自旋的機率會比較大,從而浪費更多的CPU資源,效率低於synchronized。 3、只能保證一個共享變量的原子操做: 當對一個共享變量執行操做時,咱們可使用循環CAS的方式來保證原子操做,可是對多個共享變量操做時,循環CAS就沒法保證操做的原子性,這個時候就能夠用鎖。
CopyOnWriteArrayList(免鎖容器)的好處之一是當多個迭代器同時遍歷和修改這個列表時,不會拋出ConcurrentModificationException。在CopyOnWriteArrayList中,寫入將致使建立整個底層數組的副本,而源數組將保留在原地,使得複製的數組在被修改時,讀取操做能夠安全地執行。 1、因爲寫操做的時候,須要拷貝數組,會消耗內存,若是原數組的內容比較多的狀況下,可能致使young gc或者full gc; 2、不能用於實時讀的場景,像拷貝數組、新增元素都須要時間,因此調用一個set操做後,讀取到數據可能仍是舊的,雖然CopyOnWriteArrayList 能作到最終一致性,可是仍是無法知足實時性要求; CopyOnWriteArrayList透露的思想 1、讀寫分離,讀和寫分開 2、最終一致性 三、使用另外開闢空間的思路,來解決併發衝突
一個很明顯的緣由是JAVA提供的鎖是對象級的而不是線程級的,每一個對象都有鎖,經過線程得到。因爲wait,notify和notifyAll都是鎖級別的操做,因此把他們定義在Object類中由於鎖屬於對象。
isInterrupted() 僅僅是查詢當前線程的中斷狀態
interrupted() 查詢當前線程的中斷狀態,而且清除原狀態。若是一個線程被中斷了,第一次調用interrupted則返回true,第二次和後面的就返回false了。
若是一個線程處於了阻塞狀態(如線程調用了thread.sleep、thread.join、thread.wait、1.5中的condition.await、以及可中斷的通道上的 I/O 操做方法後可進入阻塞狀態),則在線程在檢查中斷標示時若是發現中斷標示爲true,則會在這些阻塞方法(sleep、join、wait、1.5中的condition.await及可中斷的通道上的 I/O 操做方法)調用處拋出InterruptedException異常,而且在拋出異常後當即將線程的中斷標示位清除,即從新設置爲false。
使用方式:編程
public void run1() { try { ... /* * 無論循環裏是否調用過線程阻塞的方法如sleep、join、wait,這裏仍是須要加上 * !Thread.currentThread().isInterrupted()條件,雖然拋出異常後退出了循環,顯 * 得用阻塞的狀況下是多餘的,但若是調用了阻塞方法但沒有阻塞時,這樣會更安全、更及時。 */ while (!Thread.currentThread().isInterrupted()&& more work to do) { do more work } } catch (InterruptedException e) { //線程在wait或sleep期間被中斷了 } finally { //線程結束前作一些清理工做 } } 或者 public void run2() { while (!Thread.currentThread().isInterrupted()&& more work to do) { try { ... sleep(delay); } catch (InterruptedException e) { Thread.currentThread().interrupt();//從新設置中斷標示 } } }
使當前線程從執行狀態(運行狀態)變爲可執行態(就緒狀態)。
當前線程到了就緒狀態,那麼接下來哪一個線程會從就緒狀態變成執行狀態呢?多是當前線程,也多是其餘線程,看系統的分配了。
當一個線程須要調用對象的wait()方法的時候,這個線程必須擁有該對象的鎖,接着它就會釋放這個對象鎖並進入等待狀態直到其餘線程調用這個對象上的notify()方法。一樣的,當一個線程須要調用對象的notify()方法時,它會釋放這個對象的鎖,以便其餘在等待的線程就能夠獲得這個對象鎖。因爲全部的這些方法都須要線程持有對象的鎖,這樣就只能經過同步來實現,因此他們只能在同步方法或者同步塊中被調用。