關於多執行緒的一些瞭解
補充之前的學習筆記
1JUC是什麼
1.1 Java.util.concurrent =JUC
1.2 程序:系統裡執行的多個程式QQ.exe 執行緒:一個程序中有多個執行緒
1.3 執行緒的多種狀態。
.start()--就緒狀態
State:
new建立 runnable啟動 blocked阻塞 waiting等待(不見不散) timed_waiting等待(過時不候) terminated終結
1.4 wait交鎖 sleep不交鎖
1.5 併發:同一時間點多個執行緒訪問同一個資源 並行:同時執行多個資源
2、lambda表示式
①寫法:拷貝中括號+寫死右箭頭+落地大括號
②只有函式介面(接口裡只有一個方法時)才能用lambda表示式
③介面上標記@FunctionalInterface
Foo foo = () -> {業務邏輯程式碼,實現方法}
④default方法的實現
用@FunctionalInterface的介面只能有個一方法,但是可以又多個default方法
⑤靜態方法實現
用@FunctionalInterface的介面只能有個一方法,但是可以又多個default方法,可以有多個靜態方法
3、執行緒間的通訊
3.1生產者+消費者
3.2通知等待喚醒機制
判斷 幹活 通知
3.3 2個執行緒變4個執行緒,禁止出現虛假喚醒,判斷條件用while
3.4
----------------lock-----------------
Lock lock=new ReentrantLock();
Condition condition = lock.newCondition();
condition.awati(); codittion.signalAll();
-------------------syncronised-----------------
| |
wait(); notifiyAll();
4、傳值和傳引用
基本型別傳影印件
引用型別傳引用
String型別比較特殊,因為有個池的概念,所以相當於指向變了,但是原來的指標還是指向原來的引用
5、Callable
callable:有異常、有返回值、call
runnable:無異常、無返回值、run
FutureTask作用:非同步呼叫
自頂向下,逐步求精
一個futuretask被多個執行緒呼叫,結果可以複用
futureTask.get()只允許放到最後,get方法只計算一次
-----------------原理,底層------------------
①在主執行緒中需要執行比較耗時的操作時,但又不想阻塞主執行緒時,可以把這些作業交給Future物件在後臺完成,當主執行緒將來需要時,就可以通過future物件獲得後臺作業的計算結果或者執行狀態。
②一般FutureTask多用於耗時的計算,主執行緒可以在完成自己的任務後,再去獲取結果
③僅再計算完成時才能檢索結果,如果計算尚未完成,則阻塞get方法,一旦計算完成,就不能再重新開始或者取消計算,get方法獲取結果只有再計算完成時獲取,否則會一直阻塞直到任務轉入完成狀態,然後會返回結果或者丟擲異常。
6、ReadWriteLock
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock ();
rwLock .writeLock().lock();
rwLock .readLock().lock();
讀寫鎖案例:讀可共享,寫排他
class ReadWrite{ private Object obj; private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
public void writeLock(Object obj){ rwLock.writeLock().lock(); try { this. obj = obj; System. out.println(Thread. currentThread().getName()+"寫執行緒正在執行\t"+obj); } catch (Exception e) { // TODO: handle exception } finally{ rwLock.writeLock().unlock(); } }
public void readLock(){ rwLock.readLock().lock(); try { System. out.println(Thread. currentThread().getName()+"讀執行緒正在執行\t"+obj); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ rwLock.readLock().unlock(); } }
} |
7、執行緒池
ExecutorService service=Executors.newFixedThreadPool(5);//一池5執行緒(核心執行緒=最大執行緒=5)
-
阻塞佇列採用了LinkedBlockingQueue,它是一個無界佇列;
-
由於阻塞佇列是一個無界佇列,因此永遠不可能拒絕任務;
-
由於採用了無界佇列,實際執行緒數量將永遠維持在nThreads,因此maximumPoolSize和keepAliveTime將無效。
public static ExecutorService newFixedThreadPool( int nThreads) { return new ThreadPoolExecutor( nThreads, nThreads , 0L, TimeUnit. MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } |
ExecutorService service=Executors.newSingleThreadExecutor();//一池1執行緒(核心執行緒=最大執行緒=1)
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService ( new ThreadPoolExecutor(1, 1, 0L, TimeUnit. MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } |
ExecutorService service=Executors.newCachedThreadPool();//一池N執行緒(核心執行緒0,最大執行緒int的最大值相當於沒有上限)
-
它是一個可以無限擴大的執行緒池;
-
它比較適合處理執行時間比較小的任務;
-
corePoolSize為0,maximumPoolSize為無限大,意味著執行緒數量可以無限大;
-
keepAliveTime為60S,意味著執行緒空閒時間超過60S就會被殺死;
-
採用SynchronousQueue裝等待的任務,這個阻塞佇列沒有儲存空間,這意味著只要有請求到來,就必須要找到一條工作執行緒處理他,如果當前沒有空閒的執行緒,那麼就會再建立一條新的執行緒。
public static ExecutorService newCachedThreadPool () { return new ThreadPoolExecutor(0, Integer. MAX_VALUE, 60L, TimeUnit. SECONDS, new SynchronousQueue<Runnable>()); } |
service.submit(Runnable);
service.shutdown();
---------------------------------
ScheduledExecutorService service=Executors.newScheduledThreadPool(5);//時間輪詢,每隔多少時間執行一個任務,如果執行緒忙不過來會自動新加執行緒(核心執行緒5,最大執行緒int最大值相當於沒有上限)
-
它採用DelayQueue儲存等待的任務
-
DelayQueue內部封裝了一個PriorityQueue,它會根據time的先後時間排序,若time相同則根據sequenceNumber排序;
-
DelayQueue也是一個無界佇列;
-
工作執行緒的執行過程:
-
工作執行緒會從DelayQueue取已經到期的任務去執行;
-
執行結束後重新設定任務的到期時間,再次放回DelayQueue
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor( corePoolSize); } public ScheduledThreadPoolExecutor( int corePoolSize) { super(corePoolSize, Integer. MAX_VALUE, 0, TimeUnit. NANOSECONDS, new DelayedWorkQueue()); } public ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors. defaultThreadFactory(), defaultHandler); } |
service.schedule(callable,delay,unit);//執行緒,延遲,時間單位 每隔2s提交一次請求
public class ReadWriteLock { public static <E> void main(String[] args) {
ExecutorService service = Executors. newCachedThreadPool();
final ReadWrite readWrite = new ReadWrite();
try { Thread thread1 = new Thread( new Runnable() { @Override public void run() { readWrite.writeLock( "this is write");
} }, "AA"); service.execute( thread1);
for( int i=1; i<=100; i++){ Thread thread2 = new Thread( new Runnable() { @Override public void run() { readWrite.readLock(); } }, String. valueOf(i)); service.execute( thread2); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ service.shutdown(); } } } |
8、常用工具類
CountDownLatch----所有執行緒執行完才執行的任務(秦滅六國,一統華夏)
final CountDownLatch countDownLatch = new CountDownLatch(6);
countDownLatch .countDown();//沒執行一條就-1,直到6條都執行完
countDownLatch .await();//阻塞最後一個要執行的主執行緒
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException { //CountDownLatch final CountDownLatch countDownLatch = new CountDownLatch(6);
for( int i=1; i<=6; i++){ new Thread( new Runnable() {
@Override public void run() { System. out.println(Thread. currentThread().getName()+"\t 國家被滅"); countDownLatch.countDown(); } }, CountryEnums.forEachCountryEnums(i).getMsg()).start(); }
countDownLatch.await(); System. out.println(Thread. currentThread().getName()+"\t 秦滅六國,一統華夏" ); } } |
CyclicBarrier---集齊7顆龍珠,可以召喚神龍(其他執行緒執行完了只能等待)
public class CyclicBarrierDemo {
private final static int number=7;
public static void main(String[] args) { final CyclicBarrier cyclicBarrier = new CyclicBarrier(number, new Runnable() { @Override public void run() { System. out.println( "集齊7顆龍珠,可以召喚神龍" ); } }); for( int i=1; i<=7; i++){ final int temp= i; new Thread( new Runnable() { @Override public void run() { try { System. out.println(Thread. currentThread().getName()+"\t 收集第"+ temp+ "顆龍珠"); cyclicBarrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } }, String. valueOf(i)).start(); }
} } |
Semaphore----訊號燈(爭車位)
public class SemaphoreDemo { public static void main(String[] args) { final Semaphore semaphore = new Semaphore(3); //模擬3個停車位 for( int i=1; i<=6; i++){ //模擬6個汽車 new Thread( new Runnable() {
@Override public void run() { try { semaphore.acquire(); System. out.println(Thread. currentThread().getName()+ "\t 搶佔到停車位" ); TimeUnit. SECONDS.sleep( new Random().nextInt(5)); System. out.println(Thread. currentThread().getName()+ "\t---- 離開了停車位" ); } catch (InterruptedException e) { e.printStackTrace(); } finally{ semaphore.release(); }
} }, String. valueOf(i)).start(); } } } |
9、集合不安全類
ArrayList、HashMap、HashSet
java.util.ConcurrentModificationException
CopyOnWriteArrayList();//寫時複製
往元素中新增元素時,先複製一份新的陣列(Arrays.copyOf),長度+1,把要新增的元素新增到新的陣列中。最後把引用指向新的陣列。(整個過程添加了reentrainLock)
CopyOnWriteArraySet<String>();
ConcurrentHashMap<>();
sss---->Arrays Collections
三者對比:
1)CountDownLatch和CyclicBarrier都能夠實現執行緒之間的等待,只不過它們側重點不同: CountDownLatch一般用於某個執行緒A等待若干個其他執行緒執行完任務之後,它才執行; 而CyclicBarrier一般用於一組執行緒互相等待至某個狀態,然後這一組執行緒再同時執行; 另外,CountDownLatch是不能夠重用的,而CyclicBarrier是可以重用的。 2)Semaphore其實和鎖有點類似,它一般用於控制對某組資源的訪問許可權。 |
10、volatile
記憶體可見性
public class SafeSingletonDemo { private static volatile SafeSingletonDemo safeSingletonDemo = null; private SafeSingletonDemo(){ System. out.println( "********"+Thread. currentThread().getName()); }
//double check lock public static SafeSingletonDemo getInstance(){ if( null == safeSingletonDemo){ synchronized (SafeSingletonDemo. class) { if( null == safeSingletonDemo){ safeSingletonDemo= new SafeSingletonDemo(); } } } return safeSingletonDemo; }
public static void main(String[] args) { new Thread( new Runnable() {
@Override public void run() { SafeSingletonDemo. getInstance(); } }, "AA").start(); new Thread( new Runnable() {
@Override public void run() { SafeSingletonDemo. getInstance(); } }, "BB").start(); new Thread( new Runnable() {
@Override public void run() { SafeSingletonDemo. getInstance(); } }, "CC").start(); } } |
彩蛋:某次公開課中記錄的筆記
擊穿快取的方法:
口訣:讀多寫少用快取,寫多讀少用佇列,限流、分流
方法1:化併發為同步
lock:等待鎖:粗粒度的鎖
1個執行緒拿到鎖,重建快取,
其他1999個執行緒等待,從redis取
方法2:互斥鎖ConcurrentHashMap<> map 細粒度的鎖
車次號1-->是否有鎖
車次號2-->是否有鎖
boolean lock = false;
lock = map.putIfAbsent(key,value)==null;//代表當前沒有資料,不為null,當前有資料
if(lock){//拿到鎖
//重建快取
//再查一次
}else{//沒拿到鎖
//快取降級
1:提示:當前人數太多,請耐心等待
}