做者:享學課堂James老師java
轉載請聲明出處!程序員
接着第1, 2篇後,咱們繼續來跟進一下併發編程的其它內容,以下:編程
線程池的核心接口是 ExecutorService
。java.util.concurrent
還提供了一個靜態工廠類 Executors
,其中包含用於建立配置線程池的工廠方法。安全
其實 靜態工廠方法以下bash
注意:數據結構
調整線程池大小時,大小是根據你的計算機中的邏輯核心數而定的。這個大小能夠經過調用
Runtime.getRuntime().availableProcessors()
方法得到該值。多線程
任務隨着 ExecutorService#submit
, ExecutorService#invokeAll
或者提交, ExecutorService#invokeAny
對於不一樣類型的任務具備多個重載。併發
其實 功能接口以下ide
Future
是對於具體的Runnable任務或Callable任務的執行結果進行取消、查詢是否完成、獲取結果。必要時能夠經過get方法獲取執行結果,該方法會阻塞直到任務返回結果。高併發
ExecutorService
使用 Future
做爲返回類型。
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> future = executorService.submit(() -> "結果");
try {
String result = future.get(1L, TimeUnit.SECONDS);
System.out.println("結果爲 '" + 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();
複製代碼
該 java.util.concurrent.locks
軟件包括了常常使用到的 Lock
接口。ReentrantLock
類其實也實現了 synchronized
關鍵字的功能,還提供了其它功能,例如獲取有關鎖的狀態,非阻塞 tryLock()
和可中斷鎖的信息。使用顯式 ReentrantLock
的示例以下:
class JamesCounter {
private final Lock lock = new ReentrantLock();
private int value;
int increment() {
lock.lock();
try {
return ++value;
}
finally {
lock.unlock();
}
}
}
複製代碼
java.util.concurrent.locks
還包含一個 ReadWriteLock
接口( ReentrantReadWriteLock
實現),讀寫鎖,一般容許多個併發讀取,但只容許一個寫入。
class JamesStatistic {
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主要用過計數,好比開項目大會,項目經理在會議室門口,有5個程序員A B C D E(至關於5個線程)分別來會議室開會,項目經理手寫拿了一份會議人員名單,程序員A進入了會議室後,項目經理把A名單打個勾表示來了(至關於建立了線程A),B進會議室後,在名單上把B也打勾(至關於建立了線程B),但請注意,人沒到齊, A,B程序員只能在座位上等待(線程全在等待阻塞中),還不能開會,等5個程序員都到齊了,纔開會(5個線程同時被喚醒,開始工做)。
@SpringBootTest(classes = TripApplication.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class JamesTestInvokeRemote {
private static final int THREADS = 200;//200線程模擬用戶提交併發
RestTemplate rest = new RestTemplate();
private final String url = "http://127.0.0.1:8090/buyTicket?idcard=123456";
private static CountDownLatch cdl = new CountDownLatch(THREADS);//200
@Test
public void TestInvoke() throws InterruptedException {
for (int i = 0; i < THREADS; i++){
new Thread(new TicketRequest()).start();
//模擬5個程序員陸陸續續進門
}
}
public class JamesTicketRequest implements Runnable{
@Override
public void run() {
cdl.countDown();
//項目經理的名單上勾掉一個,其實就是減1
try {
cdl.await();
//全部程序員末到位前,都在椅子上等待(全部線程等待),直到 //cdl.countDown()減爲0時喚醒
}
catch (InterruptedException e) {
e.printStackTrace();
}
//5個線程同時(併發請求)執行業務邏輯`
String str = rest.getForEntity(url, String.class).getBody();
//併發同時請求
System.out.println(str);
}
}
}
複製代碼
集合線程安全的最簡單方法就是使用 Collections#synchronized
鎖定方法。因爲此解決方案在高併發場景下表現不佳,所以 java.util.concurrent
提供了針對併發使用進行了優化的各類數據結構。
隊列實際上是充當了「生產者」和「消費者」之間的管道。換句話來講就是個「先進先出」(FIFO)順序而已。BlockingQueue
接口擴展 Queue
,提供永久阻塞或按指定的時間段進行阻塞的方法,它的等待條件會因另外一個線程的操做而發生改變。
花了幾天時間總算寫完了,但願對你們有幫助。看完這篇,你們至少要對多線程的一些經常使用工具類要有所瞭解。
關注我,還有更多技術乾貨分享~