認(rèn)識(shí)線程
? 線程的啟動(dòng)方式以及應(yīng)用場(chǎng)景
? 線程的狀態(tài)和常用方法
? 線程的優(yōu)先級(jí) 提升任務(wù)的響應(yīng)速度
? 線程間通訊 (子線程與主線程 發(fā)消息)多線程開(kāi)發(fā)
? 線程安全 (關(guān)鍵字synchornized 鎖 , 原子類治专, 并發(fā)容器)
? 線程流程控制線程池原理
? 任務(wù)調(diào)度
? 復(fù)用原理多線程優(yōu)化
?線程池
?并發(fā)安全
?kotlin(協(xié)程)
線程與進(jìn)程
分類:ui線程 工作線程
5種線程創(chuàng)建方式
-
new Thread (2種創(chuàng)建方法)
缺點(diǎn): 卻反統(tǒng)一管理寂恬,可能無(wú)限制新建線程吏祸,相互之間競(jìng)爭(zhēng)糟把,可能占用過(guò)多系統(tǒng)資源導(dǎo)致OOM 或者死機(jī).
public class ThreadDemo {
public static void main(String[] args) {
Thread t1 = new MyThread();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("線程2 =" + i);
}
}
});
t1.start();
t2.start();
}
static class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 10; i++) {
System.out.println("線程1 = " + i);
}
}
}
}
-
AsyncTask (3種寫(xiě)法)
于是Android 提供了 AsyncTask, 輕量級(jí)任務(wù)工具類没咙,提供任務(wù)執(zhí)行的進(jìn)度回調(diào)到UI線程秦驯,
場(chǎng)景:如果需要知道任務(wù)執(zhí)行的進(jìn)度掌动,多個(gè)任務(wù)串行
缺點(diǎn):生命周期和宿主的生命周期不同不蝴乔,有可能會(huì)導(dǎo)致內(nèi)存泄漏记餐,默認(rèn)情況下所有任務(wù)都是串行執(zhí)行。
解決方法 可以加上static不持有宿主的對(duì)象薇正,如果程序不能并發(fā)執(zhí)行片酝,就指定一個(gè)線程池.
public class AsyncTaskDemo extends AsyncTask<String,Integer,String> {
@Override
protected String doInBackground(String... params) {
//TODO 囚衔。。雕沿。练湿。。
publishProgress(**);
return params[0];
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(s);
// 拿到結(jié)果 result
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// 拿到進(jìn)度 审轮, values[0];
}
}
main(){
AsyncTaskDemo asyncTask = new AsyncTaskDemo();
// #1, 默認(rèn)串行執(zhí)行肥哎,可以感知任務(wù)進(jìn)度
asyncTask.execute( params );
// 可以修改為并發(fā)執(zhí)行
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
// #2,如果不想知道任務(wù)執(zhí)行進(jìn)度,可以直接創(chuàng)建
AsyncTask.execute(new Runnable() {
@Override
public void run() {
// ......
}
});
//# 3疾渣,直接創(chuàng)建 并發(fā)執(zhí)行
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
}
});
}
-
HandlerThread
使用于主線程需要和工作線程通信篡诽,適用于持續(xù)性任務(wù),比如輪詢榴捡,所有任務(wù)串行執(zhí)行杈女。
缺點(diǎn): 不會(huì)像普通線程一樣主動(dòng)銷毀資源,會(huì)一直運(yùn)行薄疚,所以可能會(huì)造成內(nèi)存泄漏碧信。
private void handlerThread() {
// 創(chuàng)建一個(gè)子線程
HandlerThread thread = new HandlerThread("current-thread");
thread.start();
/**
* 在主線程中新建一個(gè)Handler,持有 Thread 的 looper
Looper.
* 現(xiàn)在是主線程中的Handler 持有了一個(gè) 子線程的 Looper
*/
MHandler handler = new MHandler(thread.getLooper());
// handler 發(fā)送數(shù)據(jù) ,就是主線程 向子線程發(fā)送消息
handler.sendEmptyMessage(1);
}
static class MHandler extends Handler {
public MHandler(@NonNull Looper looper) {
super(looper);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
// 因?yàn)?Handler 持有了子線程的 Looper,所以此時(shí)實(shí)在子線程當(dāng)中街夭。
Log.e("MHandler", "message = " + msg.what);
}
}
如果
MHandler handler = new MHandler(); // 沒(méi)有傳遞Looper
handler.sendEmptyMessage(1);
// 因?yàn)橹骶€程中創(chuàng)建 Handler 時(shí)會(huì)有個(gè)默認(rèn)的 Looper
//這時(shí)捕獲消息的就是再主線程 中.
-
IntentService
繼承與Service ,在創(chuàng)建IntentService時(shí),onCreate()內(nèi)部會(huì)創(chuàng)建一個(gè)子線程來(lái)完成耗時(shí)操作
應(yīng)用場(chǎng)景: 適用于任務(wù)需要夸頁(yè)面讀取任務(wù)執(zhí)行的進(jìn)度躏筏、結(jié)果板丽。比如后臺(tái)上傳圖片,批量操作數(shù)據(jù)庫(kù)等趁尼。任務(wù)執(zhí)行完成后埃碱,就會(huì)自我結(jié)束,不需要手動(dòng) stopService()(與Service的區(qū)別)
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
String value = intent.getStringExtra("key");
Log.e("MyIntentService", "傳遞 key = " + value);
}
}
}
main 中調(diào)用
Intent intent = new Intent(this, MyIntentService.class);
intent.putExtra("key", "value");
startService(intent);
-
ThreadPoolExecutor
使用與快速處理大量耗時(shí)較短的任務(wù)場(chǎng)景酥泞。
Executors.newCachedThreadPool(); // 線程可復(fù)用的線程池
Executors.newFixedThreadPool(); // 固定線程數(shù)量的線程池
Executors.newScheduledThreadPool(); // 可指定定時(shí)任務(wù)的線程池
Executors.newSingleThreadExecutor(); // 線程數(shù)量為1 的線程池.
線程的優(yōu)先級(jí)
一般而言砚殿,線程的優(yōu)先級(jí)越高,獲得Cpu片的概率越大芝囤。
/**
* Jdk 提供的 似炎, [1,10] 數(shù)字越大,優(yōu)先級(jí)越高悯姊, UI線程的 優(yōu)先級(jí) 為 5 ;
*
*/
Thread thread = new Thread();
thread.setPriority(1);
/**
* Android API 羡藐, 優(yōu)先級(jí)更精細(xì)的劃分為 [-20,19]悯许,數(shù)字越小仆嗦,優(yōu)先級(jí)越高, UI線程的 優(yōu)先級(jí)為 -10.
* 效果較為明顯
* 一般把耗時(shí)較長(zhǎng)的線程的優(yōu)先級(jí)設(shè)置較低級(jí)別,
* 耗時(shí)較短較頻繁的線程的優(yōu)先級(jí)設(shè)置較高級(jí)別. 但是不要高于UI 線程.
*/
android.os.Process.setThreadPriority();
線程的狀態(tài)及常用方法
wait()/notify()
? 當(dāng)前線程進(jìn)入等待狀態(tài)先壕,并且釋放資源對(duì)象鎖瘩扼,可使用notify(),notifyAll() 或者等待超時(shí)來(lái)喚醒.
使用與多線程同步谆甜,一個(gè)線程要等待另一個(gè)線程的結(jié)果或者部分結(jié)果。 注意 wait - notify 的執(zhí)行順序
final Object object = new Object();
class Runnable1 implements Runnable {
@Override
public void run() {
Log.e("Runnable", "thread1 - start");
synchronized (object) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.e("Runnable", "thread1 - end");
}
}
class Runnable2 implements Runnable {
@Override
public void run() {
Log.e("Runnable", "thread2 - start");
synchronized (object) {
object.notify();
}
/**
* 線程2 notify()喚醒了線程1 集绰,
* 如果線程2有耗時(shí)操作店印,就先執(zhí)行線程1 了.
*/
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
Log.e("Runnable", "thread2 - end");
}
}
new Thread(new Runnable1()).start();
new Thread(new Runnable2()).start();
以上, object就是對(duì)象鎖倒慧, 這里如果換成 this , 跟object有啥不同嗎按摘。 wait()的使用是要在 synchornized() 同步代碼塊里面執(zhí)行的.
理想狀態(tài)下,線程1 執(zhí)行了 wait()以后使當(dāng)前線程進(jìn)入等待狀態(tài)纫谅,于是線程2拿到Cpu片開(kāi)始執(zhí)行炫贤,然后通過(guò)notify()喚醒線程1。
但是實(shí)際情況付秕,線程1 和線程2 誰(shuí)先執(zhí)行可不知道兰珍,如果線程2先執(zhí)行了,輪到線程1執(zhí)行時(shí)询吴,到了wait() 就沒(méi)有喚醒了出現(xiàn)假死現(xiàn)象掠河。
為了規(guī)避這種情況,可以定義一個(gè) volatile boolean hasNotify = false, 線程2 notify() 以后 hsNotify = true 猛计; 在 線程1 wait()前判斷 if(!hasNotify){ wait() } 唠摹, 或者 wait(1000), 自動(dòng)喚醒,可以避免線程 一直 wait()奉瘤;
join()
? ? 在當(dāng)前線程中插入一條任務(wù)勾拉,當(dāng)任務(wù)完成以后線程才可以繼續(xù)執(zhí)行.
class JoinThread extends Thread {
@Override
public void run() {
super.run();
Log.e("JoinThread", "run: 1 " + System.currentTimeMillis());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("JoinThread", "run:2 " + System.currentTimeMillis());
}
}
JoinThread thread = new JoinThread();
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("JoinThread", "join: 3 " + System.currentTimeMillis());
}
執(zhí)行順訊 就是 1,2盗温,3
UI線程中插入了一個(gè) JoinThread藕赞,UI線程要等JoinThread執(zhí)行完畢以后才接著執(zhí)行。
yield()
?? 不咋的用
暫停當(dāng)前正在執(zhí)行的線程對(duì)象卖局,不會(huì)釋放資源鎖斧蜕。
sleep()
?? 使調(diào)用線程進(jìn)入休眠狀態(tài),但在一個(gè)synchornized塊中執(zhí)行sleep,線程雖然休眠砚偶,但不會(huì)釋放資源對(duì)象鎖.
final Object object = new Object();
class Runnable1 implements Runnable {
@Override
public void run() {
Log.e("Runnable", "thread1 - start");
synchronized (object) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.e("Runnable", "thread1 - end");
}
}
class Runnable2 implements Runnable {
@Override
public void run() {
synchronized (object) {
Log.e("Runnable", "thread2 - start");
Log.e("Runnable", "thread2 - end");
}
}
}
new Thread(new Runnable1()).start();
new Thread(new Runnable2()).start();
線程1 和線程2 的執(zhí)行順序不保證批销,如果線程1先執(zhí)行,線程2會(huì)等線程1全部執(zhí)行完才執(zhí)行.
線程間通信
子線程發(fā)主線程發(fā)送消息
?? ?? post() 蟹演,runOnUiThread()...
主線程向子線程發(fā)送消息
/**
* 要讓主線程給子線程發(fā)送消息风钻, 必然要使子線程有處理消息的能力 ,Looper
*/
class LooperThread extends Thread {
Looper looper;
public LooperThread(String name) {
super(name);
}
// 但是由于 Thread 可能不會(huì)一 start 就執(zhí)行 run(),
// 所以 looper可能為空
// 于是 wait();
public Looper getLooper() {
if (looper == null && isAlive()) {
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return looper;
}
@Override
public void run() {
Looper.prepare();
looper = Looper.myLooper();
synchronized (this) {
notify();
}
// 開(kāi)啟Looper 的無(wú)線循環(huán);
Looper.loop();
}
}
LooperThread looperThread = new LooperThread("Looper - Thread");
looperThread.start();
// 創(chuàng)建的 Handler 持有了子線程的Looper, handler 就會(huì)捕獲有子線程中Looper分發(fā)的消息.
Handler handler = new Handler(looperThread.getLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
Log.e("ThreadMessage", " message = " + msg.what);
Log.e("ThreadMessage", "thread - name: " + Thread.currentThread().getName());
}
};
handler.sendEmptyMessage(2);
線程安全
(買票)
線程安全的本質(zhì):
? ? 能夠讓并發(fā)線程酒请,有序的運(yùn)行(有序可能是先來(lái)后到的排隊(duì)骡技,也可能是有人插隊(duì),但同一時(shí)刻只能有一個(gè)線程有權(quán)訪問(wèn)同步數(shù)據(jù)), 線程執(zhí)行的結(jié)果,能夠?qū)ζ渌€程可見(jiàn)布朦。
AutomicInteger 原子類
? ? 實(shí)現(xiàn)無(wú)鎖數(shù)據(jù)更新囤萤,自旋( do-while 循環(huán) ,線程不會(huì)阻塞) 的設(shè)計(jì)能夠有效避免線程阻塞-喚醒的系統(tǒng)資源開(kāi)銷是趴。 客戶端并發(fā)量不會(huì)很高.
適用于多線程技術(shù)涛舍,原子操作,并發(fā)量小的場(chǎng)景唆途。
volatile 可見(jiàn)性修飾
? ? volatile修飾的成員變量在每次被線程訪問(wèn)時(shí)富雅,都強(qiáng)迫從共享內(nèi)存重新讀取該成員的值,而且肛搬,當(dāng)成員變量值發(fā)生變化時(shí)没佑,強(qiáng)迫將變化的值重新寫(xiě)入共享內(nèi)存
不能解決非原子操作的線程安全性,性能不及原子類高温赔。
volatile int count;
private void increment() {
// 其他線程可見(jiàn).
count = 5;
// 以下兩步操作都不是一步操作蛤奢,是非原子操作,所以不可被其他線程可見(jiàn).
count = count + 1;
count++;
}
對(duì)比兩種方式
public class AtomicDemo {
public static void main(String[] args) {
final AtomicTask task = new AtomicTask();
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
task.atoAdd();
task.volatileCount();
}
}
};
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("原子類操作的結(jié)果 = " + task.atomicInteger.get()); // 結(jié)果 20000
System.out.println("volatile 操作的結(jié)果 = " + task.count); // 結(jié)果 18720
}
static class AtomicTask {
AtomicInteger atomicInteger = new AtomicInteger();
volatile int count = 0;
public void atoAdd() {
// 增量 陶贼,先 加1啤贩,然后返回操作之前的值.
atomicInteger.getAndIncrement();
// 減量 ,先獲取未更改前的值拜秧,然后 減1
//atomicInteger.getAndDecrement();
}
public void volatileCount() {
count++;
}
}
}
以上痹屹,在輸出結(jié)果中表現(xiàn) 使用volatile 修飾的變量 不保證線程安全。
synchornized
鎖java對(duì)象腹纳, 鎖Class類對(duì)象痢掠,鎖代碼塊.
- 鎖方法 , 加在方法上,為獲取到對(duì)象鎖的其他線程不可以訪問(wèn)該方法
public class ThreadDemo {
static ArrayList<String> tickets = new ArrayList<>();
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
tickets.add("票 -" + (i + 1));
}
saleTicket();
}
private static void saleTicket() {
final SynchronizedDemo demo = new SynchronizedDemo();
for (int i = 0; i < tickets.size(); i++) {
new Thread(new Runnable() {
@Override
public void run() {
demo.buyTicket();
}
}).start();
}
}
static class SynchronizedDemo {
synchronized void buyTicket() {
String name = Thread.currentThread().getName();
System.out.println("買票人:" + name + " 已經(jīng)準(zhǔn)備好");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("買票人 :" + name + " 買到的票是 " + tickets.remove(0));
}
}
}
========== 輸出結(jié)果 ============
買票人:Thread-0 已經(jīng)準(zhǔn)備好
買票人 :Thread-0 買到的票是 票 -1
買票人:Thread-4 已經(jīng)準(zhǔn)備好
買票人 :Thread-4 買到的票是 票 -2
買票人:Thread-3 已經(jīng)準(zhǔn)備好
買票人 :Thread-3 買到的票是 票 -3
買票人:Thread-2 已經(jīng)準(zhǔn)備好
買票人 :Thread-2 買到的票是 票 -4
買票人:Thread-1 已經(jīng)準(zhǔn)備好
買票人 :Thread-1 買到的票是 票 -5
以上, 針對(duì)同一個(gè) 買票人對(duì)象嘲恍,在butTicket()方法上加 synchronized就可以保證線程安全. 如果在多線程中訪問(wèn)不同對(duì)象的同步方法,就不能保證線程安全了.
因?yàn)榫€程同步是對(duì)同一個(gè)對(duì)象而言.
比如這樣, 每個(gè)線程中都創(chuàng)建一個(gè)買票人對(duì)象雄驹,
for (int i = 0; i < tickets.size(); i++) {
new Thread(new Runnable() {
@Override
public void run() {
final SynchronizedDemo demo = new SynchronizedDemo();
demo.buyTicket();
}
}).start();
}
========== 輸出結(jié)果 ============
買票人 :Thread-3 買到的票是 票 -2
買票人 :Thread-0 買到的票是 票 -1
買票人 :Thread-2 買到的票是 票 -4
買票人 :Thread-1 買到的票是 票 -3
買票人 :Thread-4 買到的票是 票 -1
========= == 購(gòu)買的票出現(xiàn)了重復(fù) =====
-
鎖Class 對(duì)象佃牛, 加載 static 方法上相當(dāng)于給Class對(duì)象加鎖,哪怕是不同的Java對(duì)象實(shí)例医舆,也需要排隊(duì)等候.
如果 synchronized 加在 static方法上俘侠,線程安全
static class SynchronizedDemo {
synchronized static void buyTicket() {
String name = Thread.currentThread().getName();
System.out.println("買票人:" + name + " 已經(jīng)準(zhǔn)備好");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("買票人 :" + name + " 買到的票是 " + tickets.remove(0));
}
}
=========== 輸出結(jié)果 ==========
買票人:Thread-0 已經(jīng)準(zhǔn)備好
買票人 :Thread-0 買到的票是 票 -1
買票人:Thread-4 已經(jīng)準(zhǔn)備好
買票人 :Thread-4 買到的票是 票 -2
買票人:Thread-3 已經(jīng)準(zhǔn)備好
買票人 :Thread-3 買到的票是 票 -3
買票人:Thread-2 已經(jīng)準(zhǔn)備好
買票人 :Thread-2 買到的票是 票 -4
買票人:Thread-1 已經(jīng)準(zhǔn)備好
買票人 :Thread-1 買到的票是 票 -5
以上,static加鎖蔬将,相當(dāng)于給Class類加鎖爷速, 在內(nèi)存中只有一個(gè),即使不同的買票人對(duì)象來(lái)訪問(wèn)霞怀,也是可以保證線程安全的.
- 鎖代碼塊惫东,獲取到對(duì)象鎖的線程可以執(zhí)行同步代碼塊以外的代碼.
void buyTicket() {
String name = Thread.currentThread().getName();
synchronized (this) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("買票人 :" + name + "正在買票");
}
System.out.println("買票人 :" + name + " 買到的票是 " + tickets.remove(0));
}
================ 輸出結(jié)果 ===================
買票人 :Thread-1正在買票
買票人 :Thread-1 買到的票是 票 -1
買票人 :Thread-3正在買票
買票人 :Thread-3 買到的票是 票 -2
買票人 :Thread-4正在買票
買票人 :Thread-4 買到的票是 票 -3
買票人 :Thread-2正在買票
買票人 :Thread-2 買到的票是 票 -4
買票人 :Thread-0正在買票
買票人 :Thread-0 買到的票是 票 -5
以上 如果 synchronized (SynchronizedDemo.class){ .........買票...... }
能保證線程同步.
synchronized 優(yōu)劣勢(shì)
優(yōu)勢(shì):可以規(guī)避死鎖,即使同步代碼快中出現(xiàn)了異常,jvm也能自動(dòng)釋放鎖廉沮。
劣勢(shì): - 必須要獲取鎖對(duì)象的線程執(zhí)行完或者異常才可以釋放鎖颓遏,不能中斷
??? ??- 多個(gè)線程獲取鎖 是否成功也不知道,不夠靈活.
??? ??- 不能設(shè)置超時(shí)滞时。
ReentrantLock鎖
void lock() ; 獲取不到會(huì)阻塞
boolean tryLock() ; 嘗試獲取鎖,獲取到鎖返回 true,
基本用法 :
public class ReentrantLockDemo {
public static void main(String[] args) {
final ReentrantTask task = new ReentrantTask();
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
task.buyTicket();
}
}).start();
}
}
static class ReentrantTask {
ReentrantLock lock = new ReentrantLock();
void buyTicket() {
String name = Thread.currentThread().getName();
try {
lock.lock();
System.out.println(name + " :準(zhǔn)備買票");
Thread.sleep(100);
System.out.println(name + " : 票買好了");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
=============== 輸出結(jié)果 ============
Thread-0 :準(zhǔn)備買票
Thread-0 : 票買好了
Thread-1 :準(zhǔn)備買票
Thread-1 : 票買好了
Thread-2 :準(zhǔn)備買票
Thread-2 : 票買好了
Thread-3 :準(zhǔn)備買票
Thread-3 : 票買好了
Thread-4 :準(zhǔn)備買票
Thread-4 : 票買好了
Thread-5 :準(zhǔn)備買票
Thread-5 : 票買好了
可重入叁幢,避免死鎖
在不釋放鎖的情況下,重復(fù)獲取鎖的對(duì)象.
try {
lock.lock();
System.out.println(name + " :準(zhǔn)備買票");
Thread.sleep(100);
System.out.println(name + " : 票買好了");
lock.lock();
System.out.println(name + " :準(zhǔn)備買票");
Thread.sleep(100);
System.out.println(name + " : 票買好了");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
lock.unlock();
}
============== 輸出結(jié)果 ==============
Thread-0 :準(zhǔn)備買票
Thread-0 : 票買好了
Thread-0 : 又準(zhǔn)備買票
Thread-0 : 票又買好了
Thread-1 :準(zhǔn)備買票
Thread-1 : 票買好了
Thread-1 : 又準(zhǔn)備買票
Thread-1 : 票又買好了
Thread-2 :準(zhǔn)備買票
Thread-2 : 票買好了
Thread-2 : 又準(zhǔn)備買票
Thread-2 : 票又買好了
Thread-3 :準(zhǔn)備買票
Thread-3 : 票買好了
Thread-3 : 又準(zhǔn)備買票
Thread-3 : 票又買好了
Thread-4 :準(zhǔn)備買票
Thread-4 : 票買好了
Thread-4 : 又準(zhǔn)備買票
Thread-4 : 票又買好了
公平鎖與非公平鎖
公平鎖 : 所有進(jìn)入阻塞隊(duì)列的線程依次有機(jī)會(huì)執(zhí)行.
(默認(rèn))非公平鎖:允許線程插隊(duì)坪稽,避免每一個(gè)線程都阻塞再被喚醒曼玩,性能較高。但是因?yàn)榭梢圆尻?duì)的原因窒百,會(huì)導(dǎo)致有的線程一直得不到執(zhí)行.
ReentrantLock lock = new ReentrantLock( true / false );
簡(jiǎn)單模擬多線程打印功能
public class ReentrantDemo2 {
public static void main(String[] args) {
final PrintTask task = new PrintTask();
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
task.print();
}
}).start();
}
}
static class PrintTask {
private ReentrantLock lock = new ReentrantLock(true);
void print() {
String name = Thread.currentThread().getName();
try {
lock.lock();
System.out.println(name + ": 第一次打印");
Thread.sleep(1000);
lock.unlock();
lock.lock();
System.out.println(name + ": 第二次打印");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
如果 公平鎖
輸出結(jié)果
Thread-0: 第一次打印
Thread-1: 第一次打印
Thread-2: 第一次打印
Thread-3: 第一次打印
Thread-4: 第一次打印
Thread-0: 第二次打印
Thread-1: 第二次打印
Thread-2: 第二次打印
Thread-3: 第二次打印
Thread-4: 第二次打印
如果非公平鎖
Thread-0: 第一次打印
Thread-0: 第二次打印
Thread-1: 第一次打印
Thread-1: 第二次打印
Thread-2: 第一次打印
Thread-2: 第二次打印
Thread-3: 第一次打印
Thread-3: 第二次打印
Thread-4: 第一次打印
Thread-4: 第二次打印
以上黍判,表明公平鎖釋放鎖以后,每個(gè)線程獲取到鎖的機(jī)會(huì)是均等的贝咙, 如果是非公平鎖样悟,當(dāng)?shù)谝淮未蛴⊥赆尫沛i以后,因?yàn)槠渌€程的狀態(tài)都是阻塞的庭猩,重新喚醒需要有一定的資源消耗窟她,而當(dāng)前線程釋放掉以后還沒(méi)有進(jìn)入阻塞狀態(tài),由當(dāng)前線程來(lái)獲取鎖對(duì)象是開(kāi)銷最小的蔼水。
應(yīng)用場(chǎng)景:
公平鎖:交易
非公平鎖 :synchornized, 場(chǎng)景就很多了.
ReentrantLock- Condintion對(duì)象
使用 await - singnal 可以指定喚醒一個(gè)(或一組)線程震糖。
共享鎖 & 排他鎖 (ReentrantReadWriteLock)
共享鎖,所有線程都可同時(shí)獲得趴腋,并發(fā)量高吊说,比如在線查看文檔
排他鎖,同一時(shí)刻只能有一個(gè)線程有權(quán)修改資源优炬,比如在線文檔編輯.
??如何正確的使用鎖和原子類
一般情況下 synchronized 已經(jīng)足夠使用颁井,
如果需要知道鎖的細(xì)節(jié),就要使用Lock
?? 調(diào)優(yōu)
- 減少持鎖的時(shí)間蠢护,比如synchronized 同步代碼快雅宾,非持有鎖的線程可以執(zhí)行同步代碼塊之外的程序,可以減少線程等待時(shí)間.
- 鎖粗化 葵硕, 如果多次同步代碼塊之間有一部分耗時(shí)很短不用同步的代碼眉抬,可以將這些程序合并為一次同步.
- 在并發(fā)量不大的情況下,可以考慮使用原子類懈凹。
線程池
??為什么引入線程池
- 降低資源消耗 : 通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀的造成的消耗蜀变。
- 提高響應(yīng)速度: 當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不用等待線程的創(chuàng)建就可以立即執(zhí)行介评。
- 提高線程的可管理性:線程是稀缺資源库北,如果無(wú)限制的創(chuàng)建,不僅會(huì)造成系統(tǒng)資源的浪費(fèi),還會(huì)影響系統(tǒng)的穩(wěn)定性贤惯,使用線程池統(tǒng)一分配洼专,調(diào)優(yōu)和監(jiān)控.