《Java 多線程編程核心技術(shù)》學(xué)習(xí)筆記及總結(jié)

文章來(lái)源:http://www.54tianzhisheng.cn/2017/06/04/Java-Thread/

第一章 —— Java 多線程技能

線程技術(shù)點(diǎn):

線程的啟動(dòng)

如何使線程暫停

如何使線程停止

線程的優(yōu)先級(jí)

線程安全相關(guān)問(wèn)題

進(jìn)程和線程的概念及多線程的優(yōu)點(diǎn)

進(jìn)程:比如我們電腦運(yùn)行的 QQ.exe 程序,是操作系統(tǒng)管理的基本運(yùn)行單元

線程:在進(jìn)程中獨(dú)立運(yùn)行的子任務(wù)倘待,比如 QQ.exe 進(jìn)程中就有很多線程在運(yùn)行,下載文件線程泛鸟、發(fā)送消息線程朴则、語(yǔ)音線程呻引、視頻線程等知残。

多線程優(yōu)點(diǎn):我們電腦可以同時(shí)操作不同的軟件,邊聽(tīng)著歌蝠筑,敲著代碼,查看 pdf 文檔揩懒,瀏覽網(wǎng)頁(yè)等什乙,CPU 在這些任務(wù)之間不停的切換,切換非骋亚颍快臣镣,所以我們就覺(jué)得他們是在同時(shí)運(yùn)行的。

使用多線程

繼承 THREAD 類

JDK 源碼注釋(Thread.java)如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

One is to declare a class to be asubclass(子類)of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started. For example, a thread that computes primes

larger than a stated value could be written as follows:

//繼承 Thread 類

class PrimeThread extends Thread {

longminPrime;

PrimeThread(longminPrime) {

this.minPrime = minPrime;

}

publicvoidrun(){

// compute primes larger than minPrime

重寫 Thread 類的 run 方法

}

}

The following code would then create a thread and start it running:

//開(kāi)啟線程

PrimeThread p =newPrimeThread(143);

p.start();

實(shí)現(xiàn) RUNNABLE 接口

JDK 源碼注釋(Thread.java)如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating

Thread, and started. The same example in this other style looks like the following:

//實(shí)現(xiàn) Runnable 接口

class PrimeRun implements Runnable {

long minPrime;

PrimeRun(long minPrime) {

this.minPrime = minPrime;

}

public void run() {

// compute primes larger than minPrime

//重寫 run 方法

}

}

The following code would then create a thread and start it running:

//開(kāi)啟線程

PrimeRun p = new PrimeRun(143);

new Thread(p).start();

currentThread() 方法

該方法返回代碼段正在被哪個(gè)線程調(diào)用的信息智亮。

isAlive() 方法

判斷當(dāng)前線程是否處于活動(dòng)狀態(tài)(已經(jīng)啟動(dòng)但未終止)

sleep() 方法

在指定的毫秒數(shù)內(nèi)讓當(dāng)前“正在執(zhí)行的線程(this.currentThread() 返回的線程)”休眠(暫停執(zhí)行)忆某。

getId() 方法

獲取線程的唯一標(biāo)識(shí)

停止線程

可以使用Thread.stop()方法,但最好不要用阔蛉,因?yàn)檫@個(gè)方法是不安全的弃舒,已經(jīng)棄用作廢了翰蠢。

大多數(shù)停止一個(gè)線程是使用 Thread.interrupt() 方法

判斷線程是否是停止?fàn)顟B(tài)

interrupted()

1

2

3

4

5

//測(cè)試當(dāng)前線程是否已經(jīng)中斷了杖剪,這個(gè)線程的中斷狀態(tài)會(huì)被這個(gè)方法清除。

//換句話說(shuō)触机,如果連續(xù)兩次調(diào)用了這個(gè)方法颠区,第二次調(diào)用的時(shí)候?qū)?huì)返回 false 削锰,

publicstaticbooleaninterrupted(){

returncurrentThread().isInterrupted(true);

}

isInterrupted()

1

2

3

4

5

6

7

8

9

10

11

//測(cè)試線程是否已經(jīng)中斷了,線程的狀態(tài)不會(huì)受這個(gè)方法的影響

//線程中斷被忽略毕莱,因?yàn)榫€程處于中斷下不處于活動(dòng)狀態(tài)的線程由此返回false的方法反映出來(lái)

publicbooleanisInterrupted(){

returnisInterrupted(false);

}

/**

* Tests if some Thread has been interrupted.? The interrupted state

* is reset or not based on the value of ClearInterrupted that is

* passed.

*/

privatenativebooleanisInterrupted(booleanClearInterrupted);

在沉睡中停止

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

publicclassMyThread2extendsThread

{

@Override

publicvoidrun(){

try{

System.out.println("run start");

Thread.sleep(20000);

System.out.println("run end");

}catch(InterruptedException e) {

System.out.println("run catch "+this.isInterrupted());

e.printStackTrace();

}

}

publicstaticvoidmain(String[] args){

try{

MyThread2 t2 =newMyThread2();

t2.start();

Thread.sleep(200);

t2.interrupt();

}catch(InterruptedException e) {

System.out.println("main catch");

e.printStackTrace();

}

System.out.println("main end");

}

}

運(yùn)行結(jié)果:

1

2

3

4

5

6

run start

main end

runcatchfalse

java.lang.InterruptedException: sleep interrupted

at java.lang.Thread.sleep(Native Method)

at com.zhisheng.thread.thread1.MyThread2.run(MyThread2.java:12)

從運(yùn)行結(jié)果來(lái)看喂窟,如果在 sleep 狀態(tài)下停止某一線程,會(huì)進(jìn)入 catch 語(yǔ)句央串,并清除停止?fàn)顟B(tài)值磨澡,使之變成 false。

在停止中沉睡

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

publicclassMyThread3extendsThread

{

@Override

publicvoidrun(){

try{

System.out.println("run start");

Thread.sleep(20000);

System.out.println("run end");

}catch(InterruptedException e) {

System.out.println("run catch "+this.isInterrupted());

e.printStackTrace();

}

}

publicstaticvoidmain(String[] args){

MyThread3 t3 =newMyThread3();

t3.start();

t3.interrupt();

}

}

運(yùn)行結(jié)果:

1

2

3

4

5

run start

runcatchfalse

java.lang.InterruptedException: sleep interrupted

at java.lang.Thread.sleep(Native Method)

at com.zhisheng.thread.thread1.MyThread3.run(MyThread3.java:12)

能停止的線程 —— 暴力停止

使用 stop() 方法停止線程

暫停線程

可使用 suspend 方法暫停線程质和,使用 resume() 方法恢復(fù)線程的執(zhí)行稳摄。

SUSPEND 和 RESUME 方法的使用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

publicclassMyThread4extendsThread

{

privateinti;

publicintgetI(){

returni;

}

publicvoidsetI(inti){

this.i = i;

}

@Override

publicvoidrun(){

while(true) {

i++;

}

}

publicstaticvoidmain(String[] args)throwsInterruptedException{

MyThread4 t4 =newMyThread4();

t4.start();

System.out.println("A----- "+ System.currentTimeMillis() +" ---- "+ t4.getI());

Thread.sleep(2000);

System.out.println("A----- "+ System.currentTimeMillis() +" ---- "+ t4.getI());

t4.suspend();

Thread.sleep(2000);

t4.resume();

System.out.println("B----- "+ System.currentTimeMillis() +" ---- "+ t4.getI());

Thread.sleep(2000);

System.out.println("B----- "+ System.currentTimeMillis() +" ---- "+ t4.getI());

}

}

從運(yùn)行結(jié)果來(lái)看,線程的確能夠暫停和恢復(fù)饲宿。

但是 suspend 和 resume 方法的缺點(diǎn)就是:不同步厦酬,因?yàn)榫€程的暫停導(dǎo)致數(shù)據(jù)的不同步胆描。

yield 方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

/**

* A hint to the scheduler that the current thread is willing to yield

* its current use of a processor. The scheduler is free to ignore this

* hint.

*

*

Yield is a heuristic attempt to improve relative progression

* between threads that would otherwise over-utilise a CPU. Its use

* should be combined with detailed profiling and benchmarking to

* ensure that it actually has the desired effect.

*

*

It is rarely appropriate to use this method. It may be useful

* for debugging or testing purposes, where it may help to reproduce

* bugs due to race conditions. It may also be useful when designing

* concurrency control constructs such as the ones in the

* {@linkjava.util.concurrent.locks} package.

*/

//暫停當(dāng)前正在執(zhí)行的線程對(duì)象,并執(zhí)行其他線程仗阅。暫停的時(shí)間不確定昌讲。

publicstaticnativevoidyield();

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

publicclassMyThread5extendsThread

{

@Override

publicvoidrun(){

doublestart = System.currentTimeMillis();

for(inti =0; i <200000; i++) {

//yield();//暫停的時(shí)間不確定

i++;

}

doubleend = System.currentTimeMillis();

System.out.println("time is "+(end - start));

}

publicstaticvoidmain(String[] args){

MyThread5? t5 =newMyThread5();

t5.start();

}

}

線程的優(yōu)先級(jí)

設(shè)置優(yōu)先級(jí)的方法:setPriority() 方法

1

2

3

4

5

6

7

8

9

10

11

12

13

publicfinalvoidsetPriority(intnewPriority){

ThreadGroup g;

checkAccess();

if(newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {

thrownewIllegalArgumentException();

}

if((g = getThreadGroup()) !=null) {

if(newPriority > g.getMaxPriority()) {

newPriority = g.getMaxPriority();

}

setPriority0(priority = newPriority);

}

}

不一定優(yōu)先級(jí)高的線程就先執(zhí)行。

守護(hù)線程

當(dāng)進(jìn)程中不存在非守護(hù)線程了减噪,則守護(hù)線程自動(dòng)銷毀短绸。垃圾回收線程就是典型的守護(hù)線程,當(dāng)進(jìn)程中沒(méi)有非守護(hù)線程了筹裕,則垃圾回收線程也就沒(méi)有存在的必要了醋闭,自動(dòng)銷毀。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

/**

* Marks this thread as either a {@linkplain#isDaemon daemon} thread

* or a user thread. The Java Virtual Machine exits when the only

* threads running are all daemon threads.

*

*

This method must be invoked before the thread is started.

*

*@paramon

*? ? ? ? if {@codetrue}, marks this thread as a daemon thread

*@throwsIllegalThreadStateException

*? ? ? ? ? if this thread is {@linkplain#isAlive alive}

*@throwsSecurityException

*? ? ? ? ? if {@link#checkAccess} determines that the current

*? ? ? ? ? thread cannot modify this thread

*/

publicfinalvoidsetDaemon(booleanon){

checkAccess();

if(isAlive()) {

thrownewIllegalThreadStateException();

}

daemon = on;

}

第二章 —— 對(duì)象及變量的并發(fā)訪問(wèn)

技術(shù)點(diǎn):

synchronized 對(duì)象監(jiān)視器為 Object 時(shí)的使用

synchronized 對(duì)象監(jiān)視器為 Class 時(shí)的使用

非線程安全是如何出現(xiàn)的

關(guān)鍵字 volatile 的主要作用

關(guān)鍵字 volatile 與 synchronized 的區(qū)別及使用情況

synchronized 同步方法

方法內(nèi)的變量為線程安全

“非線程安全”問(wèn)題存在于“實(shí)例變量”中朝卒,如果是方法內(nèi)部的私有變量证逻,則不存在“非線程安全”問(wèn)題,所得結(jié)果也就是“線程安全”了抗斤。

實(shí)例變量非線程安全

如果多線程共同訪問(wèn)一個(gè)對(duì)象中的實(shí)例變量囚企,則有可能出現(xiàn)“非線程安全”問(wèn)題。

在兩個(gè)線程訪問(wèn)同一個(gè)對(duì)象中的同步方法時(shí)一定是線程安全的瑞眼。

臟讀

發(fā)生臟讀的情況是在讀取實(shí)例變量時(shí)洞拨,此值已經(jīng)被其他線程更改過(guò)了。

如下例子就可以說(shuō)明负拟,如果不加 synchronized 關(guān)鍵字在 setValue 和 getValue 方法上,就會(huì)出現(xiàn)數(shù)據(jù)臟讀歹河。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

classVarName

{

privateString userName ="A";

privateString password ="AA";

synchronizedpublicvoidsetValue(String userName, String password){

try{

this.userName = userName;

Thread.sleep(500);

this.password = password;

System.out.println("setValue method Thread name is :? "+ Thread.currentThread().getName() +" userName = "+ userName +" password = "+ password);

}catch(InterruptedException e) {

e.printStackTrace();

}

}

//synchronized

publicvoidgetValue(){

System.out.println("getValue method Thread name is :? "+ Thread.currentThread().getName() +" userName = "+ userName +" password = "+ password);

}

}

classThread1extendsThread

{

privateVarName varName;

publicThread1(VarName varName){

this.varName = varName;

}

@Override

publicvoidrun(){

varName.setValue("B","BB");

}

}

publicclassTest

{

publicstaticvoidmain(String[] args)throwsInterruptedException{

VarName v =newVarName();

Thread1 thread1 =newThread1(v);

thread1.start();

Thread.sleep(200);//打印結(jié)果受睡眠時(shí)間的影響

v.getValue();

}

}

SYNCHRONIZED 鎖重入

關(guān)鍵字 synchronized 擁有鎖重入的功能掩浙,也就是在使用 synchronized 時(shí),當(dāng)一個(gè)線程得到一個(gè)對(duì)象鎖后秸歧,再次請(qǐng)求此對(duì)象鎖是可以再次得到該對(duì)象的鎖的厨姚。這也證明了在一個(gè) synchronized 方法/塊的內(nèi)部調(diào)用本類的其他 synchronized 方法/塊時(shí),是永遠(yuǎn)可以得到鎖的键菱。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

classService

{

synchronizedpublicvoidservice1(){

System.out.println("service 1");

service2();

}

synchronizedpublicvoidservice2(){

System.out.println("service 2");

service3();

}

synchronizedpublicvoidservice3(){

System.out.println("service 3");

}

}

classThread2extendsThread

{

@Override

publicvoidrun(){

Service s =newService();

s.service1();

}

}

publicclassTest2

{

publicstaticvoidmain(String[] args){

Thread2 t2 =newThread2();

t2.start();

}

}

運(yùn)行結(jié)果:

1

2

3

service 1

service 2

service 3

同步不具有繼承性

同步不可以繼承谬墙。

synchronized 同步語(yǔ)句塊

SYNCHRONIZED 代碼塊間的同步性

當(dāng)一個(gè)線程訪問(wèn) object 的一個(gè) synchronized(this) 同步代碼塊時(shí),其他線程對(duì)同一個(gè) object 中所有其他 synchronized(this) 同步代碼塊的訪問(wèn)將被阻塞经备,這說(shuō)明 synchronized 使用的 “對(duì)象監(jiān)視器” 是一個(gè)拭抬。

將任意對(duì)象作為對(duì)象監(jiān)視器

多個(gè)線程調(diào)用同一個(gè)對(duì)象中的不同名稱的 synchronized 同步方法或者 synchronized(this) 同步代碼塊時(shí),調(diào)用的效果就是按順序執(zhí)行侵蒙,也就是同步的造虎,阻塞的。

靜態(tài)同步 SYNCHRONIZED 方法與 SYNCHRONIZED(CLASS) 代碼塊

關(guān)鍵字 synchronized 還可以應(yīng)用在 static 靜態(tài)方法上纷闺,如果這樣寫就是對(duì)當(dāng)前的 *.java 文件對(duì)應(yīng)的 Class 類進(jìn)行加鎖算凿。而 synchronized 關(guān)鍵字加到非 static 靜態(tài)方法上就是給對(duì)象加鎖份蝴。

多線程的死鎖

volatile 關(guān)鍵字

作用:使變量在多個(gè)線程間可見(jiàn)。

通過(guò)使用 volatile 關(guān)鍵字氓轰,強(qiáng)制的從公共內(nèi)存中讀取變量的值婚夫。使用 volatile 關(guān)鍵字增加了實(shí)例變量在多個(gè)線程之間的可見(jiàn)性,但 volatile 關(guān)鍵字最致命的缺點(diǎn)就是不支持原子性署鸡。

關(guān)鍵字 synchronized 和 volatile 比較:

關(guān)鍵字 volatile 是線程同步的輕量實(shí)現(xiàn)案糙,所以 volatile 性能肯定要比 synchronized 要好,并且 volatile 只能修飾于變量储玫,而 synchronized 可以修飾方法侍筛,以及代碼塊。

多線程訪問(wèn) volatile 不會(huì)發(fā)生阻塞撒穷,而 synchronized 會(huì)出現(xiàn)阻塞匣椰。

volatile 能保證數(shù)據(jù)的可見(jiàn)性,但不能保證原子性端礼;而 synchronized 可以保證原子性禽笑,也可以間接保證可見(jiàn)性,因?yàn)樗鼤?huì)將私有內(nèi)存和公有內(nèi)存中的數(shù)據(jù)做同步蛤奥。

關(guān)鍵字 volatile 解決的是變量在多個(gè)線程之間的可見(jiàn)性佳镜;而 synchronized 關(guān)鍵字解決的是多個(gè)線程之間訪問(wèn)資源的同步性。

?

第三章 —— 線程間通信

技術(shù)點(diǎn):

使用 wait/notify 實(shí)現(xiàn)線程間的通信

生產(chǎn)者/消費(fèi)者模式的實(shí)現(xiàn)

方法 join 的使用

ThreadLocal 類的使用

等待/通知機(jī)制

wait 使線程停止運(yùn)行凡桥,notify 使停止的線程繼續(xù)運(yùn)行蟀伸。

關(guān)鍵字 synchronized 可以將任何一個(gè) Object 對(duì)象作為同步對(duì)象看待,而 Java 為每個(gè) Object 都實(shí)現(xiàn)了 wait() 和 notify() 方法缅刽,他們必須用在被 synchronized 同步的 Object 的臨界區(qū)內(nèi)啊掏。通過(guò)調(diào)用 wait 方法可以使處于臨界區(qū)內(nèi)的線程進(jìn)入等待狀態(tài),同時(shí)釋放被同步對(duì)象的鎖衰猛。而 notify 操作可以喚醒一個(gè)因調(diào)用了 wait 方法而處于阻塞狀態(tài)的線程迟蜜,使其進(jìn)入就緒狀態(tài)。被重新喚醒的線程會(huì)試圖重新獲得臨界區(qū)的控制權(quán)啡省,繼續(xù)執(zhí)行臨界區(qū)內(nèi) wait 之后的代碼娜睛。

wait 方法可以使調(diào)用該方法的線程釋放共享資源的鎖,從運(yùn)行狀態(tài)退出卦睹,進(jìn)入等待狀態(tài)畦戒,直到再次被喚醒。

notify() 方法可以隨機(jī)喚醒等待對(duì)列中等待同一共享資源的一個(gè)線程结序,并使該線程退出等待狀態(tài)兢交,進(jìn)入可運(yùn)行狀態(tài)。

notifyAll() 方法可以隨機(jī)喚醒等待對(duì)列中等待同一共享資源的所有線程笼痹,并使這些線程退出等待狀態(tài)配喳,進(jìn)入可運(yùn)行狀態(tài)酪穿。

線程狀態(tài)示意圖:

新創(chuàng)建一個(gè)線程對(duì)象后,在調(diào)用它的 start() 方法晴裹,系統(tǒng)會(huì)為此線程分配 CPU 資源被济,使其處于 Runnable(可運(yùn)行)狀態(tài),如果線程搶占到 CPU 資源涧团,此線程就會(huì)處于 Running (運(yùn)行)狀態(tài)

Runnable 和 Running 狀態(tài)之間可以相互切換只磷,因?yàn)榫€程有可能運(yùn)行一段時(shí)間后,有其他優(yōu)先級(jí)高的線程搶占了 CPU 資源泌绣,此時(shí)線程就從 Running 狀態(tài)變成了 Runnable 狀態(tài)钮追。

線程進(jìn)入 Runnable 狀態(tài)有如下幾種情況:

調(diào)用 sleep() 方法后經(jīng)過(guò)的時(shí)間超過(guò)了指定的休眠時(shí)間

線程調(diào)用的阻塞 IO 已經(jīng)返回,阻塞方法執(zhí)行完畢

線程成功的獲得了試圖同步的監(jiān)視器

線程正在等待某個(gè)通知阿迈,其他線程發(fā)出了通知

處于掛狀態(tài)的線程調(diào)用了 resume 恢復(fù)方法

Blocked 是阻塞的意思元媚,例如線程遇到一個(gè) IO 操作,此時(shí) CPU 處于空閑狀態(tài)苗沧,可能會(huì)轉(zhuǎn)而把 CPU 時(shí)間片分配給其他線程刊棕,這時(shí)也可以稱為 “暫停”狀態(tài)待逞。Blocked 狀態(tài)結(jié)束之后甥角,進(jìn)入 Runnable 狀態(tài),等待系統(tǒng)重新分配資源识樱。

出現(xiàn)阻塞狀態(tài)的有如下幾種情況:

線程調(diào)用 sleep 方法嗤无,主動(dòng)放棄占用的處理器資源

線程調(diào)用了阻塞式 IO 方法,在該方法返回之前怜庸,該線程被阻塞

線程試圖獲得一個(gè)同步監(jiān)視器当犯,但該同步監(jiān)視器正在被其他線程所持有

線程等待某個(gè)通知

程序調(diào)用了 suspend 方法將該線程掛起

run 方法運(yùn)行結(jié)束后進(jìn)入銷毀階段,整個(gè)線程執(zhí)行完畢休雌。

生產(chǎn)者/消費(fèi)者模式實(shí)現(xiàn)

一個(gè)生產(chǎn)者,一個(gè)消費(fèi)者

存儲(chǔ)值對(duì)象:

1

2

3

4

5

6

7

8

9

10

packagecom.zhisheng.thread.thread5;

/**

* Created by 10412 on 2017/6/3.

* 存儲(chǔ)值對(duì)象

*/

publicclassValueObject

{

publicstaticString value ="";

}

生產(chǎn)者:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

packagecom.zhisheng.thread.thread5;

/**

* Created by 10412 on 2017/6/3.

* 生產(chǎn)者

*/

publicclassProduct

{

privateString lock;

publicProduct(String lock){

this.lock = lock;

}

publicvoidsetValue(){

synchronized(lock) {

if(!ValueObject.value.equals("")) {

try{

lock.wait();

}catch(InterruptedException e) {

e.printStackTrace();

}

}

String value = System.currentTimeMillis() +"_"+ System.nanoTime();

System.out.println("生產(chǎn)者 set 的值是:"+ value);

ValueObject.value = value;

lock.notify();

}

}

}

消費(fèi)者:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

packagecom.zhisheng.thread.thread5;

/**

* Created by 10412 on 2017/6/3.

* 消費(fèi)者

*/

publicclassResume

{

privateString lock;

publicResume(String lock){

this.lock = lock;

}

publicvoidgetValue(){

synchronized(lock) {

if(ValueObject.value.equals("")) {

try{

lock.wait();

}catch(InterruptedException e) {

e.printStackTrace();

}

}

System.out.println("消費(fèi)者 get 的值:"+ ValueObject.value);

ValueObject.value ="";

lock.notify();

}

}

}

生產(chǎn)者線程:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

packagecom.zhisheng.thread.thread5;

/**

* Created by 10412 on 2017/6/3.

* 生產(chǎn)者線程

*/

publicclassProductThreadextendsThread

{

privateProduct p;

publicProductThread(Product p){

this.p = p;

}

@Override

publicvoidrun(){

while(true) {

p.setValue();

}

}

}

消費(fèi)者線程:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

packagecom.zhisheng.thread.thread5;

/**

* Created by 10412 on 2017/6/3.

* 消費(fèi)者線程

*/

publicclassResumeThreadextendsThread

{

privateResume r;

publicResumeThread(Resume r){

this.r = r;

}

@Override

publicvoidrun(){

while(true) {

r.getValue();

}

}

}

主函數(shù):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

packagecom.zhisheng.thread.thread5;

/**

* Created by 10412 on 2017/6/3.

* 一個(gè)生產(chǎn)者一個(gè)消費(fèi)者測(cè)試

*/

publicclassTest

{

publicstaticvoidmain(String[] args){

String str =newString("");

Product p =newProduct(str);

Resume r =newResume(str);;

ProductThread pt =newProductThread(p);

ResumeThread rt =newResumeThread(r);

pt.start();

rt.start();

}

}

題目:創(chuàng)建20個(gè)線程肝断,其中10個(gè)線程是將數(shù)據(jù)備份到數(shù)據(jù)庫(kù)A杈曲,另外10個(gè)線程將數(shù)據(jù)備份到數(shù)據(jù)庫(kù)B中去,并且備份數(shù)據(jù)庫(kù)A和備份數(shù)據(jù)庫(kù)B是交叉進(jìn)行的胸懈。

工具類:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

packagecom.zhisheng.thread.thread6;

/**

* Created by 10412 on 2017/6/3.

* 創(chuàng)建20個(gè)線程担扑,其中10個(gè)線程是將數(shù)據(jù)備份到數(shù)據(jù)庫(kù)A,另外10個(gè)線程將數(shù)據(jù)備份到數(shù)據(jù)庫(kù)B中去趣钱,并且

* 備份數(shù)據(jù)庫(kù)A和備份數(shù)據(jù)庫(kù)B是交叉進(jìn)行的

*/

publicclassDBTools

{

volatileprivatebooleanprevIsA =false;

//確保A備份先進(jìn)行

synchronizedpublicvoidbackA(){

while(prevIsA ==true) {

try{

wait();

}catch(InterruptedException e) {

e.printStackTrace();

}

}

for(inti =0; i <5; i++) {

System.out.println("AAAAA");

}

prevIsA =true;

notifyAll();

}

synchronizedpublicvoidbackB(){

while(prevIsA ==false) {

try{

wait();

}catch(InterruptedException e) {

e.printStackTrace();

}

}

for(inti =0; i <5; i++) {

System.out.println("BBBBB");

}

prevIsA =false;

notifyAll();

}

}

備份A先線程:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

packagecom.zhisheng.thread.thread6;

/**

* Created by 10412 on 2017/6/3.

*/

publicclassThreadAextendsThread

{

privateDBTools dbTools;

publicThreadA(DBTools dbTools){

this.dbTools = dbTools;

}

@Override

publicvoidrun(){

dbTools.backA();

}

}

備份B線程:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

packagecom.zhisheng.thread.thread6;

/**

* Created by 10412 on 2017/6/3.

*/

publicclassThreadBextendsThread

{

privateDBTools dbTools;

publicThreadB(DBTools dbTools){

this.dbTools = dbTools;

}

@Override

publicvoidrun(){

dbTools.backB();

}

}

測(cè)試:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

packagecom.zhisheng.thread.thread6;

/**

* Created by 10412 on 2017/6/3.

*/

publicclassTest

{

publicstaticvoidmain(String[] args){

DBTools dbTools =newDBTools();

for(inti =0; i <20; i++) {

ThreadB tb =newThreadB(dbTools);

tb.start();

ThreadA ta =newThreadA(dbTools);

ta.start();

}

}

}

Join 方法的使用

作用:等待線程對(duì)象銷毀

join 方法具有使線程排隊(duì)運(yùn)行的作用涌献,有些類似同步的運(yùn)行效果。join 與 synchronized 的區(qū)別是:join 在內(nèi)部使用 wait() 方法進(jìn)行等待首有,而 synchronized 關(guān)鍵字使用的是 “對(duì)象監(jiān)視器” 原理做為同步燕垃。

在 join 過(guò)程中枢劝,如果當(dāng)前線程對(duì)象被中斷,則當(dāng)前線程出現(xiàn)異常卜壕。

方法 join(long) 中的參數(shù)是設(shè)定等待的時(shí)間您旁。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

/**

* 等待該線程終止的時(shí)間最長(zhǎng)為 millis 毫秒。超時(shí)為 0 意味著要一直等下去轴捎。

* Waits at most {@codemillis} milliseconds for this thread to

* die. A timeout of {@code0} means to wait forever.

*

*

This implementation uses a loop of {@codethis.wait} calls

* conditioned on {@codethis.isAlive}. As a thread terminates the

* {@codethis.notifyAll} method is invoked. It is recommended that

* applications not use {@codewait}, {@codenotify}, or

* {@codenotifyAll} on {@codeThread} instances.

*

*@parammillis

*? ? ? ? the time to wait in milliseconds

*

*@throwsIllegalArgumentException

*? ? ? ? ? if the value of {@codemillis} is negative

*

*@throwsInterruptedException

*? ? ? ? ? if any thread has interrupted the current thread. The

*? ? ? ? ? interrupted status of the current thread is

*? ? ? ? ? cleared when this exception is thrown.

*/

publicfinalsynchronizedvoidjoin(longmillis)

throwsInterruptedException {

longbase = System.currentTimeMillis();

longnow =0;

if(millis <0) {

thrownewIllegalArgumentException("timeout value is negative");

if(millis ==0) {

while(isAlive()) {

wait(0);

}

}else{

while(isAlive()) {

longdelay = millis - now;

if(delay <=0) {

break;

}

wait(delay);

now = System.currentTimeMillis() - base;

}

}

}

類 ThreadLocal 的使用

該類提供了線程局部 (thread-local) 變量鹤盒。這些變量不同于它們的普通對(duì)應(yīng)物,因?yàn)樵L問(wèn)某個(gè)變量(通過(guò)其 get 或

set 方法)的每個(gè)線程都有自己的局部變量侦副,它獨(dú)立于變量的初始化副本侦锯。ThreadLocal 實(shí)例通常是類中的

private static 字段,它們希望將狀態(tài)與某一個(gè)線程(例如秦驯,用戶 ID 或事務(wù) ID)相關(guān)聯(lián)尺碰。

GET() 方法

1

2

3

4

5

6

7

8

9

10

11

12

13

publicTget(){

Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t);

if(map !=null) {

ThreadLocalMap.Entry e = map.getEntry(this);

if(e !=null) {

@SuppressWarnings("unchecked")

T result = (T)e.value;

returnresult;

}

}

returnsetInitialValue();

}

返回此線程局部變量的當(dāng)前線程副本中的值。如果變量沒(méi)有用于當(dāng)前線程的值汇竭,則先將其初始化為調(diào)用 initialValue() 方法返回的值葱蝗。

InheritableThreadLocal 類的使用

該類擴(kuò)展了 ThreadLocal,為子線程提供從父線程那里繼承的值:在創(chuàng)建子線程時(shí)细燎,子線程會(huì)接收所有可繼承的線程局部變量的初始值两曼,以獲得父線程所具有的值。通常玻驻,子線程的值與父線程的值是一致的悼凑;但是,通過(guò)重寫這個(gè)類中的 childValue 方法璧瞬,子線程的值可以作為父線程值的一個(gè)任意函數(shù)户辫。

當(dāng)必須將變量(如用戶 ID 和 事務(wù) ID)中維護(hù)的每線程屬性(per-thread-attribute)自動(dòng)傳送給創(chuàng)建的所有子線程時(shí),應(yīng)盡可能地采用可繼承的線程局部變量嗤锉,而不是采用普通的線程局部變量反浓。

第四章 —— Lock 的使用

使用 ReentrantLock 類

一個(gè)可重入的互斥鎖 Lock,它具有與使用synchronized方法和語(yǔ)句所訪問(wèn)的隱式監(jiān)視器鎖相同的一些基本行為和語(yǔ)義霞捡,但功能更強(qiáng)大手销。

ReentrantLock將由最近成功獲得鎖,并且還沒(méi)有釋放該鎖的線程所擁有访诱。當(dāng)鎖沒(méi)有被另一個(gè)線程所擁有時(shí)垫挨,調(diào)用lock的線程將成功獲取該鎖并返回。如果當(dāng)前線程已經(jīng)擁有該鎖触菜,此方法將立即返回九榔。可以使用isHeldByCurrentThread()和getHoldCount()方法來(lái)檢查此情況是否發(fā)生。

此類的構(gòu)造方法接受一個(gè)可選的公平參數(shù)哲泊。當(dāng)設(shè)置為true時(shí)剩蟀,在多個(gè)線程的爭(zhēng)用下,這些鎖傾向于將訪問(wèn)權(quán)授予等待時(shí)間最長(zhǎng)的線程攻旦。否則此鎖將無(wú)法保證任何特定訪問(wèn)順序喻旷。與采用默認(rèn)設(shè)置(使用不公平鎖)相比,使用公平鎖的程序在許多線程訪問(wèn)時(shí)表現(xiàn)為很低的總體吞吐量(即速度很慢牢屋,常常極其慢)且预,但是在獲得鎖和保證鎖分配的均衡性時(shí)差異較小。不過(guò)要注意的是烙无,公平鎖不能保證線程調(diào)度的公平性锋谐。因此,使用公平鎖的眾多線程中的一員可能獲得多倍的成功機(jī)會(huì)截酷,這種情況發(fā)生在其他活動(dòng)線程沒(méi)有被處理并且目前并未持有鎖時(shí)涮拗。還要注意的是,未定時(shí)的tryLock方法并沒(méi)有使用公平設(shè)置迂苛。因?yàn)榧词蛊渌€程正在等待三热,只要該鎖是可用的,此方法就可以獲得成功三幻。

建議總是立即實(shí)踐就漾,使用lock塊來(lái)調(diào)用try,在之前/之后的構(gòu)造中念搬,最典型的代碼如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

classX{

privatefinalReentrantLock lock =newReentrantLock();

// ...

publicvoidm(){

lock.lock();// block until condition holds

try{

// ... method body

}finally{

lock.unlock()

}

}

}

Condition

Condition 將 Object 監(jiān)視器方法(wait抑堡、notify 和 notifyAll)分解成截然不同的對(duì)象,以便通過(guò)將這些對(duì)象與任意 Lock 實(shí)現(xiàn)組合使用朗徊,為每個(gè)對(duì)象提供多個(gè)等待 set(wait-set)首妖。其中,Lock 替代了 synchronized 方法和語(yǔ)句的使用爷恳,Condition 替代了 Object 監(jiān)視器方法的使用有缆。

假定有一個(gè)綁定的緩沖區(qū),它支持 put 和 take 方法温亲。如果試圖在空的緩沖區(qū)上執(zhí)行 take 操作棚壁,則在某一個(gè)項(xiàng)變得可用之前,線程將一直阻塞铸豁;如果試圖在滿的緩沖區(qū)上執(zhí)行 put 操作灌曙,則在有空間變得可用之前菊碟,線程將一直阻塞节芥。我們喜歡在單獨(dú)的等待 set 中保存 put 線程和 take 線程,這樣就可以在緩沖區(qū)中的項(xiàng)或空間變得可用時(shí)利用最佳規(guī)劃,一次只通知一個(gè)線程头镊◎纪眨可以使用兩個(gè) Condition 實(shí)例來(lái)做到這一點(diǎn)。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

classBoundedBuffer{

finalLock lock =newReentrantLock();

finalCondition notFull? = lock.newCondition();

finalCondition notEmpty = lock.newCondition();

finalObject[] items =newObject[100];

intputptr, takeptr, count;

publicvoidput(Object x)throwsInterruptedException{

lock.lock();

try{

while(count == items.length)

notFull.await();

items[putptr] = x;

if(++putptr == items.length) putptr =0;

++count;

notEmpty.signal();

}finally{

lock.unlock();

}

}

publicObjecttake()throwsInterruptedException{

lock.lock();

try{

while(count ==0)

notEmpty.await();

Object x = items[takeptr];

if(++takeptr == items.length) takeptr =0;

--count;

notFull.signal();

returnx;

}finally{

lock.unlock();

}

}

}

正確使用 Condition 實(shí)現(xiàn)等待/通知

MyService.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

packagecom.zhisheng.thread.Thread9;

importjava.util.concurrent.locks.Condition;

importjava.util.concurrent.locks.Lock;

importjava.util.concurrent.locks.ReentrantLock;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassMyService

{

privateLock lock =newReentrantLock();

privateCondition condition = lock.newCondition();

publicvoidawait(){

lock.lock();

try{

System.out.println("await A");

condition.await();//使當(dāng)前執(zhí)行的線程處于等待狀態(tài) waiting

System.out.println("await B");

}catch(InterruptedException e) {

e.printStackTrace();

}finally{

lock.unlock();

System.out.println("釋放鎖");

}

}

publicvoidsignal(){

lock.lock();

System.out.println("signal A");

condition.signal();

System.out.println("signal B");

lock.unlock();

}

}

ThreadA.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

packagecom.zhisheng.thread.Thread9;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassThreadAextendsThread

{

privateMyService service;

publicThreadA(MyService service){

this.service = service;

}

@Override

publicvoidrun(){

service.await();

}

}

Test.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

packagecom.zhisheng.thread.Thread9;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassTest

{

publicstaticvoidmain(String[] args)throwsInterruptedException{

MyService service =newMyService();

ThreadA ta =newThreadA(service);

ta.start();

Thread.sleep(5000);

service.signal();

}

}

運(yùn)行結(jié)果:

1

2

3

4

5

await A

signal A

signal B

await B

釋放鎖

Object 類中的 wait() 方法相當(dāng)于 Condition 類中 await() 方法

Object 類中的 wait(long time) 方法相當(dāng)于 Condition 類中 await(long time, TimeUnit unit) 方法

Object 類中的 notify() 方法相當(dāng)于 Condition 類中 signal() 方法

Object 類中的 notifyAll() 方法相當(dāng)于 Condition 類中 signalAll() 方法

題目:實(shí)現(xiàn)生產(chǎn)者與消費(fèi)者 一對(duì)一交替打印

MyService.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

packagecom.zhisheng.thread.thread10;

importjava.util.concurrent.locks.Condition;

importjava.util.concurrent.locks.Lock;

importjava.util.concurrent.locks.ReentrantLock;

/**

* Created by 10412 on 2017/6/4.

* 實(shí)現(xiàn)生產(chǎn)者與消費(fèi)者? 一對(duì)一·交替打印

*/

publicclassMyService

{

privateLock lock =newReentrantLock();

privateCondition condition = lock.newCondition();

privatebooleanflag =false;

publicvoidsetValue(){

lock.lock();

while(flag ==true) {

try{

condition.await();

}catch(InterruptedException e) {

e.printStackTrace();

}

}

System.out.println("SetValue? AAAAAA");

flag =true;

condition.signal();

lock.unlock();

}

publicvoidgetValue(){

lock.lock();

while(flag ==false) {

try{

condition.await();

}catch(InterruptedException e) {

e.printStackTrace();

}

}

System.out.println("GetValue BBBB");

flag =false;

condition.signal();

lock.unlock();

}

}

ThreadA.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

packagecom.zhisheng.thread.thread10;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassThreadAextendsThread

{

privateMyService service;

publicThreadA(MyService service){

this.service = service;

}

@Override

publicvoidrun(){

for(inti =0; i < Integer.MAX_VALUE; i++) {

service.setValue();

}

}

}

ThreadB.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

packagecom.zhisheng.thread.thread10;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassThreadBextendsThread

{

privateMyService service;

publicThreadB(MyService service){

this.service = service;

}

@Override

publicvoidrun(){

for(inti =0; i < Integer.MAX_VALUE; i++) {

service.getValue();

}

}

}

Test.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

packagecom.zhisheng.thread.thread10;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassTest

{

publicstaticvoidmain(String[] args){

MyService service =newMyService();

ThreadA ta =newThreadA(service);

ThreadB tb =newThreadB(service);

ta.start();

tb.start();

}

}

getHoldCount() 查詢當(dāng)前線程保持此鎖定的個(gè)數(shù)相艇,也就是調(diào)用 lock() 的方法

getQueueLength() 返回正等待獲取此鎖定的線程估計(jì)數(shù)

getWaitQueueLength() 返回等待與此鎖定相關(guān)的給定條件 Condition 的線程估計(jì)數(shù)

hasQueuedThread() 查詢指定的線程是否正在等待獲取此鎖定

hasQueuedThreads() 查詢是否有線程正在等待獲取此鎖定

hasWaiters() 查詢是否有線程正在等待與此鎖定有關(guān)的 condition 條件

isFair() 判斷是否是公平鎖(默認(rèn)下 ReentrantLock類使用的是非公平鎖)

isHeldByCurrentThread() 查詢當(dāng)前線程是否保持此鎖定

isLocked() 查詢此鎖定是否由任意線程保持

lockInterruptibly() 如果當(dāng)前線程未被中斷颖杏,則獲取鎖定,如果已經(jīng)被中斷則出現(xiàn)異常

tryLock() 僅在調(diào)用時(shí)鎖定未被另一個(gè)線程保持的情況下坛芽,才獲取該鎖定

tryLock(long time, TimeUtil util) 如果鎖定在給定的等待時(shí)間內(nèi)沒(méi)有被另一個(gè)線程保持留储,且當(dāng)前線程未被中斷,則獲取該鎖定咙轩。

使用 ReentrantReadWriteLock 類

讀寫互斥:

MyService.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

packagecom.zhisheng.thread.Thread11;

importjava.util.concurrent.locks.ReentrantReadWriteLock;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassMyService

{

privateReentrantReadWriteLock lock =newReentrantReadWriteLock();

publicvoidread(){

lock.readLock().lock();

System.out.println(Thread.currentThread().getName() +" Read AAA? "+ System.currentTimeMillis());

try{

Thread.sleep(10000);

}catch(InterruptedException e) {

e.printStackTrace();

}

lock.readLock().unlock();

}

publicvoidwrite(){

lock.writeLock().lock();

System.out.println(Thread.currentThread().getName() +" write BBB "+ System.currentTimeMillis());

try{

Thread.sleep(10000);

}catch(InterruptedException e) {

e.printStackTrace();

}

lock.writeLock().unlock();

}

}

ThreadA.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

packagecom.zhisheng.thread.Thread11;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassThreadAextendsThread

{

privateMyService service;

publicThreadA(MyService service){

this.service = service;

}

@Override

publicvoidrun(){

service.read();

}

}

ThreadB.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

packagecom.zhisheng.thread.Thread11;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassThreadBextendsThread

{

privateMyService service;

publicThreadB(MyService service){

this.service = service;

}

@Override

publicvoidrun(){

service.write();

}

}

Test.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

packagecom.zhisheng.thread.Thread11;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassTest

{

publicstaticvoidmain(String[] args)throwsInterruptedException{

MyService service =newMyService();

ThreadA ta =newThreadA(service);

ta.setName("A");

ta.start();

Thread.sleep(1000);

ThreadB tb =newThreadB(service);

tb.setName("B");

tb.start();

}

}

運(yùn)行結(jié)果:

1

2

A Read AAA? 1496556770402

B write BBB 1496556780402

第六章 —— 單例模式與多線程

推薦文章《深入淺出單實(shí)例Singleton設(shè)計(jì)模式》

立即加載模式 / “餓漢模式”

立即加載:使用類的時(shí)候已經(jīng)將對(duì)象創(chuàng)建完畢获讳,new 實(shí)例化

1

2

3

4

5

6

7

8

9

publicclassMyObject

{

privatestaticMyObject object =newMyObject();

privateMyObject(){

}

publicstaticMyObjectgetInstance(){

returnobject;

}

}

延遲加載 / “ 懶漢模式 ”

就是在調(diào)用 get 的時(shí)候?qū)嵗疟粍?chuàng)建。在 get() 方法中進(jìn)行 new 實(shí)例化活喊。

1

2

3

4

5

6

7

8

9

10

11

12

13

publicclassMyObject

{

privatestaticMyObject object;

privateMyObject(){

}

publicstaticMyObjectgetInstance(){

if(object !=null) {

}else{

object =newMyObject();

}

returnobject;

}

}

使用 DCL 雙重檢查鎖丐膝,解決“懶漢模式”遇到的多線程問(wèn)題

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

publicclassMyObject

{

privatevolatilestaticMyObject object;

privateMyObject(){

}

//synchronized

publicstaticMyObjectgetInstance(){

if(object !=null) {

}else{

synchronized(MyObject.class) {

if(object ==null) {

object =newMyObject();

}

}

}

returnobject;

}

}

使用靜態(tài)內(nèi)部類實(shí)現(xiàn)單例模式

1

2

3

4

5

6

7

8

9

10

11

12

publicclassMyObject

{

privatestaticclassMyObjectHandler

{

privatestaticMyObject object =newMyObject();

}

privateMyObject(){

}

publicstaticMyObjectgetInstance(){

returnMyObjectHandler.object;

}

}

序列化與反序列化的單例模式實(shí)現(xiàn)

MyObject.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

packagecom.zhisheng.thread.thread15;

importjava.io.ObjectStreamException;

importjava.io.Serializable;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassMyObjectimplementsSerializable

{

privatestaticfinallongserialVersionUID =888L;

privatestaticclassMyObjectHandler

{

privatestaticfinalMyObject object =newMyObject();

}

privateMyObject(){

}

publicstaticMyObjectgetInstance(){

returnMyObjectHandler.object;

}

protectedObjectreadResolve()throwsObjectStreamException{

System.out.println("調(diào)用了readResolve方法!");

returnMyObjectHandler.object;

}

}

SaveAndRead.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

packagecom.zhisheng.thread.thread15;

importjava.io.*;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassSaveAndRead

{

publicstaticvoidmain(String[] args){

try{

MyObject object = MyObject.getInstance();

FileOutputStream fos =newFileOutputStream(newFile("fos.txt"));

ObjectOutputStream oos =newObjectOutputStream(fos);

oos.writeObject(object);

oos.close();

fos.close();

System.out.println(object.hashCode());

}catch(FileNotFoundException e) {

e.printStackTrace();

}catch(IOException e) {

e.printStackTrace();

}

try{

FileInputStream fis =newFileInputStream(newFile("fos.txt"));

ObjectInputStream ois =newObjectInputStream(fis);

MyObject o = (MyObject) ois.readObject();

ois.close();

fis.close();

System.out.println(o.hashCode());

}catch(FileNotFoundException e) {

e.printStackTrace();

}catch(IOException e) {

e.printStackTrace();

}catch(ClassNotFoundException e) {

e.printStackTrace();

}

}

}

這里主要要指出 MyObject.java 中 readResolve 方法

1

2

3

4

protectedObjectreadResolve()throwsObjectStreamException{

System.out.println("調(diào)用了readResolve方法钾菊!");

returnMyObjectHandler.object;

}

方法 readResolve 允許 class 在反序列化返回對(duì)象前替換帅矗、解析在流中讀出來(lái)的對(duì)象。實(shí)現(xiàn) readResolve 方法煞烫,一個(gè) class 可以直接控制反序化返回的類型和對(duì)象引用浑此。

方法 readResolve 會(huì)在 ObjectInputStream 已經(jīng)讀取一個(gè)對(duì)象并在準(zhǔn)備返回前調(diào)用。ObjectInputStream 會(huì)檢查對(duì)象的 class 是否定義了 readResolve 方法红竭。如果定義了尤勋,將由 readResolve 方法指定返回的對(duì)象。返回對(duì)象的類型一定要是兼容的茵宪,否則會(huì)拋出 ClassCastException 最冰。

使用 static 代碼塊實(shí)現(xiàn)單例模式

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

packagecom.zhisheng.thread.thread16;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassMyObject

{

privatestaticMyObject instance =null;

privateMyObject(){

}

static{

instance =newMyObject();

}

publicstaticMyObjectgetInstance(){

returninstance;

}

}

ThreadA.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

packagecom.zhisheng.thread.thread16;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassThreadAextendsThread

{

@Override

publicvoidrun(){

for(inti =0; i <5; i++) {

System.out.println(MyObject.getInstance().hashCode());

}

}

}

Test.java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

packagecom.zhisheng.thread.thread16;

/**

* Created by 10412 on 2017/6/4.

*/

publicclassTest

{

publicstaticvoidmain(String[] args){

ThreadA ta1 =newThreadA();

ThreadA ta2 =newThreadA();

ThreadA ta3 =newThreadA();

ta1.start();

ta2.start();

ta3.start();

}

}

使用枚舉數(shù)據(jù)類型實(shí)現(xiàn)單例模式

在使用枚舉類時(shí),構(gòu)造方法會(huì)被自動(dòng)調(diào)用稀火,也可以應(yīng)用這個(gè)特性實(shí)現(xiàn)單例模式暖哨。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

publicclassMyObject{

privateenumMyEnumSingleton{

INSTANCE;

privateResource resource;

privateMyEnumSingleton(){

resource =newResource();

}

publicResourcegetResource(){

returnresource;

}

}

publicstaticResourcegetResource(){

returnMyEnumSingleton.INSTANCE.getResource();

}

}

測(cè)試:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

importtest.MyObject;

publicclassRun{

classMyThreadextendsThread{

@Override

publicvoidrun(){

for(inti =0; i <5; i++) {

System.out.println(MyObject.getResource().hashCode());

}

}

}

publicstaticvoidmain(String[] args){

Run.MyThread t1 =newRun().new MyThread();

Run.MyThread t2 =newRun().new MyThread();

Run.MyThread t3 =newRun().new MyThread();

t1.start();

t2.start();

t3.start();

}

}

這里再推薦一篇 stackoverflow 上的一個(gè)問(wèn)題回答:What is an efficient way to implement a singleton pattern in Java?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市凰狞,隨后出現(xiàn)的幾起案子篇裁,更是在濱河造成了極大的恐慌,老刑警劉巖赡若,帶你破解...
    沈念sama閱讀 223,207評(píng)論 6 521
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件达布,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡逾冬,警方通過(guò)查閱死者的電腦和手機(jī)黍聂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,455評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門躺苦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人产还,你說(shuō)我怎么就攤上這事匹厘。” “怎么了脐区?”我有些...
    開(kāi)封第一講書人閱讀 170,031評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵愈诚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我牛隅,道長(zhǎng)炕柔,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 60,334評(píng)論 1 300
  • 正文 為了忘掉前任媒佣,我火速辦了婚禮汗唱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘丈攒。我一直安慰自己哩罪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,322評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布巡验。 她就那樣靜靜地躺著际插,像睡著了一般。 火紅的嫁衣襯著肌膚如雪显设。 梳的紋絲不亂的頭發(fā)上框弛,一...
    開(kāi)封第一講書人閱讀 52,895評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音捕捂,去河邊找鬼瑟枫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛指攒,可吹牛的內(nèi)容都是我干的慷妙。 我是一名探鬼主播,決...
    沈念sama閱讀 41,300評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼允悦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼膝擂!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起隙弛,我...
    開(kāi)封第一講書人閱讀 40,264評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤架馋,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后全闷,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體叉寂,經(jīng)...
    沈念sama閱讀 46,784評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,870評(píng)論 3 343
  • 正文 我和宋清朗相戀三年总珠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屏鳍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伊约。...
    茶點(diǎn)故事閱讀 40,989評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖孕蝉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腌逢,我是刑警寧澤降淮,帶...
    沈念sama閱讀 36,649評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站搏讶,受9級(jí)特大地震影響佳鳖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜媒惕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,331評(píng)論 3 336
  • 文/蒙蒙 一系吩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧妒蔚,春花似錦穿挨、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,814評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至菜皂,卻和暖如春贞绵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背恍飘。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,940評(píng)論 1 275
  • 我被黑心中介騙來(lái)泰國(guó)打工榨崩, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人章母。 一個(gè)月前我還...
    沈念sama閱讀 49,452評(píng)論 3 379
  • 正文 我出身青樓母蛛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親乳怎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子溯祸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,995評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容