線程基礎(chǔ)
1. 線程的生命周期
1.1 新建狀態(tài):
- 使用 new 關(guān)鍵字和 Thread 類或其子類建立一個(gè)線程對象后,該線程對象就處于新建狀態(tài)。它保持這個(gè)狀態(tài)直到程序 start() 這個(gè)線程。
1.2 就緒狀態(tài):
- 當(dāng)線程對象調(diào)用了start()方法之后,該線程就進(jìn)入就緒狀態(tài)。就緒狀態(tài)的線程處于就緒隊(duì)列中,要等待JVM里線程調(diào)度器的調(diào)度。
1.3 運(yùn)行狀態(tài):
- 如果就緒狀態(tài)的線程獲取 CPU 資源,就可以執(zhí)行 run(),此時(shí)線程便處于運(yùn)行狀態(tài)。處于運(yùn)行狀態(tài)的線程最為復(fù)雜,它可以變?yōu)樽枞麪顟B(tài)、就緒狀態(tài)和死亡狀態(tài)。
1.4 阻塞狀態(tài):
-
如果一個(gè)線程執(zhí)行了sleep(睡眠)、suspend(掛起)等方法,失去所占用資源之后,該線程就從運(yùn)行狀態(tài)進(jìn)入阻塞狀態(tài)。在睡眠時(shí)間已到或獲得設(shè)備資源后可以重新進(jìn)入就緒狀態(tài)。可以分為三種:
- 等待阻塞:運(yùn)行狀態(tài)中的線程執(zhí)行 wait() 方法,使線程進(jìn)入到等待阻塞狀態(tài)。
- 同步阻塞:線程在獲取 synchronized 同步鎖失敗(因?yàn)橥芥i被其他線程占用)。
- 其他阻塞:通過調(diào)用線程的 sleep() 或 join() 發(fā)出了 I/O 請求時(shí),線程就會(huì)進(jìn)入到阻塞狀態(tài)。當(dāng)sleep() 狀態(tài)超時(shí),join() 等待線程終止或超時(shí),或者 I/O 處理完畢,線程重新轉(zhuǎn)入就緒狀態(tài)。
1.5 死亡狀態(tài):
- 一個(gè)運(yùn)行狀態(tài)的線程完成任務(wù)或者其他終止條件發(fā)生時(shí),該線程就切換到終止?fàn)顟B(tài)。
2. 線程的優(yōu)先級和守護(hù)線程
2.1 線程的優(yōu)先級
- 每一個(gè) Java 線程都有一個(gè)優(yōu)先級,這樣有助于操作系統(tǒng)確定線程的調(diào)度順序。
-
Java 線程的優(yōu)先級是一個(gè)整數(shù),其取值范圍是
1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )
。 -
默認(rèn)情況下,每一個(gè)線程都會(huì)分配一個(gè)優(yōu)先級
NORM_PRIORITY(5)
。
2.2 守護(hù)線程
- Java中有兩種線程:用戶線程和守護(hù)線程??梢酝ㄟ^isDeamon()方法來區(qū)別它們:如果返回false,則說明該線程是“用戶線程”;否則就是“守護(hù)線程”。
- 用戶線程一般用戶執(zhí)行用戶級任務(wù),而守護(hù)線程也就是“后臺線程”,一般用來執(zhí)行后臺任務(wù)。
- 需要注意的是:JVM在“用戶線程”都結(jié)束后會(huì)退出。
3. 創(chuàng)建線程
3.1 通過實(shí)現(xiàn) Runnable 接口
-
步驟:
- 創(chuàng)建類實(shí)現(xiàn) Runnable 接口
- 實(shí)現(xiàn) run() 方法,線程實(shí)際運(yùn)行的方法
- 實(shí)現(xiàn) start() 方法,里面實(shí)例化線程對象(new Thread(this, threadName)),調(diào)用線程對象的 start() 方法
-
代碼實(shí)現(xiàn)
package com.ljw.thread; public class RunnableDemo { public static void main(String[] args) { // 測試 RunnableDemo R = new RunnableDemo(); RunnableThread R1 = R.new RunnableThread("thread1"); R1.start(); RunnableThread R2 = R.new RunnableThread("thread2"); R2.start(); } class RunnableThread implements Runnable{ private String threadName; private Thread t; public RunnableThread(String name) { // TODO Auto-generated constructor stub threadName = name; System.out.println("創(chuàng)建線程 "+threadName); } @Override public void run() { System.out.println("正在運(yùn)行線程:"+threadName); try { for(int i=10;i>0;i--) { System.out.println("線程:"+threadName+" 正在打印:"+i); Thread.sleep(50); } }catch(Exception e) { e.printStackTrace(); } System.out.println("線程:"+threadName+" 正在退出......"); } public void start() { System.out.println("開始線程 "+threadName); if(t == null) { t = new Thread(this, threadName); t.start(); } } } }
3.2 通過繼承 Thread 類本身
-
步驟:
- 創(chuàng)建類繼承 Thread 類
- 下面與用Runnable接口一樣
3.3 通過 Callable 和 Future 創(chuàng)建線程
-
步驟:
- 創(chuàng)建 Callable 接口的實(shí)現(xiàn)類,并實(shí)現(xiàn) call() 方法,該 call() 方法將作為線程執(zhí)行體,并且有返回值。
- 創(chuàng)建 Callable 實(shí)現(xiàn)類的實(shí)例,使用 FutureTask 類來包裝 Callable 對象,該 FutureTask 對象封裝了該 Callable 對象的 call() 方法的返回值。
- 使用 FutureTask 對象作為 Thread 對象的 target 創(chuàng)建并啟動(dòng)新線程。
- 調(diào)用 FutureTask 對象的 get() 方法來獲得子線程執(zhí)行結(jié)束后的返回值。
-
Callable接口與Runnable接口的區(qū)別:
- Callable中call方法可以有返回值,而Runnable中的run方法沒有返回值
-
代碼實(shí)現(xiàn)
package com.ljw.thread; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class CallableThreadTest implements Callable { public static void main(String[] args) { CallableThreadTest ctt = new CallableThreadTest(); FutureTask ft = new FutureTask<>(ctt); for(int i = 0;i < 10;i++) { System.out.println(Thread.currentThread().getName()+" 的循環(huán)變量i的值"+i); if(i%2==0) { new Thread(ft,"有返回值的線程").start(); } } try { System.out.println("子線程的返回值:"+ft.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } @Override public Integer call() throws Exception { int i = 0; for(;i<10;i++) { System.out.println(Thread.currentThread().getName()+" "+i); } return i; } }
4. synchronized關(guān)鍵字
4.1 概述
- synchronized關(guān)鍵字是為了解決共享資源競爭的問題,共享資源一般是以對象形式存在的內(nèi)存片段,但也可以是文件、輸入/輸出端口,或者是打印機(jī)。
- 要控制對共享資源的訪問,得先把它包裝進(jìn)一個(gè)對象。然后把所有要訪問的這個(gè)資源的方法標(biāo)記為synchronized。
- 如果某個(gè)任務(wù)處于一個(gè)對標(biāo)記為synchronized的方法的調(diào)用中,那么在這個(gè)線程從該方法返回之前,其他所有要調(diào)用類中任何標(biāo)記為synchronized方法的線程都會(huì)被阻塞。
4.2 基本原則
-
第一條:當(dāng)一個(gè)線程訪問某對象的
synchronized方法
或者synchronized代碼塊
時(shí),其他線程對該對象的該synchronized方法
或者synchronized代碼塊
的訪問將被阻塞。 -
第二條:當(dāng)一個(gè)線程訪問某對象的
synchronized方法
或者synchronized代碼塊
時(shí),其他線程仍然可以訪問該對象的非同步代碼塊。 -
第三條:當(dāng)一個(gè)線程訪問某對象的
synchronized方法
或者synchronized代碼塊
時(shí),其他線程對該對象的其他的synchronized方法
或者synchronized代碼塊
的訪問將被阻塞。
4.3 實(shí)例
-
兩個(gè)相似的例子
- 實(shí)例1:實(shí)現(xiàn)接口Runnable
package com.ljw.thread; public class RunnableTest { public static void main(String[] args) { class MyRunnable implements Runnable{ @Override public void run() { synchronized (this) { for(int i=0;i<5;i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 正在進(jìn)行打印 " +i); } } } } Runnable runnable = new MyRunnable(); Thread t1 = new Thread(runnable,"t1"); Thread t2 = new Thread(runnable,"t2"); t1.start(); t2.start(); } }
運(yùn)行結(jié)果:
t1 正在進(jìn)行打印 0 t1 正在進(jìn)行打印 1 t1 正在進(jìn)行打印 2 t1 正在進(jìn)行打印 3 t1 正在進(jìn)行打印 4 t2 正在進(jìn)行打印 0 t2 正在進(jìn)行打印 1 t2 正在進(jìn)行打印 2 t2 正在進(jìn)行打印 3 t2 正在進(jìn)行打印 4
結(jié)果說明:run()方法中存在synchronized(this)代碼塊,而且t1和t2都是基于MyRunnable這個(gè)Runnable對象創(chuàng)建的線程。這就意味著,我們可以將synchronized(this)中的this看做是MyRunnable這個(gè)Runnable對象;因此,線程t1和t2共享“MyRunable對象的同步鎖”。所以,當(dāng)一個(gè)線程運(yùn)行的時(shí)候,另外一個(gè)線程必須等待正在運(yùn)行的線程釋放MyRunnable的同步鎖之后才能運(yùn)行。
- 實(shí)例2:繼承Thread類
public class ThreadTest { public static void main(String[] args) { class MyThread extends Thread{ public MyThread(String name){ super(name); } @Override public void run() { synchronized(this){ for(int i=0;i<10;i++){ try { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" 正在進(jìn)行打印 "+i); } catch (InterruptedException e) { e.printStackTrace(); } } } } } Thread t1 = new MyThread("t1"); Thread t2 = new MyThread("t2"); t1.start(); t2.start(); } }
運(yùn)行結(jié)果:
t2 正在進(jìn)行打印 0 t1 正在進(jìn)行打印 0 t2 正在進(jìn)行打印 1 t1 正在進(jìn)行打印 1 t1 正在進(jìn)行打印 2 t2 正在進(jìn)行打印 2 t2 正在進(jìn)行打印 3 t1 正在進(jìn)行打印 3 t1 正在進(jìn)行打印 4 t2 正在進(jìn)行打印 4
對比結(jié)果:發(fā)現(xiàn)實(shí)例1的兩個(gè)線程是一個(gè)結(jié)束后,另一個(gè)才運(yùn)行,實(shí)例2的是交叉運(yùn)行,在run()方法中都有synchronized(this),為什么結(jié)果不一樣?
分析:synchronized(this)中的this是指當(dāng)前對象,即synchronized(this)所在類對應(yīng)的當(dāng)前對象。它的作用是獲取獲取當(dāng)前對象的同步鎖。對于實(shí)例2中的synchronized(this)中的this代表的是MyThread對象,t1和t2是兩個(gè)不同的MyThread對象,因此t1和t2在執(zhí)行synchronized(this)時(shí)獲取的是不同對象的同步鎖。對于實(shí)例1來說,synchronized(this)中的this代表的時(shí)候MyRunnable對象,t1和t2是共同一個(gè)MyRunnable對象,因此,一個(gè)線程獲取了對象的同步鎖,會(huì)造成另一個(gè)線程的等待。
4.4 synchronized方法和synchronized代碼塊
4.4.1 概述
-
synchronized方法
是用synchronized修飾方法,這是一種粗粒度鎖;這個(gè)同步方法(非static方法)無需顯式指定同步監(jiān)視器,同步方法的同步監(jiān)視器是this,也就是調(diào)用該方法的對象。 -
synchronized代碼塊
是用synchronized修飾代碼塊,這是一種細(xì)粒度鎖。線程開始執(zhí)行同步代碼塊之前,必須先獲得對同步監(jiān)視器的鎖定,任何時(shí)候只能有一個(gè)線程可以獲得對同步監(jiān)視器的鎖定,當(dāng)同步代碼塊執(zhí)行完成后,該線程會(huì)釋放對同步監(jiān)視器的鎖定。雖然Java允許使用任何對象作為同步監(jiān)視器,但同步監(jiān)視器的目的就是為了阻止兩個(gè)線程對同一個(gè)共享資源進(jìn)行并發(fā)訪問,因此通常推薦使用可能被并發(fā)訪問的共享資源充當(dāng)同步監(jiān)視器。
4.4.2 實(shí)例
public class SnchronizedTest {
public static void main(String[] args) {
class Demo {
// synchronized方法
public synchronized void synMethod() {
for(int i=0; i<1000000; i++)
;
}
public void synBlock() {
// synchronized代碼塊
synchronized( this ) {
for(int i=0; i<1000000; i++)
;
}
}
}
}
}
4.5 實(shí)例鎖和全局鎖
4.5.1 概述
- 實(shí)例鎖:鎖在某個(gè)實(shí)例對象上。如果該類是單例,那么該鎖也是具有全局鎖的概念。實(shí)例鎖對應(yīng)的就是synchronized關(guān)鍵字。
- 全局鎖:該鎖針對的是類,無論實(shí)例多少個(gè)對象,那么線程都共享該鎖。全局鎖對應(yīng)的就是static synchronized(或者是鎖在該類的class或者classloader對象上)。
4.5.2 實(shí)例
pulbic class Something {
public synchronized void isSyncA(){}
public synchronized void isSyncB(){}
public static synchronized void cSyncA(){}
public static synchronized void cSyncB(){}
}
假設(shè),類Something有兩個(gè)實(shí)例(對象)分別為x和y。分析下面4組表達(dá)式獲取鎖的情況。
-
x.isSyncA()與x.isSyncB()
- 不能同時(shí)訪問,因?yàn)槎际窃L問對象x的同步鎖
-
x.isSyncA()與y.isSyncA()
- 可以同時(shí)訪問,因?yàn)槭窃L問不同對象(x和y)的同步鎖
-
x.cSyncA()與y.cSyncB()
- 不能同時(shí)訪問,因?yàn)閮蓚€(gè)方法是靜態(tài)的,相當(dāng)于用Something.cSyncA()和Something.cSyncB()訪問,是相同的對象
-
x.isSyncA()與Something.cSyncA()
- 可以同時(shí)訪問,因?yàn)樵L問不同對象
5. Volatile 關(guān)鍵字
5.1 Volatile原理
- Java語言提供了一種稍微同步機(jī)制,即volatile變量,用來確保將變量的更新操作通知其他線程
- 在訪問volatile變量是不會(huì)執(zhí)行加鎖操作,因此也就不會(huì)重新執(zhí)行線程阻塞,volatile變量是一種比synchronized關(guān)鍵字輕量級的同步機(jī)制
- 當(dāng)一個(gè)變量被volatile修飾后,不但具有可見性,而且還禁止指令重排。volatile的讀性能消耗與普通變量幾乎相同,但是寫操作就慢一些,因?yàn)樗WC本地代碼中插入許多內(nèi)存屏障指令來保證處理器不發(fā)生亂序執(zhí)行
6. 線程等待和喚醒
6.1 常用方法
- 在Object.java中,定義了wait(),notify()和notifyAll()等接口
- wait()方法的作用是讓當(dāng)前線程進(jìn)入阻塞狀態(tài),同時(shí)會(huì)釋放當(dāng)前對象所持有的鎖
- notify()喚醒當(dāng)前對象上的等待線程,notifyAll()則是喚醒所有的線程
6.2 實(shí)例
package com.ljw.thread;
public class WaitDemo {
public static void main(String[] args) {
class ThreadTest extends Thread{
@Override
public void run() {
synchronized (this) {
System.out.println("開始運(yùn)行線程 "+Thread.currentThread().getName());
System.out.println("喚醒線程notify()");
notify();
}
}
}
ThreadTest thread1 = new ThreadTest();
thread1.start();
synchronized (thread1) {
try {
System.out.println("主線程進(jìn)入阻塞,釋放thread對象的同步鎖,wait()");
thread1.wait(); // wait()是讓當(dāng)前線程進(jìn)入阻塞狀態(tài),wait()是在主線程中執(zhí)行,
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("主線程繼續(xù)進(jìn)行");
}
}
7. 線程讓步和休眠
7.1 線程讓步
7.1.1 概述
- 在Java線程中,yield()方法的作用是讓步,它能讓當(dāng)前線程由“運(yùn)行狀態(tài)”進(jìn)入到“就緒狀態(tài)”,可能讓其它同級別的線程獲得執(zhí)行權(quán),但不一定,可能它自己再次由“就緒狀態(tài)”進(jìn)入到“運(yùn)行狀態(tài)”
7.1.2 實(shí)例
package com.ljw.thread;
public class YieldTest {
public static void main(String[] args) {
class ThreadA extends Thread{
public ThreadA(String name){
super(name);
}
@Override
public synchronized void run() {
for(int i=0;i<5;i++){
System.out.println(" "+this.getName()+" "+i);
if(i%2 == 0){
Thread.yield();
}
}
}
}
ThreadA t1 = new ThreadA("t1");
ThreadA t2 = new ThreadA("t2");
t1.start();
t2.start();
}
}
運(yùn)行結(jié)果(不唯一):
t1 0
t2 0
t1 1
t1 2
t2 1
t1 3
t2 2
t1 4
t2 3
t2 4
結(jié)果說明:
線程t1在能被2整除的時(shí)候,并不一定切換到線程2。這表明,yield()方法雖然可以讓線程由“運(yùn)行狀態(tài)”進(jìn)入到“就緒狀態(tài)”;但是,它不一定會(huì)讓其他線程獲取CPU執(zhí)行權(quán)(其他線程進(jìn)入到“運(yùn)行狀態(tài)”)。即時(shí)這個(gè)“其他線程”與當(dāng)前調(diào)用yield()的線程具有相同的優(yōu)先級。
7.1.3 yield()和wait()比較
- wait()的作用是讓當(dāng)前線程由“運(yùn)行狀態(tài)”進(jìn)入“阻塞狀態(tài)”,而yield()是讓當(dāng)前線程由“運(yùn)行狀態(tài)”進(jìn)入“就緒狀態(tài)”
- wait()是會(huì)讓線程釋放它所持有的對象的同步鎖,而yield()方法不會(huì)釋放對象的同步鎖。
7.2 線程休眠
7.2.1 概述
- sleep()方法定義在Thread類中,sleep()的作用是讓當(dāng)前線程休眠,即當(dāng)前線程會(huì)從“遠(yuǎn)程狀態(tài)”進(jìn)入到“休眠(阻塞)狀態(tài)”
- sleep()會(huì)指定休眠時(shí)間,線程休眠的時(shí)間會(huì)大于/等于該休眠時(shí)間
- 在線程重新被喚醒時(shí),它會(huì)由“阻塞狀態(tài)”變成“就緒狀態(tài)”,從而等待CPU的調(diào)度執(zhí)行。
7.2.2 sleep() 和 wait()的比較
- wait()的作用是讓當(dāng)前的線程由“運(yùn)行狀態(tài)”進(jìn)入到“等待(阻塞)狀態(tài)”的同時(shí),也會(huì)釋放同步鎖- 但是sleep()的作用是讓當(dāng)前線程由“運(yùn)行狀態(tài)”進(jìn)入到“休眠(阻塞)”狀態(tài),但不會(huì)釋放鎖。
8. 加入一個(gè)線程
8.1 概述
- 在一個(gè)線程T上調(diào)用另一個(gè)線程t的 join() 方法,相當(dāng)于在T中加入線程t,要等t結(jié)束后(即t.isAlive為假), join() 后面的代碼塊才會(huì)執(zhí)行。
- 可以在調(diào)用jion()時(shí)帶上一個(gè)超時(shí)參數(shù)(單位可以是毫秒,或者納秒),這樣如果目標(biāo)線程在這段時(shí)間到期時(shí)還沒有結(jié)束的話,join()方法總能返回
9. 終止一個(gè)線程
9.1 概述
- interrupt()并不會(huì)終止處于“運(yùn)行狀態(tài)”的線程,它會(huì)將線程的中斷標(biāo)記設(shè)為true。
- 綜合線程處于“阻塞狀態(tài)”和“運(yùn)行狀態(tài)”的終止方式,比較通用的終止線程的形式如下:
@Override
public void run() {
try {
// 1. isInterrupted()保證,只要中斷標(biāo)記為true就終止線程。
while (!isInterrupted()) {
// 執(zhí)行任務(wù)...
}
} catch (InterruptedException ie) {
// 2. InterruptedException異常保證,當(dāng)InterruptedException異常產(chǎn)生時(shí),線程被終止。
}
}
9.2 實(shí)例
public class InterruptBlock {
/**
* @param args
*/
public static void main(String[] args) {
class MyThread extends Thread{
public MyThread(String name){
super(name);
}
@Override
public void run() {
try {
int i=0;
while(!isInterrupted()){
Thread.sleep(100);
i++;
System.out.println(Thread.currentThread().getName()+ " ("+this.getState()+") loop "+i);
}
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName()+ " ("+this.getState()+") catch InterruptedExecption");
}
}
}
try {
//新建
Thread t1 = new MyThread("t1");
System.out.println(t1.getName()+" ("+t1.getState()+" ) is new.");
System.out.println("luo1:"+t1.isInterrupted());
//啟動(dòng)
t1.start();
System.out.println(t1.getName()+" ("+t1.getState()+" ) is started.");
System.out.println("luo2:"+t1.isInterrupted());
//主線程休眠300ms,然后主線程給t1發(fā)“中斷”指令
Thread.sleep(300);
t1.interrupt();
System.out.println("luo3:"+t1.isInterrupted());
System.out.println(t1.getName()+" ("+t1.getState()+" ) is interrupted.");
//主線程休眠300ms,然后查看t1的狀態(tài)
Thread.sleep(300);
System.out.println(t1.getName()+" ("+t1.getState()+" ) is interrupted now .");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
運(yùn)行結(jié)果:
t1 (NEW ) is new.
luo1:false
t1 (RUNNABLE ) is started.
luo2:false
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
luo3:true
t1 (RUNNABLE) loop 3
t1 (RUNNABLE ) is interrupted.
t1 (TERMINATED ) is interrupted now .
9.3 interrupt()和isInterrupted()的區(qū)別
- interrupt()和isInterrupted()都能夠用于檢測對象的“中斷標(biāo)記”。區(qū)別是:interrupt()除了返回中斷標(biāo)記之外,它還會(huì)清除中斷標(biāo)記(即將中斷標(biāo)記設(shè)為false);而isInterrupted()僅僅返回中斷標(biāo)記。
線程進(jìn)階
1. 線程池
- 示例
package com.ljw.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) { // 主線程
// 線程池 ---> Executors工具類(工廠類)
/*
* newFixedThreadPool(int threadCount) 創(chuàng)建固定數(shù)量的線程池
* newCachedThreadPool() 創(chuàng)建動(dòng)態(tài)數(shù)量的線程池
*/
ExecutorService es = Executors.newFixedThreadPool(3);
Runnable task = new MyTask();
// 提交任務(wù)
es.submit(task);
es.submit(task);
es.shutdown(); // 關(guān)閉線程池,則表示不在接收新任務(wù),不代表正在線程池的任務(wù)會(huì)停掉
}
}
class MyTask implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++) {
System.out.println(Thread.currentThread().getName()+" MyTask "+i);
}
}
}
2. 線程安全與鎖
2.1 重入鎖和讀寫鎖
package com.ljw.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
/**
* ReentrantLock類,重入鎖:Lock接口的實(shí)現(xiàn)類,與synchronized一樣具有互斥鎖功能 lock() 和 unlock()
* ReentrantReadWriteLock類,讀寫鎖:一種支持一寫多讀的同步鎖,讀寫分離,分別分配讀鎖和寫鎖,在讀操作遠(yuǎn)遠(yuǎn)高于寫操作的環(huán)境中可以提高效率
* 互斥規(guī)則:
* 寫--寫:互斥,阻塞
* 讀--寫:互斥,阻塞
* 讀--讀:不互斥,不阻塞
*
*/
public class LockDemo {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(20);
Student s = new Student();
// ReentrantLock rLock = new ReentrantLock(); // 用ReenTrantLock加鎖運(yùn)行時(shí)間20008ms
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); // 用讀寫鎖分別對讀寫任務(wù)加鎖運(yùn)行時(shí)間3003ms
ReadLock rl = rwLock.readLock();
WriteLock wl = rwLock.writeLock();
// 寫任務(wù)
Callable writeTask = new Callable() {
@Override
public Object call() throws Exception {
// rLock.lock();
wl.lock();
try {
Thread.sleep(1000);
s.setValue(100);
}finally {
// rLock.unlock();
wl.unlock();
}
return null;
}};
// 讀任務(wù)
Callable readTask = new Callable() {
@Override
public Object call() throws Exception {
// rLock.lock();
rl.lock();
try {
Thread.sleep(1000);
s.getValue();
}finally {
// rLock.unlock();
rl.unlock();
}
return null;
}};
// 開始時(shí)間
long start = System.currentTimeMillis();
for(int i=0;i<2;i++) { // 寫任務(wù)執(zhí)行 2 次
es.submit(writeTask);
}
for(int i=0;i<18;i++) { // 讀任務(wù)執(zhí)行 18 次
es.submit(readTask);
}
es.shutdown(); // 停止線程池,不在接受新的任務(wù),將現(xiàn)有任務(wù)全部執(zhí)行完畢
while(true) {
if(es.isTerminated()) { // 當(dāng)線程池中所有任務(wù)執(zhí)行完畢,返回true,否則返回false
break;
}
}
// 執(zhí)行到這里,說明線程池中所有任務(wù)都執(zhí)行完畢,可以計(jì)算結(jié)束時(shí)間
System.out.println(System.currentTimeMillis()-start);
}
}
class Student {
private int value;
//讀
public int getValue() {
return value;
}
//寫
public void setValue(int value) {
this.value = value;
}
}
2.2 線程安全
2.2.1 Collections工具類
- Collections工具類中提供了多個(gè)可以獲得線程安全集合的方法
static Collection synchronizedCollection(Collection c)
//返回由指定集合支持的同步(線程安全)集合。
static List synchronizedList(List list)
//返回由指定列表支持的同步(線程安全)列表。
static Map synchronizedMap(Map m)
//返回由指定地圖支持的同步(線程安全)映射。
static NavigableMap synchronizedNavigableMap(NavigableMap m)
//返回由指定的可導(dǎo)航地圖支持的同步(線程安全)可導(dǎo)航地圖。
static NavigableSet synchronizedNavigableSet(NavigableSet s)
//返回由指定的可導(dǎo)航集支持的同步(線程安全)可導(dǎo)航集。
static Set synchronizedSet(Set s)
//返回由指定集合支持的同步(線程安全)集。
static SortedMap synchronizedSortedMap(SortedMap m)
//返回由指定的排序映射支持的同步(線程安全)排序映射。
static SortedSet synchronizedSortedSet(SortedSet s)
//返回由指定的排序集支持的同步(線程安全)排序集。,v>,v>,v>,v>,v>,v>,v>,v>,v>
2.2.2 CopyOnWriteArrayList
- 線程安全的ArrayList
- 讀寫分離,寫加鎖,讀沒鎖,讀寫之間不互斥
- 使用方法與ArrayList無異
2.2.3 CopyOnWriteArraySet
- 基于 CopyOnWriteArrayList
2.2.4 ConcurrentHashMap
- 初始容量默認(rèn)為16段(Segment),采用分段鎖設(shè)計(jì)
- 不對整個(gè)Map加鎖,只對每個(gè)Segment加鎖
- 當(dāng)多個(gè)對象訪問同個(gè)Segment才會(huì)互斥
審核編輯 黃昊宇
-
JAVA
+關(guān)注
關(guān)注
19文章
2952瀏覽量
104493 -
數(shù)據(jù)庫
+關(guān)注
關(guān)注
7文章
3752瀏覽量
64236 -
人工智能
+關(guān)注
關(guān)注
1791文章
46696瀏覽量
237182 -
python
+關(guān)注
關(guān)注
55文章
4774瀏覽量
84386
發(fā)布評論請先 登錄
相關(guān)推薦
評論