摘要:一和并發包中的和主要解決的是線程的互斥和同步問題,這兩者的配合使用,相當于的使用。寫鎖與讀鎖之間互斥,一個線程在寫時,不允許讀操作。的注意事項不支持重入,即不可反復獲取同一把鎖。沒有返回值,也就是說無法獲取執行結果。
一、Lock 和 Condition
Java 并發包中的 Lock 和 Condition 主要解決的是線程的互斥和同步問題,這兩者的配合使用,相當于 synchronized、wait()、notify() 的使用。
1. Lock 的優勢比起傳統的 synchronized 關鍵字,Lock 最大的不同(或者說優勢)在于:
阻塞的線程能夠響應中斷,這樣能夠有機會釋放自己持有的鎖,避免死鎖
支持超時,如果線程在一定時間內未獲取到鎖,不是進入阻塞狀態,而是拋出異常
非阻塞的獲取鎖,如果未獲取到鎖,不進入阻塞狀態,而是直接返回
三種情況分別對應 Lock 的三個方法:void lockInterruptibly(),boolean tryLock(long time, TimeUnit unit),boolean tryLock()。
Lock 最常用的一個實現類是 ReentrantLock,代表可重入鎖,意思是可以反復獲取同一把鎖。
除此之外,Lock 的構造方法可以傳入一個 boolean 值,表示是否是公平鎖。
前面實現的簡單的阻塞隊列就是使用 Lock 和 Condition ,現在其含義已經非常明確了:
public class BlockingQueue{ private int capacity; private int size; //定義鎖和條件 private final Lock lock = new ReentrantLock(); private final Condition notFull = lock.newCondition(); private final Condition notEmpty = lock.newCondition(); /** * 入隊列 */ public void enqueue(T data){ lock.lock(); try { //如果隊列滿了,需要等待,直到隊列不滿 while (size >= capacity){ notFull.await(); } //入隊代碼,省略 //入隊之后,通知隊列已經不為空了 notEmpty.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { //在finally塊中釋放鎖,避免死鎖 lock.unlock(); } } /** * 出隊列 */ public T dequeue(){ lock.lock(); try { //如果隊列為空,需要等待,直到隊列不為空 while (size <= 0){ notEmpty.await(); } //出隊代碼,省略 //出隊列之后,通知隊列已經不滿了 notFull.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } //實際應該返回出隊數據 return null; } }
可以看到,Lock 需要手動的加鎖和解鎖,并且解鎖操作是放在 finally 塊中的,這是一種編程范式,盡量遵守。
二、ReadWriteLockReadWriteLock 表示讀寫鎖,適用于讀多寫少的情況,讀寫鎖一般有幾個特征:
讀鎖與讀鎖之間不互斥,即允許多個線程同時讀變量。
寫鎖與讀鎖之間互斥,一個線程在寫時,不允許讀操作。
寫鎖與寫鎖之間互斥,只允許 一個線程寫操作。
讀寫鎖減小了鎖的粒度,在讀多寫少的場景下,對性能的提升較為明顯。ReadWriteLock 的簡單使用示例如下:
public class ReadWriteLockTest { private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock =lock.readLock(); private final Lock writeLock =lock.writeLock(); private int value; //加寫鎖 private void addValue(){ writeLock.lock(); try { value += 1; } finally { writeLock.unlock(); } } //加讀鎖 private int getValue(){ readLock.lock(); try { return value; } finally { readLock.unlock(); } } }
讀寫鎖的升級與降級
Java 中不允許鎖的升級,即加寫鎖時必須釋放讀鎖。
但是允許鎖的降級,即加讀鎖時,可以不釋放寫鎖,最后讀鎖和寫鎖一起釋放。
三、StampedLock 1. StampedLock 的使用及特點StampedLock 是 Java 1.8 版本中提供的鎖,主要支持三種鎖模式:寫鎖、悲觀讀鎖、樂觀讀。
其中寫鎖和悲觀讀鎖跟 ReadWriteLock 中的寫鎖和讀鎖的概念類似。StampedLock 在使用的時候不一樣,加鎖的時候會返回一個參數,解鎖的時候需要傳入這個參數,示例如下:
public class StampedLockTest { private final StampedLock lock = new StampedLock(); private int value; private void addValue(){ long stamp = lock.writeLock(); try { value += 1; } finally { lock.unlockWrite(stamp); } } }
StampedLock 最主要的特點是支持“樂觀讀”,即當進行讀操作的時候,并不是所有的寫操作都被阻塞,允許一個線程獲取寫鎖。樂觀讀的使用示例如下:
public class StampedLockTest { private final StampedLock lock = new StampedLock(); private int value; private void getValue(){ //樂觀讀,讀入變量 long stamp = lock.tryOptimisticRead(); int a = value; //如果驗證失敗 if (!lock.validate(stamp)){ //升級為悲觀讀鎖,繼續讀入變量 stamp = lock.readLock(); try { a = value; } finally { lock.unlockRead(stamp); } } } }
需要注意的是,這里使用 validate() 方法進行驗證,如果樂觀讀失敗,則升級為悲觀讀鎖,繼續獲取變量。
2. StampedLock 的注意事項StampedLock 不支持重入,即不可反復獲取同一把鎖。
在使用 StampedLock 的時候,不要調用中斷操作。如果需要支持中斷,可以調用 readLockInterruptibly 和 writeLockInterruptibly 方法。
四、SemaphoreSemaphore 表示信號量,初始化對象的時候,需要傳一個參數,表示信號量的計數器值。acquire() 方法將計數器加 1,release() 方法減 1,這兩個方法都能夠保證原子性。
信號量的簡單示例:
public class SemaphoreTest { private final Semaphore semaphore = new Semaphore(1); private int value; public void addValue() { try { semaphore.acquire(); value += 1; } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }
程序中使用信號量實現了一個線程安全的方法,初始值設為了 1,當多個方法訪問 addValue 方法的時候,由于 acquire 方法保證原子性,所以只能有一個線程將計數器減 1 并進入臨界區,另一個線程等待。
一個線程執行完后,調用 release 方法,計數器加 1,另一個等待的線程被喚醒。
Semaphore 與 Lock 的一個不同點便是信號量允許多個線程同時進入臨界區,例如將初始值設置的更大一些。例如下面這個例子:
public class SemaphoreTest { //初始值 2,表示 2 個線程可同時進入臨界區 private final Semaphore semaphore = new Semaphore(2); public void test() { try { semaphore.acquire(); System.out.println("線程" + Thread.currentThread().getName() + " 進入臨界區 : " + System.currentTimeMillis()); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } } }五、CountDownLatch
CountDownLatch 是一個線程同步的工具,主要實現一個線程等待多個線程的功能。在原始的 Thread 中,可以調用 join() 方法來等待線程執行完畢,而 CountDownLatch 則可以用在線程池中的線程等待。
下面是 CountDownLatch 的使用示例:
public class CountDownLatchTest { //實際生產中不推薦使用這種創建線程的方式 private final ExecutorService threadPool = Executors.newFixedThreadPool(2); public void test() throws InterruptedException { CountDownLatch latch = new CountDownLatch(2); threadPool.execute(() -> { System.out.println("線程1執行完畢"); latch.countDown(); }); threadPool.execute(() -> { System.out.println("線程2執行完畢"); latch.countDown(); }); latch.await(); System.out.println("兩個線程都執行完畢"); threadPool.shutdown(); } }
CountDownLatch 的初始值為 2,線程執行完畢則調用 countDown 方法,計數器減 1。減到 0 的時候,會喚醒主線程繼續執行。
六、CyclicBarrierCyclicBarrier 也是一個線程同步工具類,主要實現多個線程之間的互相等待。
CyclicBarrier 有兩個構造函數,可以傳一個計數器的初始值,還可以加上一個 Runnable,表示計數器執行減到 0 的時候,需要執行的回調方法。
public class CyclicBarrierTest { private final ExecutorService threadPool = Executors.newFixedThreadPool(2); private final CyclicBarrier barrier = new CyclicBarrier(2, this::note); public void print(){ threadPool.execute(() -> { System.out.println("線程1執行完畢"); try { barrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } }); threadPool.execute(() -> { System.out.println("線程2執行完畢"); try { barrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } }); threadPool.shutdown(); } public void note(){ System.out.println("兩個線程執行完畢"); } }
示例中設置 CyclicBarrier 的初始值為 2,線程執行完畢調用 await 方法,計數器減 1。print() 方法中的兩個線程執行完后,計數器減到 0,就會調用 note 方法。
七、ThreadPoolExecutor 1. 線程池的工作原理由于線程是一種重量級對象,頻繁的創建和銷毀比較消耗系統資源,因此線程池的優勢就顯現出來了。線程池可有降低資源消耗,因為不用頻繁創建和銷毀線程;提高響應速度,需要執行任務時,可直接使用線程池中的線程資源;還能夠有效的管理、監控線程池中的線程。
Java 中的線程池的實現是一種很典型的生產者-消費者模式,使用線程的一方是生產者,主要提供需要執行的任務,線程池是消費者,消費生產者提供的任務。
下面這段代碼能夠幫助理解線程池的實現原理(僅用于幫助理解,實際執行結果有出入):
public class ThreadPool { //保存任務的阻塞隊列 private BlockingQueueworkQueue; //保存工作線程的列表 private List threadList = new ArrayList<>(); //構造方法 public ThreadPool(int poolSize, BlockingQueue workQueue) { this.workQueue = workQueue; //根據poolSize的數量創建工作線程,并執行線程 for (int i = 0; i < poolSize; i++) { WorkThread thread = new WorkThread(); thread.start(); threadList.add(thread); } } //執行任務的方法,主要是將任務添加到隊列中 public void execute(Runnable task) { try { workQueue.put(task); } catch (InterruptedException e) { e.printStackTrace(); } } //工作線程 class WorkThread extends Thread{ @Override public void run() { //循環取出任務執行 while (!workQueue.isEmpty()) { try { Runnable task = workQueue.take(); task.run(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
上面的代碼注釋很詳細了,主要是使用了一個阻塞隊列,用來存儲生產者的任務。然后在構造器中創建線程,并循環從隊列中取出任務執行。
2. Java 中的線程池Java 中提供了 Executors 這個類來快速創建線程池,簡單使用示例如下:
Executors.newSingleThreadExecutor();//創建一個線程的線程池 Executors.newFixedThreadPool(5);//創建固定數量線程 Executors.newCachedThreadPool();//創建可調整數量的線程 Executors.newScheduledThreadPool(5);//創建定時任務線程池
但是在《阿里巴巴Java開發手冊》中,明確禁止使用 Executors 創建線程池(甚至也不建議使用 Thread 顯式創建線程),主要原因是 Executors 的默認方法都是使用的無界隊列,在高負載的情況下,很容易導致 OOM(Out Of Memory)。
所以在 Java 中創建線程池的正確姿勢是使用 ThreadPoolExecutor ,其構造函數有七個:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue, ThreadFactory threadFactory,//可選 RejectedExecutionHandler handler//可選 ) { ...
corePoolSize:線程池中最少的線程數
maximumPoolSize:線程池中創建的最大的線程數
keepAliveTime:表示線程池中線程的活躍時間,如果線程在這個活躍時間內沒有執行任務,并且線程數量超過了 corePoolSize,那么線程池就會回收多余的線程。
TimeUnit:上一個參數的時間單位
workQueue:保存任務的隊列,為了避免 OOM,建議使用有界隊列
threadFactory:可選參數,不傳的話就是默認值。也可以自己傳一個實現了 ThreadFactory 接口的類,表示自定義線程,例如給線程指定名字,線程組等。
handler:可選參數。定義任務的拒絕策略,表示無空閑線程時,并且隊列中的任務滿了的,怎么拒絕新的任務。目前的拒絕策略有四種:
AbortPolicy:默認的拒絕策略,拋出 RejectedExecutionException 異常
CallerRunsPolicy:讓提交任務的線程自己去執行這個任務
DiscardOldestPolicy:丟棄最老的任務,及最先加入隊列中的任務,并添加新的任務
DiscardPolicy:直接丟棄任務,并且不會拋出任何異常
調用 ThreadPoolExecutor
線程池創建好了之后,就需要執行任務,ThreadPoolExecutor 提供了兩個方法,一是 execute,二是 submit。execute 沒有返回值,也就是說無法獲取執行結果。使用示例如下:
public static void main(String[] args) { BlockingQueuequeue = new LinkedBlockingQueue<>(5); ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, queue); threadPool.execute(() -> { System.out.println("In this world"); }); threadPool.shutdown(); }
而 submit 方法有一個 Future 接口的返回值,Future 接口有五個方法:
cancle:取消任務
isCancled:任務是否已取消
isDone:任務是否已執行完
get:獲取任務執行結果
get(long timeout, TimeUnit unit):支持超時獲取任務執行結果
下面代碼展示了取消任務的方法:
public static void main(String[] args) { BlockingQueuequeue = new LinkedBlockingQueue<>(5); ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, queue); Future> future = threadPool.submit(() -> { System.out.println("I am roseduan"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } }); future.cancel(false); threadPool.shutdown(); }
程序的本意是打印語句然后休眠 5 秒,但由于調用了 cancle 方法 ,因此程序直接結束,不會有任何輸出。
八、FutureTaskFutureTask 也是一個支持獲取任務執行結果的工具類,FutureTask 實現了 Runnable 和 Future 接口。
所以可以將 FutureTask 作為任務提交給 ThreadPoolExecutor 或者 Thread 執行,并且可以獲取執行結果。簡單的使用如下:
public static void main(String[] args) throws ExecutionException, InterruptedException { //創建任務 FutureTasktask = new FutureTask<>(() -> "Java and " + "Python"); BlockingQueue queue = new LinkedBlockingQueue<>(5); ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, queue); threadPool.execute(task); //獲取執行結果 System.out.println(task.get()); threadPool.shutdown(); }
傳給 Thread 作為參數的使用示例如下:
public static void main(String[] args) throws ExecutionException, InterruptedException { FutureTask九、CompletableFuturetask = new FutureTask<>(() -> 1 + 2); Thread thread = new Thread(task); thread.start(); System.out.println(task.get());//輸出3 }
CompletableFuture 是一個異步編程的工具類,異步化能夠最大化并行程序的執行,是多線程性能優化的基礎。
1. 創建 CompletableFuture 對象Completable 有四個靜態方法,可以用來創建對象:
runAsync(Runnable runnable);//無返回值 runAsync(Runnable runnable, Executor executor);//無返回值,可指定線程池 supplyAsync(Supplier supplier);//有返回值 supplyAsync(Supplier supplier, Executor executor);//有返回值,可指定線程池
可以看到,四個方法分為了是否有返回值,和是否自定義線程池。如果不自定義線程池,那么 CompletableFuture 會使用公共的線程池,默認創建 CPU 核數的數量的線程池,當有多個任務的時候,還是建議根據每個任務自定義線程池。
一個簡單的使用示例如下,其中 task3 會等待兩個任務都執行完畢:
public static void main(String[] args) { CompletableFuturetask1 = CompletableFuture.runAsync(() -> { System.out.println("任務1執行完畢"); }); CompletableFuture task2 = CompletableFuture.runAsync(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("任務2執行完畢"); }); CompletableFuture task3 = task1.thenCombine(task2, (__, res) -> "兩個任務執行完畢"); System.out.println(task3.join()); }
CompletableFuture 實現了 Future 接口,因此可以查看任務執行的情況,并且可以獲取返回值。
2. CompletionStage 接口中的方法CompletableFuture 還實現了 CompletionStage 接口。這個接口描述了任務之間的時序關系,分別有串行、并行、聚合三種關系。需要注意的是,并行本就是其所具有的特性,所以不再探討了,并且聚合關系又分為了 AND 聚合關系和 OR 聚合關系。下面依次介紹串行、AND 聚合、OR 聚合這三種關系。
首先是串行關系,串行很簡單,一個任務執行完后再執行另一個任務,例如下圖:
描述串行關系的幾個方法是:thenApply、thenAccept、thenRun、thenCompose。
thenApply 既支持接收參數,又能夠支持返回值。
thenAccept 支持接收參數,但是不支持返回值。
thenRun 既不能接收參數,也不能有返回值。
CompletionStage 中的大部分方法都有帶有 Async 后綴的方法,表示可能會使用其他的線程來執行主體中的內容,后面介紹的方法都類似這樣,不再贅述。
簡單的使用示例如下:
public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuturefuture = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("任務1執行完畢"); return "Task1"; }).thenApply((s) -> "接收到的參數 : " + s);; System.out.println(future.get()); }
其次是 AND 匯聚關系,典型的場景便是一個線程等待兩個線程都執行完后再執行,例如下圖:
描述 AND 聚合關系的有三個方法:thenCombine、thenAcceptBoth、runAfterBoth,其是否接收參數和支持返回值,和上面的三個方法對應。一個簡單的使用示例如下:
public static void main(String[] args) { CompletableFuturetask1 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("任務1執行完畢"); return "task1"; }); CompletableFuture task2 = CompletableFuture.supplyAsync(() -> { System.out.println("任務2執行完畢"); return "task2"; }); CompletableFuture task3 = task1.thenCombine(task2, (r,s) -> r + " " + s); System.out.println(task3.join()); }
任務 1 休眠了 2 秒,任務 3 會等待前面兩個任務執行完成之后再執行。
最后是 OR 聚合關系,表示線程等待其中一個線程滿足條件之后,就可以繼續執行了,不用等待全部的線程。
描述 OR 聚合關系的是 applyToEither、acceptEither、runAfterEither。使用示例和上面的類似,只需要將方法改一下就是了,這里不再贅述了。
3. 處理異常在異步編程中,CompletionStage 接口還提供了幾個可以處理異常的方法,和 try() catch() finally() 類似。
這幾個方法分別是 :
exceptionally:相當于 catch
whenComplete:相當于 finally
handle:相當于 finally ,支持返回值
使用示例如下:
public static void main(String[] args) { CompletableFuture十、CompletionServicetask = CompletableFuture.supplyAsync(() -> { String str = null; return str.length(); //相當于catch }).exceptionally((e) -> { System.out.println("發生異常"); return 0; }); //相當于 finally task.whenComplete((s, r) -> { System.out.println("執行結束"); }); System.out.println(task.join()); }
CompletionService 是一個批量執行異步任務的工具類,先來看一個例子:
public static void main(String[] args) throws ExecutionException, InterruptedException { StringBuffer sb = new StringBuffer(); ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 5, 5, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5)); Futuretask1 = threadPool.submit(() -> { Thread.sleep(2000); return "Task1"; }); Future task2 = threadPool.submit(() -> "Task2"); Future task3 = threadPool.submit(() -> "Task3"); sb.append(task1.get()); sb.append(task2.get()); sb.append(task3.get()); }
程序的意思是,依次執行三個任務,并將其結果存儲到 StringBuffer 中,由于 task1 休眠了 2 秒,所以 sb 會在這里阻塞。
由于這三個任務之間沒有關聯,所以等待的消耗完全是沒必要的,解決的辦法便是利用一個阻塞隊列,先執行完的任務將結果保存在隊列中,sb 從隊列中取出就行了。
CompletionService 實際上就是將線程池和阻塞隊列的功能整合了起來,解決了類似上面的問題。CompletionService 的實現類是 ExecutorCompletionService,這個類有兩個構造方法:
public ExecutorCompletionService(Executor executor) {} public ExecutorCompletionService(Executor executor, BlockingQueue> completionQueue) {}
如果不傳一個阻塞隊列,則會使用默認的無界隊列。
CompletionService 主要有這幾個方法:
submit() 提交任務、take() 從阻塞隊列中獲取執行結果(如果隊列為空,線程阻塞)、poll() 也是從隊列中獲取執行結果(如果隊列為空,則返回 null),另外 poll 還支持超時獲取。
使用 CompletionService 改造后的程序示例如下:
public static void main(String[] args) throws ExecutionException, InterruptedException { StringBuffer sb = new StringBuffer(); ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 5, 5, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5)); CompletionService十一、Fork/Join 1. Fork/Join 使用service = new ExecutorCompletionService<>(threadPool); service.submit(() -> { Thread.sleep(2000); return "Task1"; }); service.submit(() -> "Task2"); service.submit(() -> "Task3"); System.out.println(sb.append(service.take().get()).toString()); System.out.println(sb.append(service.take().get()).toString()); System.out.println(sb.append(service.take().get()).toString()); }
Fork/Join 是一個處理分治任務的計算框架,所謂分治,即分而治之,將一個任務分解成子任務,求解子任務,然后將子任務的結果合并,就得到了最后的結果。分治思想的應用十分的廣泛,例如常見的快速排序、歸并排序,還有流行的大數據計算框架 MapReduce,都應用了分治思想。
Java 中,Fork 對應的是 任務分解,Join 則表示 子任務的結果合并。
Fork/Join 主要包含兩個主要的實現類:
一是線程池 ForkJoinPool,默認會創建 CPU核數數量的線程
二是 ForkJoinTask,這是一個抽象類,主要的方法有 fork() 和 join(),前者表示執行子任務,后者表示阻塞等待子任務的執行結果。ForkJoinTask 還有兩個子類:
RecursiveTask
RecursiveAction
這兩個類也是抽象的,我們需要自定義并繼承這個類,并覆蓋其 compute 方法。其中 RecursiveTask 有返回值,而 RecursiveAction 沒有返回值。
下面是一個使用 ForkJoin 的示例,實現了 n 的階乘,注釋寫得比較詳細。
public class ForkJoinTest { public static void main(String[] args) { //創建線程池 ForkJoinPool forkJoinPool = new ForkJoinPool(4); //創建任務 Factorial task = new Factorial(6); //invoke 方法執行任務(還可以使用 execute、submit),得到執行的結果 Integer res = forkJoinPool.invoke(task); System.out.println(res); } static class Factorial extends RecursiveTask2. ForkJoinPool 原理{ private final int n; Factorial(int n) { this.n = n; } @Override protected Integer compute() { if (n == 0){ return 1; } Factorial f = new Factorial(n - 1); //執行子任務 f.fork(); //等待子任務結果 return n * factorial.join(); } } }
和普通的線程池類似,ForkJoinPool 是一個特殊的線程池,并且也采用的是生產者 - 消費者模式。跟普通線程池共享一個隊列不同,ForkJoinPool 其中維護了多個雙端隊列,當一個線程對應的任務隊列為空的時候,線程并不會空閑,而是“竊取”其他隊列的任務執行。
由于是雙端隊列,正常執行任務和“竊取任務”可以從兩端進行出隊,這樣避免了數據競爭。
采用“任務竊取”這種模式,也是 ForkJoinPool 比普通線程池更加智能的體現。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/74698.html
摘要:排序算法和集合工具類排序算法和集合工具類。面試官總是問排序算法也不是在難為你,而是在考察你的編程功底。你首先要理解多線程不僅僅是和那么簡單,整個并發包下面的工具都是在為多線程服務。 去年的這個時候樓主通過兩個月的復習拿到了阿里巴巴的 offer,有一些運氣,也有一些心得,借著跳槽季來臨特此分享出來。簡單梳理一下我的復習思路,同時也希望和大家一起交流討論,一起學習,如果不對之處歡迎指正一...
摘要:語言在之前,提供的唯一的并發原語就是管程,而且之后提供的并發包,也是以管程技術為基礎的。但是管程更容易使用,所以選擇了管程。線程進入條件變量的等待隊列后,是允許其他線程進入管程的。并發編程里兩大核心問題互斥和同步,都可以由管程來幫你解決。 并發編程這個技術領域已經發展了半個世紀了。有沒有一種核心技術可以很方便地解決我們的并發問題呢?這個問題, 我會選擇 Monitor(管程)技術。Ja...
摘要:是企業與區塊鏈相遇的地方。的框架旨在成為開發區塊鏈解決方案的支柱。以太坊,主要是針對工程師使用進行區塊鏈以太坊開發的詳解。 如果你想將區塊鏈合并到一個Java項目中,現在我們來看看就是這個細分領域中三個最大的OSS玩家。 好的伙計們,我們都聽說過比特幣,以太坊或其他加密貨幣,其中有一些時髦的名字圍繞著我們常見的新聞,但我們作為Java開發人員知道如何輕松地與這些區塊鏈技術進行交互嗎?以...
摘要:同步容器及其注意事項中的容器主要可以分為四個大類,分別是和,但并不是所有的容器都是線程安全的。并發容器及其注意事項在版本之前所謂的線程安全的容器,主要指的就是同步容器,當然因為所有方法都用來保證互斥,串行度太高了,性能太差了。 Java 并發包有很大一部分內容都是關于并發容器的,因此學習和搞懂這部分的內容很有必要。 Java 1.5 之前提供的同步容器雖然也能保證線程安全,但是性能很差...
閱讀 1590·2021-09-26 09:46
閱讀 2675·2021-09-07 09:59
閱讀 2762·2021-09-07 09:59
閱讀 1889·2019-08-30 14:20
閱讀 939·2019-08-26 13:39
閱讀 3185·2019-08-26 12:24
閱讀 781·2019-08-26 11:55
閱讀 1224·2019-08-23 16:49