多線程
1.區分併發與並行
併發:單核CPU在某一個時間段,不斷的交互運行,宏觀並行,微觀併發。
並行:多核CPU,在同一時刻,同時運行多個應用程序
2.區分進程與線程
進程:系統運行的某一個應用
線程:進程中處理的多個任務,依賴進程存在,一個進程可以有多個線程
3.線程的創建
創建線程的四種方式:繼承Thread類,實現Reunable接口,實現Callable接口,使用線程池Exceutor。
1.實現Runnable接口
實現run方法 Runnable對象:任務對象 創建線程對象:new Thread(任務對象)
2.繼承Thread類
覆蓋run方法
3.創建線程池(避免重複的創建和銷燬線程,可以讓一個線程執行多個任務)
ExecutorService es = Executors.newFixedThreadPool(線程sum);(固定數量線程池)
ExecutorService es = Executors.newCachedThreadPool();(可變線程池)
es.submit(任務對象);
es.shutdown();關閉線程池
4.實現Callable接口(有返回值,能拋異常)
ExecutorService es = Executors.newCachedThreadPool();
實現call方法 Callable對象:任務對象 創建線程對象:es.submit(任務對象);
接收返回值並跑異常:Future f1 = es.submit(任務對象);
4.線程的組成
CPU時間片:操作系統會給每個線程分配執行時間
運行數據:
堆空間(共享):存儲線程需使用的對象,多個線程可以共享堆種的對象
棧空間(獨立):存儲線程需要使用的局部變量,每個線程都擁有獨立的棧
線程的邏輯代碼
5.Thread的基本狀態
初始狀態:線程對象被創建,只在堆中開闢內存
就緒狀態:調用start()之後,進入就緒狀態,等待系統選中,並分配時間片
運行狀態:獲得時間片之後,進入運行狀態,如果時間片到期,回到就緒狀態
終止狀態:主線程main()或獨立線程run()結束,進入終止狀態並釋放擁有的時間片
等待狀態:
有限期等待
1.調用Thread.sleep()進入有限期等待狀態,到期後線程進入就緒狀態,等待系統分配時間片,不會釋放鎖標記
無限期等待
2.調用線程.join()進入無限期等待,
阻塞狀態:線程沒有鎖標記進入阻塞狀態 ,等待鎖標記
6.Thread中的方法
sleep:限時等待 休眠
yield:放棄CPU 回到就緒狀態
setDaemon(true):設置線程爲守護線程,所有非守護線程都結束時,進程就會結束
join():當前線程進入等待狀態,直到線程終止,纔會恢復執行
7.線程安全問題
當多線程共同訪問同一個對象(臨界資源)的時候,如果破壞了不可分割的操作(原子操作),就可能發生數據不一致
每個Java對象都有一個互斥鎖標記,用來分配給線程
synchronized(o) { } 對 o 加鎖的同步代碼塊,只有拿到o的鎖標記的線程,才能進入對o枷鎖的同步代碼塊
synchronized 方法修飾符 對this加鎖的同步代碼塊 只有拿到o的鎖標記的線程,才能調用o的同步方法
8.死鎖問題
死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。
避免死鎖: 加鎖順序(線程按照一定的順序加鎖),鎖時限(線程嘗試獲取鎖的時候加上一定的時限,超過時限則放棄對該鎖的請求,並釋放自己佔有的鎖),死鎖檢測,避免嵌套鎖
9.線程通信(等待通知機制)
object.wait():必須出現在對object加鎖的同步代碼塊 線程會釋放鎖標記,進入等待狀態
object.notify/notifyAll():必須出現在對object加鎖的同步代碼塊,從等待狀態中釋放一個/全部線程
10.Lock接口
創建Lock對象
Lock lock = new ReentrantLock();
上鎖:調用lock.lock()方法
解鎖:調用lock.unlock()方法
獲得隊列對象
Condition c1 = lock.newCondition();
c1.await(): 進入等待隊列
c1.signalAll():喚醒等待的線程
11.jdk1.5線程安全的集合
用ArrayList實現一個高併發讀寫分離的集合
package com.baizhi.controller;
import java.util.ArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Created by 拂曉 on 2019/9/15:18:01
*/
public class MyList extends ArrayList {
//讀寫鎖 寫鎖未分配時,讀鎖可以反覆分配 讀寫分離
ReadWriteLock rwl = new ReentrantReadWriteLock();
Lock r1 = rwl.readLock();//共享鎖
Lock w1 = rwl.writeLock();
@Override
public int size() {
try {
r1.lock();
return super.size();
}finally {
r1.unlock();
}
}
@Override
public Object get(int index) {
try {
r1.lock();
return super.get(index);
}
finally {
r1.unlock();
}
}
@Override
public boolean add(Object o) {
try {
w1.lock();
return super.add(o);
}finally {
w1.unlock();
}
}
@Override
public Object remove(int index) {
try {
w1.lock();
return super.remove(index);
}finally {
w1.unlock();
}
}
@Override
public void clear() {
try {
w1.lock();
super.clear();
}finally {
w1.unlock();
}
}
}
以下線程安全的集合類,都來自Java併發包java.util.concurrent
CopyOnWriteArrayList : 利用複製數組的方式實現數組元素的修改 寫效率低,讀效率高 (適用於讀操作多於寫操作的場景),同樣的還有CopyOnWriteArraySet類
ConcurrentHashMap:實現方式是分段鎖,將散列表分割成16段,每段加鎖,各段互不影響,讀寫效率高
ConcurrentLinkedQueue:線程安全的隊列,用鏈表實現的隊列,實現方式無鎖算法CAS(比較交換算法),效率很高