摘要:前言上一篇文章請參考貓頭鷹的深夜翻譯核心并發一安全發布發布一個對象是指該對象的引用對當前的域之外也可見比如,從方法中獲取一個引用。任務的功能性接口表示一個沒有返回值的任務表示一個包含返回值的計算。
前言
上一篇文章請參考貓頭鷹的深夜翻譯:核心JAVA并發(一)
安全發布發布一個對象是指該對象的引用對當前的域之外也可見(比如,從getter方法中獲取一個引用)。要確保一個對象被安全的發布(即在初始化完成之后發布),可能需要使用同步。可以通過以下方法實現安全的發布:
靜態初始化方法。只有一個線程能夠初始化靜態變量因為該類的初始化是在一個排它鎖之下完成的。
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關鍵字。讀者線程總是能獲取最近的值,因為寫線程總是在后續的讀取之前進行
class Volatile { private volatile String state; void setState(String state) { this.state = state; } String getState() { return state; } }
Atomics。比如,AtomicInteger將一個值存儲為volatile類型,所以這里和volatile變量的規則相同
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; } }不變的對象
不變對象的一個非常棒的屬性時,他們是現成安全的,所有無需在其上進行同步。是一個對象成為不變對象的要求為:
所有的字段為final類型
所有字段可以是可變對象或不可變對象,但不能越過對象的范圍,從而對象的狀態在構建后不能更改。
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類用來表示一個應用或是一個JVM現場。其代碼通常在某個進程類的上下文中執行。(使用Thread#currentThread來獲取當前線程本身)
線程的狀態和相應的描述:
NEW: 還未啟動
RUNNABLE: 啟動并運行
BLOCKED: 在控制器上等待 - 該線程正視圖獲取鎖并進入關鍵區域
WAITING: 等待另一個線程執行特殊操作(notify/notifyAll,LockSupport#unpark)
TIMED_WAITING: 和WAITING類似,但是有超時設置
TERMINATED: 停止
Thread的方法和相應的描述:
start: 啟動一個Thread實例并且執行run()方法如何處理InterruptedException
join: 阻塞直到線程完成
interrupt: 中斷線程。如果該線程在響應終端的方法中阻塞著,則會在另一個線程中拋出InterruptedException,否則將會被設置為中斷狀態。
stop,suspend,resume,destroy: 這些方法都已經失效了。
如果可能的話,清理資源并終止線程的運行
聲明當前的方法會拋出InterruptedException
如果一個方法并沒有被聲明拋出InterruptedException,應該使用Thread.currentThread().interrupt()將中斷標識回復為true,然后在該層拋出異常。將中斷標識設為true很重要,它使得異常在可以在更高的層次上進行處。
意料之外的異常處理Threads可以設置UncaughtExceptionHandler,它會在程序突然中斷的時候收到通知。
Thread thread = new Thread(runnable); thread.setUncaughtExceptionHandler((failedThread, exception) -> { logger.error("Caught unexpected exception in thread "{}".", failedThread.getName(), exception); }); thread.start();生命力 死鎖
當多個線程在等待彼此釋放持有的資源,從而形成了資源占有和等待的循環時,就產生了死鎖。可能產生死鎖的例子:
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); } } } }
死鎖可能會這樣產生:
一個線程正視圖從第一個賬戶向第二個賬戶轉賬,并且已經獲得了第一個賬戶的鎖
與此同時,另一個線程正視圖從第二個線程像第一個線程轉賬,并且已經獲得了第二個賬戶的鎖
避免死鎖的方法有:
順序加鎖 - 總是按相同的順序獲得鎖
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); } } } }
會超時的鎖 - 不要無限的占有鎖,應當釋放所有的鎖并重新嘗試獲取
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(); } } } } }活鎖和線程饑餓
當所有的線程都在協商對資源的訪問,或是預防死鎖,從而導致沒有一個線程真正在運行時,會產生活鎖。當一個線程長時間占據一個鎖導致別的線程無法進展時,會產生線程饑餓現象。
java.util.concurrent 線程池線程池的核心接口是ExecutorService,java.util.concurrent還提供了一個靜態工廠Executors,它包含創建具有最常見配置的線程池的工廠方法。
工廠方法如下:
newSingleThreadExecutor: 返回一個只有一個線程的ExecutorService
newFixedThreadPool: 返回一個具有固定數目線程的ExecutorService
newCachedThreadPool: 返回一個可變大小的線程池ExecutorService
newSingleThreadScheduledExecutor: 返回只有一個線程的ScheduledExecutorService
newScheduledThreadPool: 返回包含一組線程的ScheduledExecutorService
newWorkStealingPool: 返回一個帶有并行級別的ExecutorService
當調整線程池大小時,最好基于機器運行該應用時分配的邏輯內核數。可以通過調用Runtime.getRuntime().availableProcessors()來獲得該值。
線程池的實現類
任務通過ExecutorService#submit,ExecutorService#invokeAll或ExecutorService#invokeAny提交,它們對不同類型的任務有多種重載。
任務的功能性接口:
Runnable: 表示一個沒有返回值的任務Future
Callable: 表示一個包含返回值的計算。它還聲明可以拋出原始異常,所以不需要對檢查異常進行包裝
Future是對所有的異步計算的抽象。它表示這些計算的結果,在某些時候可用。大多數的ExecutorService方法都是用Future作為返回值。它包含檢查當前future的狀態以及阻塞當前讀取操作直至結果可以被讀取等方法。
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實現復制了synchronized關鍵字的功能,同時提供了一些額外的功能,比如獲取當前鎖狀態的信息,非阻塞的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實現),它被定義為一組讀寫鎖,支持多個同步讀者和單一寫者。
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通過一個數值初始化。線程會調用await()方法阻塞自己,等待計數值為0后再繼續運行。其它的線程(或是同一個線程)調用countDown()來減少計數。一旦計數為0后,該倒計時器便不可以重復使用。用來在達到某個條件后,啟動一組未知數量的線程
CompletableFuture
CompletableFuture是異步計算的一個抽象。不同于Future,只能通過阻塞獲取結果,該類鼓勵注冊回調函數來創建一組任務,從而在得到返回值或是出現異常時執行該任務。
想要了解更多開發技術,面試教程以及互聯網公司內推,歡迎關注我的微信公眾號!將會不定期的發放福利哦~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70993.html
摘要:簡介從創建以來,就支持核心的并發概念如線程和鎖。這篇文章會幫助從事多線程編程的開發人員理解核心的并發概念以及如何使用它們。請求操作系統互斥,并讓操作系統調度程序處理線程停放和喚醒。 簡介 從創建以來,JAVA就支持核心的并發概念如線程和鎖。這篇文章會幫助從事多線程編程的JAVA開發人員理解核心的并發概念以及如何使用它們。 (博主將在其中加上自己的理解以及自己想出的例子作為補充) 概念 ...
摘要:有可能一個線程中的動作相對于另一個線程出現亂序。當實際輸出取決于線程交錯的結果時,這種情況被稱為競爭條件。這里的問題在于代碼塊不是原子性的,而且實例的變化對別的線程不可見。這種不能同時在多個線程上執行的部分被稱為關鍵部分。 為什么要額外寫一篇文章來研究volatile呢?是因為這可能是并發中最令人困惑以及最被誤解的結構。我看過不少解釋volatile的博客,但是大多數要么不完整,要么難...
摘要:由于需要跨進程訪問網絡上的高速緩存,因此延遲,故障和對象序列化會導致性能下降。應用程序高速緩存會自動清除條目以保持其內存占用。緩存統計高速緩存統計信息可幫助識別高速緩存的運行狀況并提供有關高速緩存行為和性能的信息。 前言 這篇文章探索了現有的各種JAVA緩存基數,它們對各種場景下提高應用的性能起著重要的作用。 近十年來,信息技術極高的提升了業務流程,它已經成為了全球企業的戰略性方案。它...
摘要:什么是為執行字節碼提供一個運行環境。它的實現主要包含三個部分,描述實現規格的文檔,具體實現和滿足要求的計算機程序以及實例具體執行字節碼。該類先被轉化為一組字節碼并放入文件中。字節碼校驗器通過字節碼校驗器檢查格式并找出非法代碼。 什么是Java Development Kit (JDK)? JDK通常用來開發Java應用和插件。基本上可以認為是一個軟件開發環境。JDK包含Java Run...
閱讀 1512·2021-11-24 09:38
閱讀 3366·2021-11-18 10:02
閱讀 3253·2021-09-22 15:29
閱讀 2937·2021-09-22 15:15
閱讀 1037·2021-09-13 10:25
閱讀 1834·2021-08-17 10:13
閱讀 1971·2021-08-04 11:13
閱讀 1973·2019-08-30 15:54