夜光序言:
在慾望,性命,血漿裏窺探命運,早晚都會被天道輪迴所反噬。
不是你的東西,終究不是。
正文:
以道御術 / 以術識道
補充:上面兩種方式的組合:將共享數據封裝在另外一個對象中,每個線程對共享數據的操作方法也分配到那個對象身上去完成,對象作爲這個外部類中的成員變量或方法中的局部變量,每個線程的 Runnable 對象
作爲外部類中的成員內部類或局部內部類。
總之,要同步互斥的幾段代碼最好是分別放在幾個獨立的方法中,這些方法再放在同一個類中,這樣比較容 易實現它們之間的同步互斥和通信。
這個需要看下上一章,謝謝
(二)多線程基礎知識--線程併發庫
Java 5 添加了一個新的包到 Java 平臺,java.util.concurrent 包。
這個包包含有一系列能夠讓 Java 的併發編程變得更加簡單輕鬆的類。
在這個包被添加以前,你需要自己去動手實現自己的相關工具類。
下面帶你認識下 java.util.concurrent 包裏的這些類,然後你可以嘗試着如何在項目中使用它們。
本文中將使用 Java 6 版本,我不確定這和 Java 5 版本里的是否有一些差異。我不會去解釋關於 Java 併發的核心問題 – 其背後的原理
( 1 ) Java 的線程併發庫介紹
Java5 的多線程並有兩個大發庫在 java.util.concurrent 包及子包中,子包主要的包有一下兩個
1) java.util.concurrent 包 (多線程併發庫)
➢ java.util.concurrent 包含許多線程安全、測試良好、高性能的併發構建塊。
不客氣地說,創建java.util.concurrent 的目的就是要實現 Collection 框架對數據結構所執行的併發操作。
通過提供一組可靠的、 高性能併發構建塊,開發人員可以提高併發類的線程安全、可伸縮性、性能、可讀性和可靠性,後面、我們會做介紹。
➢如果一些類名看起來相似,可能是因爲 java.util.concurrent 中的許多概念源自 Doug Lea 的 util.concurrent 庫。
2) java.util.concurrent.atomic 包 (多線程的原子性操作提供的工具類)
➢查看 atomic 包文檔頁下面的介紹,它可以對多線程的基本數據、數組中的基本數據和對象中的基本數據進行多線程的操作(AtomicInteger、AtomicIntegerArray、AtomicIntegerFieldUpDater…)
➢通過如下兩個方法快速理解 atomic 包的意義:
AtomicInteger 類的 boolean compareAndSet(expectedValue, updateValue);
AtomicIntegerArray 類的 int addAndGet(int i, int delta);
➢順帶解釋 volatile 類型的作用,需要查看 java 語言規範。
volatile 修飾的變量,線程在每次使用變量的時候,都會讀取變量修改後的最的值。(具有可見性)
volatile 沒有原子性。
3) java.util.concurrent.lock 包 (多線程的鎖機制)
爲鎖和等待條件提供一個框架的接口和類,它不同於內置同步和監視器。該框架允許更靈活地使用鎖和條件。
本包下有三大接口,下面簡單介紹下:
➢Lock 接口:支持那些語義不同(重入、公平等)的鎖規則,可以在非阻塞式結構的上下文(包括 handover-hand 和鎖重排算法)中使用這些規則。
主要的實現是 ReentrantLock。
➢ReadWriteLock 接口:以類似方式定義了一些讀取者可以共享而寫入者獨佔的鎖。
此包只提供了一個實現,ReentrantReadWriteLock,因爲它適用於大部分的標準用法上下文。
但程序員可以創建自己的、 適用於非標準要求的實現。
➢Condition 接口:描述了可能會與鎖有關聯的條件變量。
這些變量在用法上與使用 Object.wait 訪問的隱式監視器類似,但提供了更強大的功能。
需要特別指出的是,單個 Lock 可能與多個 Condition 對象關聯。
爲了避免兼容性問題,Condition 方法的名稱與對應的 Object 版本中的不同。
( 2 ) Java 的併發庫入門
下面我們將分別介紹 java.util.concurrent 包下的常用類的使用。
1) java.util.concurrent 包
java.util.concurrent 包描述:
在併發編程中很常用的實用工具類。
此包包括了幾個小的、已標準化的可擴展框架,以及一些提供有用功能的類。
此包下有一些組件,其中包括:
執行程序(線程池)
併發隊列
同步器
併發 Collocation
下面我們將 java.util.concurrent 包下的組件逐一簡單介紹:
A. 執行程序
➢Executors 線程池工廠類
首次我們來說下線程池的作用:
線程池作用就是限制系統中執行線程的數量。
根據系統的環境情況,可以自動或手動設置線程數量,達到運行的最佳效果;少了浪費了系統資源,多了造成系統擁擠效率不高。
用線程池控制線程數量,其他線程 排隊等候。
一個任務執行完畢,再從隊列的中取最前面的任務開始執行。若隊列中沒有等待進程,線程池的這一資源處於等待。當一個新任務需要運行時,如果線程 池中有等待的工作線程,就可以開始運行了;否則進入等待隊列。
爲什麼要用線程池:
減少了創建和銷燬線程的次數,每個工作線程都可以被重複利用,可執行多個任務
可以根據系統的承受能力,調整線程池中工作線線程的數目,防止因爲因爲消耗過多的內存,而把服務器累趴下(每個線程需要大約 1MB 內存,線程開的越多,消耗的內存也就越大,最後死機)
Executors 詳解:
Java 裏面線程池的頂級接口是 Executor,但是嚴格意義上講 Executor 並不是一個線程池,而只是一個執行線程的工具。
真正的線程池接口是 ExecutorService。ThreadPoolExecutor 是 Executors 類的底層實現。
我們先介紹下 Executors。
線程池的基本思想還是一種對象池的思想,開闢一塊內存空間,裏面存放了衆多(未死亡)的線程,池中線程執行調度由池管理器來處理。
當有線程任務時,從池中取一個,執行完成後線程對象歸池,這樣可以避免反復創建線程對象所帶來的性能開銷,節省了系統的資源。
Java5 中併發庫中,線程池創建線程大致可以分爲下面三種:
//創建固定大小的線程池
ExecutorService fPool = Executors.newFixedThreadPool(3);
//創建緩存大小的線程池
ExecutorService cPool = Executors.newCachedThreadPool();
//創建單一的線程池
ExecutorService sPool = Executors.newSingleThreadExecutor();
下面我們通過簡單示例來分別說明:
固定大小連接池
package com.hy.多線程高併發;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test8 {
public static void main(String[] args) {
//創建一個可重用固定線程數的線程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//創建實現了 Runnable 接口對象,Thread 對象當然也實現了 Runnable 接口
Thread t1 = new MyThread();
Thread t2 = new MyThread();
Thread t3 = new MyThread();
Thread t4 = new MyThread();
Thread t5 = new MyThread();
//將線程放入池中進行執行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
//關閉線程池
pool.shutdown();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在執行。。。");
}
}