摘要:前言上一篇文章請參考貓頭鷹的深夜翻譯核心并發(fā)一安全發(fā)布發(fā)布一個對象是指該對象的引用對當前的域之外也可見比如,從方法中獲取一個引用。任務(wù)的功能性接口表示一個沒有返回值的任務(wù)表示一個包含返回值的計算。
前言
上一篇文章請參考貓頭鷹的深夜翻譯:核心JAVA并發(fā)(一)
安全發(fā)布發(fā)布一個對象是指該對象的引用對當前的域之外也可見(比如,從getter方法中獲取一個引用)。要確保一個對象被安全的發(fā)布(即在初始化完成之后發(fā)布),可能需要使用同步。可以通過以下方法實現(xiàn)安全的發(fā)布:
靜態(tài)初始化方法。只有一個線程能夠初始化靜態(tài)變量因為該類的初始化是在一個排它鎖之下完成的。
class StaticInitializer { // Publishing an immutable object without additional initialization public static final Year year = Year.of(2017); public static final Setkeywords; // Using static initializer to construct a complex object static { // Creating mutable set Set keywordsSet = new HashSet<>(); // Initializing state keywordsSet.add("java"); keywordsSet.add("concurrency"); // Making set unmodifiable keywords = Collections.unmodifiableSet(keywordsSet); } }
volatile關(guān)鍵字。讀者線程總是能獲取最近的值,因為寫線程總是在后續(xù)的讀取之前進行
class Volatile { private volatile String state; void setState(String state) { this.state = state; } String getState() { return state; } }
Atomics。比如,AtomicInteger將一個值存儲為volatile類型,所以這里和volatile變量的規(guī)則相同
class Atomics { private final AtomicInteger state = new AtomicInteger(); void initializeState(int state) { this.state.compareAndSet(0, state); } int getState() { return state.get(); } }
Final類型
class Final { private final String state; Final(String state) { this.state = state; } String getState() { return state; } }
確保this引用不會再初始化過程中泄漏
class ThisEscapes { private final String name; ThisEscapes(String name) { Cache.putIntoCache(this); this.name = name; } String getName() { return name; } } class Cache { private static final MapCACHE = new ConcurrentHashMap<>(); static void putIntoCache(ThisEscapes thisEscapes) { // "this" reference escaped before the object is fully constructed. CACHE.putIfAbsent(thisEscapes.getName(), thisEscapes); } }
正確同步的域
class Synchronization { private String state; synchronized String getState() { if (state == null) state = "Initial"; return state; } }不變的對象
不變對象的一個非常棒的屬性時,他們是現(xiàn)成安全的,所有無需在其上進行同步。是一個對象成為不變對象的要求為:
所有的字段為final類型
所有字段可以是可變對象或不可變對象,但不能越過對象的范圍,從而對象的狀態(tài)在構(gòu)建后不能更改。
this引用在初始化期間不會泄露
該類為final類型,所以無法在子類中修改其行為
不變對象的例子:
// Marked as final - subclassing is forbidden public final class Artist { // Immutable object, field is final private final String name; // Collection of immutable objects, field is final private final ListThreads
java.lang.Thread類用來表示一個應(yīng)用或是一個JVM現(xiàn)場。其代碼通常在某個進程類的上下文中執(zhí)行。(使用Thread#currentThread來獲取當前線程本身)
線程的狀態(tài)和相應(yīng)的描述:
NEW: 還未啟動
RUNNABLE: 啟動并運行
BLOCKED: 在控制器上等待 - 該線程正視圖獲取鎖并進入關(guān)鍵區(qū)域
WAITING: 等待另一個線程執(zhí)行特殊操作(notify/notifyAll,LockSupport#unpark)
TIMED_WAITING: 和WAITING類似,但是有超時設(shè)置
TERMINATED: 停止
Thread的方法和相應(yīng)的描述:
start: 啟動一個Thread實例并且執(zhí)行run()方法如何處理InterruptedException
join: 阻塞直到線程完成
interrupt: 中斷線程。如果該線程在響應(yīng)終端的方法中阻塞著,則會在另一個線程中拋出InterruptedException,否則將會被設(shè)置為中斷狀態(tài)。
stop,suspend,resume,destroy: 這些方法都已經(jīng)失效了。
如果可能的話,清理資源并終止線程的運行
聲明當前的方法會拋出InterruptedException
如果一個方法并沒有被聲明拋出InterruptedException,應(yīng)該使用Thread.currentThread().interrupt()將中斷標識回復(fù)為true,然后在該層拋出異常。將中斷標識設(shè)為true很重要,它使得異常在可以在更高的層次上進行處。
意料之外的異常處理Threads可以設(shè)置UncaughtExceptionHandler,它會在程序突然中斷的時候收到通知。
Thread thread = new Thread(runnable); thread.setUncaughtExceptionHandler((failedThread, exception) -> { logger.error("Caught unexpected exception in thread "{}".", failedThread.getName(), exception); }); thread.start();生命力 死鎖
當多個線程在等待彼此釋放持有的資源,從而形成了資源占有和等待的循環(huán)時,就產(chǎn)生了死鎖。可能產(chǎn)生死鎖的例子:
class Account { private long amount; void plus(long amount) { this.amount += amount; } void minus(long amount) { if (this.amount < amount) throw new IllegalArgumentException(); else this.amount -= amount; } static void transferWithDeadlock(long amount, Account first, Account second){ synchronized (first) { synchronized (second) { first.minus(amount); second.plus(amount); } } } }
死鎖可能會這樣產(chǎn)生:
一個線程正視圖從第一個賬戶向第二個賬戶轉(zhuǎn)賬,并且已經(jīng)獲得了第一個賬戶的鎖
與此同時,另一個線程正視圖從第二個線程像第一個線程轉(zhuǎn)賬,并且已經(jīng)獲得了第二個賬戶的鎖
避免死鎖的方法有:
順序加鎖 - 總是按相同的順序獲得鎖
class Account { private long id; private long amount; // Some methods are omitted static void transferWithLockOrdering(long amount, Account first, Account second){ boolean lockOnFirstAccountFirst = first.id < second.id; Account firstLock = lockOnFirstAccountFirst ? first : second; Account secondLock = lockOnFirstAccountFirst ? second : first; synchronized (firstLock) { synchronized (secondLock) { first.minus(amount); second.plus(amount); } } } }
會超時的鎖 - 不要無限的占有鎖,應(yīng)當釋放所有的鎖并重新嘗試獲取
class Account { private long amount; // Some methods are omitted static void transferWithTimeout( long amount, Account first, Account second, int retries, long timeoutMillis ) throws InterruptedException { for (int attempt = 0; attempt < retries; attempt++) { if (first.lock.tryLock(timeoutMillis, TimeUnit.MILLISECONDS)) { try { if (second.lock.tryLock(timeoutMillis, TimeUnit.MILLISECONDS)) { try { first.minus(amount); second.plus(amount); } finally { second.lock.unlock(); } } } finally { first.lock.unlock(); } } } } }活鎖和線程饑餓
當所有的線程都在協(xié)商對資源的訪問,或是預(yù)防死鎖,從而導(dǎo)致沒有一個線程真正在運行時,會產(chǎn)生活鎖。當一個線程長時間占據(jù)一個鎖導(dǎo)致別的線程無法進展時,會產(chǎn)生線程饑餓現(xiàn)象。
java.util.concurrent 線程池線程池的核心接口是ExecutorService,java.util.concurrent還提供了一個靜態(tài)工廠Executors,它包含創(chuàng)建具有最常見配置的線程池的工廠方法。
工廠方法如下:
newSingleThreadExecutor: 返回一個只有一個線程的ExecutorService
newFixedThreadPool: 返回一個具有固定數(shù)目線程的ExecutorService
newCachedThreadPool: 返回一個可變大小的線程池ExecutorService
newSingleThreadScheduledExecutor: 返回只有一個線程的ScheduledExecutorService
newScheduledThreadPool: 返回包含一組線程的ScheduledExecutorService
newWorkStealingPool: 返回一個帶有并行級別的ExecutorService
當調(diào)整線程池大小時,最好基于機器運行該應(yīng)用時分配的邏輯內(nèi)核數(shù)。可以通過調(diào)用Runtime.getRuntime().availableProcessors()來獲得該值。
線程池的實現(xiàn)類
任務(wù)通過ExecutorService#submit,ExecutorService#invokeAll或ExecutorService#invokeAny提交,它們對不同類型的任務(wù)有多種重載。
任務(wù)的功能性接口:
Runnable: 表示一個沒有返回值的任務(wù)Future
Callable: 表示一個包含返回值的計算。它還聲明可以拋出原始異常,所以不需要對檢查異常進行包裝
Future是對所有的異步計算的抽象。它表示這些計算的結(jié)果,在某些時候可用。大多數(shù)的ExecutorService方法都是用Future作為返回值。它包含檢查當前future的狀態(tài)以及阻塞當前讀取操作直至結(jié)果可以被讀取等方法。
ExecutorService executorService = Executors.newSingleThreadExecutor(); FutureLocksfuture = executorService.submit(() -> "result"); try { String result = future.get(1L, TimeUnit.SECONDS); System.out.println("Result is "" + result + ""."); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } catch (TimeoutException e) { throw new RuntimeException(e); } assert future.isDone();
Lock
java.util.concurrent.locks包中有一個標準的Lock接口,ReentrantLock實現(xiàn)復(fù)制了synchronized關(guān)鍵字的功能,同時提供了一些額外的功能,比如獲取當前鎖狀態(tài)的信息,非阻塞的tryBlock()方法,以及可中斷的鎖。下面是使用具體的ReentrantLock實例的例子:
class Counter { private final Lock lock = new ReentrantLock(); private int value; int increment() { lock.lock(); try { return ++value; } finally { lock.unlock(); } } }
ReadWriteLock
java.util.concurrent.locks包還包含了ReadWriteLock接口(以及ReentrantReadWriteLock實現(xiàn)),它被定義為一組讀寫鎖,支持多個同步讀者和單一寫者。
class Statistic { private final ReadWriteLock lock = new ReentrantReadWriteLock(); private int value; void increment() { lock.writeLock().lock(); try { value++; } finally { lock.writeLock().unlock(); } } int current() { lock.readLock().lock(); try { return value; } finally { lock.readLock().unlock(); } } }
CountDownLatch
CountDownLatch通過一個數(shù)值初始化。線程會調(diào)用await()方法阻塞自己,等待計數(shù)值為0后再繼續(xù)運行。其它的線程(或是同一個線程)調(diào)用countDown()來減少計數(shù)。一旦計數(shù)為0后,該倒計時器便不可以重復(fù)使用。用來在達到某個條件后,啟動一組未知數(shù)量的線程
CompletableFuture
CompletableFuture是異步計算的一個抽象。不同于Future,只能通過阻塞獲取結(jié)果,該類鼓勵注冊回調(diào)函數(shù)來創(chuàng)建一組任務(wù),從而在得到返回值或是出現(xiàn)異常時執(zhí)行該任務(wù)。
想要了解更多開發(fā)技術(shù),面試教程以及互聯(lián)網(wǎng)公司內(nèi)推,歡迎關(guān)注我的微信公眾號!將會不定期的發(fā)放福利哦~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/70993.html
摘要:簡介從創(chuàng)建以來,就支持核心的并發(fā)概念如線程和鎖。這篇文章會幫助從事多線程編程的開發(fā)人員理解核心的并發(fā)概念以及如何使用它們。請求操作系統(tǒng)互斥,并讓操作系統(tǒng)調(diào)度程序處理線程停放和喚醒。 簡介 從創(chuàng)建以來,JAVA就支持核心的并發(fā)概念如線程和鎖。這篇文章會幫助從事多線程編程的JAVA開發(fā)人員理解核心的并發(fā)概念以及如何使用它們。 (博主將在其中加上自己的理解以及自己想出的例子作為補充) 概念 ...
摘要:有可能一個線程中的動作相對于另一個線程出現(xiàn)亂序。當實際輸出取決于線程交錯的結(jié)果時,這種情況被稱為競爭條件。這里的問題在于代碼塊不是原子性的,而且實例的變化對別的線程不可見。這種不能同時在多個線程上執(zhí)行的部分被稱為關(guān)鍵部分。 為什么要額外寫一篇文章來研究volatile呢?是因為這可能是并發(fā)中最令人困惑以及最被誤解的結(jié)構(gòu)。我看過不少解釋volatile的博客,但是大多數(shù)要么不完整,要么難...
摘要:由于需要跨進程訪問網(wǎng)絡(luò)上的高速緩存,因此延遲,故障和對象序列化會導(dǎo)致性能下降。應(yīng)用程序高速緩存會自動清除條目以保持其內(nèi)存占用。緩存統(tǒng)計高速緩存統(tǒng)計信息可幫助識別高速緩存的運行狀況并提供有關(guān)高速緩存行為和性能的信息。 前言 這篇文章探索了現(xiàn)有的各種JAVA緩存基數(shù),它們對各種場景下提高應(yīng)用的性能起著重要的作用。 近十年來,信息技術(shù)極高的提升了業(yè)務(wù)流程,它已經(jīng)成為了全球企業(yè)的戰(zhàn)略性方案。它...
摘要:什么是為執(zhí)行字節(jié)碼提供一個運行環(huán)境。它的實現(xiàn)主要包含三個部分,描述實現(xiàn)規(guī)格的文檔,具體實現(xiàn)和滿足要求的計算機程序以及實例具體執(zhí)行字節(jié)碼。該類先被轉(zhuǎn)化為一組字節(jié)碼并放入文件中。字節(jié)碼校驗器通過字節(jié)碼校驗器檢查格式并找出非法代碼。 什么是Java Development Kit (JDK)? JDK通常用來開發(fā)Java應(yīng)用和插件。基本上可以認為是一個軟件開發(fā)環(huán)境。JDK包含Java Run...
閱讀 1528·2021-11-24 09:38
閱讀 3377·2021-11-18 10:02
閱讀 3267·2021-09-22 15:29
閱讀 2952·2021-09-22 15:15
閱讀 1055·2021-09-13 10:25
閱讀 1874·2021-08-17 10:13
閱讀 2005·2021-08-04 11:13
閱讀 1985·2019-08-30 15:54