JDK併發包使用
JDK併發包簡單使用
JUC
在 Java 5.0 提供了 java.util.concurrent(JUC)併發包,提供併發程式設計中很常用的工具類。
變數與執行緒安全
1、volatile
修飾成員變數
Java語言提供了一種稍弱的同步機制,即volatile變數,用來確保將變數的更新操作通知到其他執行緒;
保證此變數對所有的執行緒的可見性。
2、執行緒容器ThreadLocal
當前執行緒向容器中設定的值,只有當前執行緒獲取。其它執行緒無法獲取,避免了執行緒訪問資料的安全問題。
1)簡介
Java中的ThreadLocal類可以讓你建立的變數只被同一個執行緒進行讀和寫操作。因此,儘管有兩個執行緒同時執行一段相同的程式碼,而且這段程式碼又有一個指向同一個ThreadLocal變數的引用,但是這兩個執行緒依然不能看到彼此的ThreadLocal變數域。
2)程式碼示例
併發容器
(1)Hashtable 效率低
Map<String, String> hashtable = new Hashtable<>();
(2)synchronizedMap
Map<String, String> synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>());
(3)ConcurrentHashMap
Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();
示例程式碼:
public class DefaultTokenManager implements TokenManager { private static Map<String, String> tokenMap = new ConcurrentHashMap<>(); @Override public String createToken(String username) { String token = CodecUtil.createUUID(); tokenMap.put(token, username); return token; } @Override public boolean checkToken(String token) { return !StringUtil.isEmpty(token) && tokenMap.containsKey(token); } }
併發佇列
安全佇列
佇列容器,存放資料,當佇列中資料被取完時,取資料操作執行緒被堵塞;當佇列中資料被加滿是,加資料操作執行緒被堵塞
BlockingQueue介面多個實現類,一下為其中三個實現類
(1)ArrayBlockingQueue是一個有邊界的阻塞佇列,它的內部實現是一個數組。有邊界的意思是它的容量是有限的,我們必須在其初始化的時候指定它的容量大小,容量大小一旦指定就不可改變。
BlockingQueue queue = new ArrayBlockingQueue(1024);
queue.put("1");
Object object = queue.take();
(2)LinkedBlockingQueue阻塞佇列大小的配置是可選的,如果我們初始化時指定一個大小,它就是有邊界的,如果不指定,它就是無邊界的。說是無邊界,其實是採用了預設大小為Integer.MAX_VALUE的容量 。它的內部實現是一個連結串列。和ArrayBlockingQueue一樣,LinkedBlockingQueue 也是以先進先出的方式儲存資料。
BlockingQueue<String> unbounded = new LinkedBlockingQueue<String>();
BlockingQueue<String> bounded = new LinkedBlockingQueue<String>(1024);
bounded.put("Value");
String value = bounded.take();
(3)SynchronousQueue佇列內部僅允許容納一個元素。當一個執行緒插入一個元素後會被阻塞,除非這個元素被另一個執行緒消費。
併發工具類
CountDownLatch、Semaphore、ReentrantReadWriteLock、ReentrantLock
(1)CountDownLatch
用於監聽某些初始化操作,等初始化操作完畢後,通知主執行緒繼續執行
final CountDownLatch countDown = new CountDownLatch(2);
countDown.await(); //執行緒阻塞
countDown.countDown(); //啟用一次
countDown.countDown(); //啟用二次
(2)Semaphore
Semaphore是計數訊號量,經常用於限制獲取某種資源的執行緒數量
final Semaphore semp = new Semaphore(3);
semp.acquire(); //獲取許可
//業務程式碼 //這裡面只允許3個執行緒來執行
semp.release(); //釋放許可
(3)讀寫鎖執行緒安全ReentrantReadWriteLock
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final ReadLock readLock = rwLock.readLock(); //獲取讀鎖
private final WriteLock wirteLock = rwLock.writeLock(); //獲取寫鎖
readLock.lock();
readLock.unlock();
讀讀共享,寫寫互斥,讀寫互斥
讀寫鎖:分為讀鎖和寫鎖,多個讀鎖不互斥,讀鎖與寫鎖互斥,這是由jvm自己控制的,你只要上好相應的鎖即可。
- 如果你的程式碼只讀資料,可以很多人同時讀,但不能同時寫,那就上讀鎖;
- 如果你的程式碼修改資料,只能有一個人在寫,且不能同時讀取,那就上寫鎖。
- 總之,讀的時候上讀鎖,寫的時候上寫鎖!
private Object data = null; //共享資料 ,只能有一個執行緒寫該資料,但可以有多個執行緒同時讀
ReadWriteLock rwl = new ReentrantReadWriteLock(); //讀寫鎖
rwl.readLock().lock();//上讀鎖 可以有多個執行緒同時讀 不管是否異常做釋放鎖操作
rw1.writeLock().lock();//新增寫鎖,保證只能有一個執行緒進行寫操作 不管是否異常做釋放鎖操作
(4)執行緒安全ReentrantLock
定義鎖:private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
使用:
lock.lock();加鎖
c1.await(); //執行緒阻塞 等待,c1.signal(); //發訊號解除阻塞
lock.unlock();釋放鎖
執行緒池
實現執行緒池建立執行緒比手動new Thread()好,效率高,統一管理
(1)Java通過Executors提供四種執行緒池:
①newCachedThreadPool建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。
②newFixedThreadPool 建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。Runtime.getRuntime().availableProcessors()
③newScheduledThreadPool 建立一個定長執行緒池,支援定時及週期性任務執行。
④newSingleThreadExecutor 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。
(2)簡單示例
cachedThreadPool.execute(new Runnable(){...}) //需要傳入實現Runnable介面的的業務類
scheduledThreadPool.schedule(new Runnable(){...},3, TimeUnit.SECONDS) //延遲3秒執行
scheduledThreadPool.schedule(new Runnable(){...},1,3, TimeUnit.SECONDS) //延遲1秒後,每隔3秒執行一次 定時週期執行任務 比Timeer
FutureTask future = new FutureTask(new UserFuture()); //構造FutureTask,傳入業務程式碼執行的物件,該類需要實現Callable介面
ExecutorService executor = Executors.newFixedThreadPool(1); //定義執行緒池,裡面可開闢兩個執行緒
Future f = executor.submit(future); //單獨啟動一個執行緒(執行緒池)去執行 不影響下面程式碼執行
f.get(); //返回null表示任務執行完成
future.get(); //非同步獲取業務類處理的結果 並阻塞一個執行緒