- 線程與進程
- 線程的幾種創(chuàng)建方式
- 線程的優(yōu)先級
- 線程的幾種狀態(tài)與常用方法
- 線程間消息通訊
- 線程安全
線程與進程
- 一個進程至少一個線程
- 進程可以包含多個線程
- 進程在執(zhí)行過程中擁有獨立的內(nèi)存空間作喘,而線程運行在進程內(nèi)
線程的幾種創(chuàng)建方式
-
new Thread:可復寫Thread#run方法。也可傳遞Runnable對象宏所,更加靈活惊搏。
- 缺點:缺乏統(tǒng)一管理溪胶,可能無限制新建線程,相互之間競爭,極可能占用過多系統(tǒng)資源導致死機或oom
// 傳遞Runnable對象
1.new Thread(new Runnable(){
...
}).start()
//復寫Thread#run方法
2.class MyThread extends Thread{
public void run(){
}
}
new MyThread().start()
-
AysncTask:輕量級的異步任務工具類喉镰,提供任務執(zhí)行的進度回調(diào)給UI線程
- 場景:需要知曉任務執(zhí)行的速度乖阵,多個任務串行執(zhí)行
- 缺點:生命周期和宿主的生命周期不同步宣赔,有可能發(fā)生內(nèi)存泄漏,默認情況所有任務串行執(zhí)行
class AsyncTaskTest extends AsyncTask<String,Integer,String> {
private static final String TAG = "AsyncTaskTest";
@Override
protected String doInBackground(String... params) {
for (int i = 0; i < 10; i++) {
publishProgress(i * 10);
}
return params[0];
}
@Override
protected void onPostExecute(String result) {
Log.e(TAG,"result:" + result);
}
@Override
protected void onProgressUpdate(Integer... values) {
Log.e(TAG,"onProgressUpdate:" + values[0].intValue());
}
public static void main(String[] args) {
//1.子類復寫方法
AsyncTaskTest asyncTaskTest = new AsyncTaskTest();
asyncTaskTest.execute("execute asyncTaskTest");
//or
asyncTaskTest.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"execute AsyncTaskTest");
//2.使用execute方法瞪浸,同樣串行執(zhí)行
AsyncTask.execute(new Runnable() {
@Override
public void run() {
.....
}
});
//3.使用內(nèi)置THREAD_POOL_EXECUTOR線程池儒将,并發(fā)執(zhí)行
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
}
});
}
}
-
HandlerThread:適用于主線程需要和工作線程通信,適用于持續(xù)性任務对蒲,比如輪訓的場景钩蚊,所有任務串行執(zhí)行。
- 缺點:不會像普通線程一樣主動銷毀資源蹈矮,會一直運行下去砰逻,所以可能會造成內(nèi)存溢出。
HandlerThread thread = new HandlerThread("concurrent-thread");
thread.start();
ThreadHandler handler = new ThreadHandler(thread.getLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
switch ((msg.what)) {
case MSG_WHAT_FLAG_1:
break;
}
}
};
handler.sendEmptyMessage(MSG_WHAT_FLAG_1);
thread.quitSafely();
//定義成靜態(tài)含滴,防止內(nèi)存泄漏
static class ThreadHandler extends Handler {
public ThreadHandler(Looper looper) {
super(looper);
}
}
- IntentService:適用于我們的任務需要跨頁面讀取任務執(zhí)行的進度诱渤,結(jié)果。比如后臺上傳頁面谈况,批量操作數(shù)據(jù)庫等勺美,任務執(zhí)行完成功后,就會自我結(jié)束碑韵,所以不需要手動stopService赡茸,這是它跟Service的區(qū)分。
class MyIntentService extends IntentService{
@Override
protected void onHandleIntent(@Nullable Intent intent){
int conmmand = intent.getInt("command")
·····
}
}
//啟動IntentService
context.startService(new Intent())
- ThreadPoolExecutor:適用于快速處理大量耗時較短的任務場景
Excutors.newCachedThreadPool();//線程可復用線程池
Excutors.newFixedThreadPool();//固定線程數(shù)量的線程池
Excutors.newScheduledThreadPool();//可指定定時任務的線程池
Excutors.newSingleThreadExecutor();//線程數(shù)量為1的線程池
線程優(yōu)先級
public static void main(String[] args){
Tread thread = new Thread();
thread.start();
int ui_proi = Process.getThreadPriority(0);
int th_proi = thread.getPriority();
//輸出結(jié)果
ui_proi = 5
th_proi = 5
}
- 線程的優(yōu)先級具有繼承性祝闻,在某線程中創(chuàng)建的線程會繼承此線程的優(yōu)先級占卧,那么我們在UI線程中創(chuàng)建了線程遗菠,則線程優(yōu)先級是和UI線程優(yōu)先級一樣,平等的和UI搶占CPU時間片資源华蜒。
- JDK Api辙纬,限制了新設置的線程的優(yōu)先級必須為[1~10],優(yōu)先級priority的值越高,獲取CPU時間片的概率越高叭喜。UI線程優(yōu)先級為5
java.lang.Thread.setPriority(int newPriority)
- Android Api贺拣,可以為線程設置更為精細的優(yōu)先級(-20~19),優(yōu)先級priority的值越低捂蕴,獲取CPU時間片的概率越高譬涡。UI線程優(yōu)先級為-10
android.os.Process.setThreadPriority(int newPriority)
線程的幾種狀態(tài)與常用方法
方法名 | 說明 |
---|---|
NEW | 初始狀態(tài),線程被新建啥辨,還沒調(diào)用start方法 |
RUNNABLE | 運行狀態(tài)涡匀,把“運行中”和“就緒”統(tǒng)稱為運行狀態(tài) |
BLOCKED | 阻塞狀態(tài),表示線程阻塞于鎖 |
WAITING | 等待狀態(tài)溉知,需要其他線程通知喚醒 |
TIME_WAITING | 超時等待狀態(tài)陨瘩,表示可以在指定的時間超時后自行返回 |
TERMINATED | 終止狀態(tài),表示當前線程已經(jīng)執(zhí)行完畢 |
方法名 | 說明 |
---|---|
wait | 進入等待池级乍,釋放資源對象鎖拾酝,可使用notify.notifyAll,或等待超時來喚醒 |
join | 等待目標線程執(zhí)行完成后再執(zhí)行此線程 |
yield | 暫停當前正在執(zhí)行的線程對象,不會釋放資源鎖卡者,使同優(yōu)先級或更高優(yōu)先級的線程有執(zhí)行的機會 |
sleep | 使調(diào)用線程進入休眠狀態(tài)腰湾,但在一個synchronized塊中執(zhí)行sleep脐湾,線程雖然會休眠果复,但不會釋放資源資源對象鎖 |
線程間消息通訊
- 主線程向子線程發(fā)送消息
//自定義一個looper子線程
class LooperThread extends Thread {
private Looper looper;
public Looper getLooper(){
synchronized (this){
if(looper == null && isAlive()){
try {
wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
return looper;
}
@Override
public void run() {
Looper.prepare();
synchronized (this){
looper = Looper.myLooper();
notifyAll();
}
Looper.loop();
}
}
//主線程向子線程發(fā)送消息
LooperThread looperThread = new LooperThread("looper-thread");
looperThread.start();
Handler handler = new Handler(looperThread.getLooper()){
@Override
public void handleMessage(@NonNull Message msg){
super.handleMessage(msg);
Log.e(TAG,"handleMessage:" + msg.what);
Log.e(TAG,"handleMessage:" + Thread.currentThread.getName);
}
};
handler.sendEmptyMessage(MSG_WHAT_1);
- 子線程向主線程發(fā)送消息(很熟悉颤枪,這里就省略了)
線程安全
什么是線程并發(fā)安全
線程安全的本質(zhì)是能夠讓并發(fā)線程有序的運行(這個有序有可能是先來后到的排隊,有可能有人插隊恒傻,但不管怎么樣脸侥,同一時刻只能一個線程有權訪問同步資源),線程執(zhí)行的結(jié)果盈厘,能夠?qū)ζ渌€程可見睁枕。
線程安全的幾種分類
- synchronized關鍵字
- ReentrantLock鎖
-
AtomicInteger...原子類
image.png
-
synchronized,ReentrantLock-鎖
image-20230729222013984.png -
原子類-自旋
image-20230729222203332.png - 鎖適合寫操作多的場景沸手,先加鎖可以保證寫操作時數(shù)據(jù)正確外遇。
- 原子類適合讀操作多的場景,不加鎖的特點能夠使其讀操作的性能大幅提升
如何保證線程安全
-
AtomicInteger原子包裝類
- AtomicInteger原子包裝類,CAS(Compare-And-Swap)實現(xiàn)無鎖數(shù)據(jù)更新契吉。自旋的設計能夠有效避免線程因阻塞-喚醒帶來的系統(tǒng)資源開銷跳仿。
- 使用場景:多線程計數(shù),原子操作捐晶,并發(fā)數(shù)量小的場景菲语。
//1妄辩、構建對象 AtomicInteger atomicInteger = new AtomicInteger(1); //2.調(diào)用 Api atomicInteger.getAndIncrement(); atomicInteger.getAndAdd(2); atomicInteger.getAndDecrement(); atomicInteger.getAndAdd(-2);
-
volatile 可見性修飾
volatile修飾的成員變量在每次被線程訪問時,都強迫從共享內(nèi)存重新讀取該成員的值山上,而且眼耀,當成員變量值發(fā)生變化時,強迫變化的值重新寫入共享內(nèi)存佩憾。
不能解決非原子操作的線程安全畔塔。性能不及原子類高。
volatile int count; public void increment(){ //其他線程可見 count = 5; //非原子操作鸯屿,其他線程不可見 count = count + 1; count ++;
-
synchronized
鎖java對象,鎖Class對象把敢,鎖代碼塊
- 鎖方法(本質(zhì)是鎖java對象)寄摆,加在方法上,未獲取到對象鎖的其他線程都不可以訪問該方法修赞。
synchronized void printThreadName(){ }
- 鎖Class對象婶恼,加在static方法上相當于給Class對象加鎖,哪怕是不同的java對象實例柏副,也需要排隊執(zhí)行勾邦。
static synchronized void printThreadName(){ }
- 鎖代碼塊,未獲取到對象鎖的其他線程可以執(zhí)行同步塊之外的代碼
void printThreadName(){ String name = Thread.currentThread.getName(); System.out.println("線程:" + name + "準備好了..."); synchronized(this){ } }
synchronized的優(yōu)勢是什么呢割择?
- 哪怕我們一個同步方法中出現(xiàn)了異常眷篇,那么JVM也能夠為我們自動釋放鎖,能主動從而規(guī)避死鎖荔泳,不需要開發(fā)者手動釋放鎖蕉饼。
劣勢是什么呢?
- 必須要等到獲取鎖對象的線程執(zhí)行完成玛歌,或者出現(xiàn)異常昧港,才能釋放掉。不能中途中途釋放鎖支子,不能中斷一個正在試圖獲得鎖的線程创肥。
- 另外咱們也不知道多線程競爭鎖的時候,獲取鎖成功與否值朋,所以不夠靈活叹侄。
- 每個鎖僅有單一的條件(某個對象),不能設定超時昨登。
-
ReentrantLock悲觀鎖圈膏,可重入鎖,公平鎖篙骡,非公平鎖
- 基本用法
ReentrantLock lock = new ReentrantLock(); try{ lock.lock ... }finally{ lock.unLock() }
void lock();//獲取不到會阻塞 boolean tryLock();//嘗試獲取鎖稽坤,成功返回true boolean tryLock(3000,TimeUnit.MILLISECONDS);//在一定時間內(nèi)去不斷嘗試獲取鎖 void lockInterruptibly();//可使用Thread.interrupt()打斷阻塞狀態(tài)丈甸,退出競爭,讓給其他線程
- 可重入尿褪,避免死鎖
ReentrantLock lock = new ReentrantLock(); public void doWork(){ try{ lock.lock(); doWork();//遞歸調(diào)用睦擂,使得統(tǒng)一線程多次獲得鎖 }finally{ lock.unLock(); } }
-
公平鎖與非公平鎖
- 公平鎖,所有進入阻塞的線程排隊依次均有機會執(zhí)行
- 默認非公平鎖杖玲,允許線程插隊顿仇,避免每一個線程都進入阻塞,在喚醒摆马,性能高臼闻。因為線程可以插隊,導致隊列中可能會存在線程餓死的情況囤采,一直得不到鎖述呐,一直得不到執(zhí)行。
-
ReentrantLock進階用法——Condition條件對象
可使用它的await-singnal指定喚醒一個(組)線程蕉毯。相比于wait-notify要么全部喚醒乓搬,要么只能喚醒一個,更加靈活可控
ReentrantLock lock = new ReentrantLock(); Condition worker1 = lock.newCondition(); Condition worker2 = lock.newCondition(); class Worker1{ ... worker1.await();//進入阻塞代虾,等待喚醒 ... } class Worker2{ ... worker2.await();//進入阻塞进肯,等待喚醒 } class Boss{ if(...){ worker1.signal();//指定喚醒線程1 }else{ worker2.signal();//指定喚醒線程2 } }
-
ReentrantReadWriteLock共享鎖,排他鎖
- 共享鎖棉磨,所有線程均可同時獲得江掩,并發(fā)量高,比如在線文檔查看乘瓤。
- 排他鎖频敛,同一時刻只有一個線程有權修改資源,比如在線文檔編輯馅扣。
ReentrantReadWriteLock reentrantReadWriteLock; ReentrantReadWriteLock.ReadLock readLock; ReentrantReadWriteLock.WriteLock writeLock;
如何正確的使用鎖&原子類
-
減少持鎖時間
盡管鎖在同一時間只能允許一個線程持有斟赚,其他想要占用鎖的線程都在臨界區(qū)外等待鎖的釋放,這個等待的時間我們希望盡可能的短差油。
public void syncMethod(){ noneLockedCode1();//2s synchronized(this){ needLockedMethed();//2s } noneLockedCode2();//2s }
-
鎖分離
讀讀拗军,讀寫,寫讀蓄喇。只要有寫鎖進入才需要做同步處理发侵,但是對于大多數(shù)應用來說,讀的場景要遠大于寫的場景妆偏,因此一旦使用讀寫鎖刃鳄,在讀多寫少的場景中,就可以很好的提高系統(tǒng)的性能钱骂。
讀鎖 寫鎖 讀鎖 可以訪問 不可訪問 寫鎖 不可訪問 不可訪問 -
鎖粗化
多次加鎖叔锐,釋放鎖合并成一次
public void doSomethingMethod(){ synchronized(lock){ //do some thing } ... //這里還有一些代碼挪鹏,做其它不需要同步的工作,但能很快執(zhí)行完畢 ... synchronized(lock){ //do other thing } }
public void doSomethingMethod(){ //進行鎖粗化:整合成一次鎖請求愉烙、釋放 synchronized(lock){ //do something //做其它不需要同步的工作讨盒,但能很快執(zhí)行完畢 //do other thing } }