java基础(六)多线程/设计模式
這輩子沒辦法做太多事情,所以每一件都要做到精彩絕倫!
People can't do too many things in my life,so everything will be wonderful???
?
本資料只進行簡單介紹說明以及相關(guān)重要問題的解答;關(guān)于類的詳細說明及使用請參考java API文檔
本文參考的依據(jù)是:JDK API 1.6.0 中文版,下載地址:http://down.51cto.com/data/2300228
1.?????多線程
1.1?????多線程概述
(1)多線程:一個應(yīng)用程序有多條執(zhí)行路徑
?????????????????? 進程:正在執(zhí)行的應(yīng)用程序
?????????????????? 線程:進程的執(zhí)行單元,執(zhí)行路徑
?????????????????? 單線程:一個應(yīng)用程序只有一條執(zhí)行路徑
?????????????????? 多線程:一個應(yīng)用程序有多條執(zhí)行路徑
??????????????????
?????????????????? 多進程的意義?
??????????????????????????? 提高CPU的使用率
?????????????????? 多線程的意義?
??????????????????????????? 提高應(yīng)用程序的使用率
(2)Java程序的運行原理及JVM的啟動是多線程的嗎?
?????????????????? A:Java命令去啟動JVM,JVM會啟動一個進程,該進程會啟動一個主線程。
??? java 命令會啟動java 虛擬機,啟動JVM,等于啟動了一個應(yīng)用程序,也就是啟動了一個進程。該進程會自動啟動一個“主線程” ,然后主線程去調(diào)用某個類的 main 方法。所以main方法運行在主線程中。在此之前的所有程序都是單線程的。
?????????????????? B:JVM的啟動是多線程的,因為它最低有兩個線程啟動了,主線程和垃圾回收線程。
1.2?????多線程實現(xiàn)
1.2.1???????方案一Thread類
l? 通過查看API來學(xué)習(xí)多線程程序的實現(xiàn)
??? 參考Thread類
l? 繼承Thread類
??? 幾個小問題:
??? 為什么要重寫run()方法
??? 啟動線程使用的是那個方法
??? 線程能不能多次啟動
??? run()和start()方法的區(qū)別:
1) start:
用start方法來啟動線程,真正實現(xiàn)了多線程運行,這時無需等待run方法體代碼執(zhí)行完畢而直接繼續(xù)執(zhí)行下面的代碼。通過調(diào)用Thread類的start()方法來啟動一個線程,這時此線程處于就緒(可運行)狀態(tài),并沒有運行,一旦得到spu時間片,就開始執(zhí)行run()方法,這里方法run()稱為線程體,它包含了要執(zhí)行的這個線程的內(nèi)容,Run方法運行結(jié)束,此線程隨即終止。
?
2) run:
run()方法只是類的一個普通方法而已,如果直接調(diào)用Run方法,程序中依然只有主線程這一個線程,其程序執(zhí)行路徑還是只有一條,還是要順序執(zhí)行,還是要等待run方法體執(zhí)行完畢后才可繼續(xù)執(zhí)行下面的代碼,這樣就沒有達到寫線程的目的。
l? Thread類的基本獲取和設(shè)置方法
??? public final String getName()
??? public final void setName(String name)
??? 其實通過構(gòu)造方法也可以給線程起名字
??? 思考:
??? 如何獲取main方法所在的線程名稱呢?
??? public static Thread currentThread()
l?這樣就可以獲取任意方法所在的線程名稱
1.2.2???????方案二Runnable接口
實現(xiàn)Runnable接口
如何獲取線程名稱
如何給線程設(shè)置名稱
實現(xiàn)接口方式的好處
可以避免由于Java單繼承帶來的局限性。
適合多個相同程序的代碼去處理同一個資源的情況,把線程同程序的代碼,數(shù)據(jù)有效分離,較好的體現(xiàn)了面向?qū)ο蟮脑O(shè)計思想。
1.2.3???????方案3實現(xiàn)Callable接口
實現(xiàn)Callable接口
步驟和1.9線程其他概述中的線程池執(zhí)行Runnable對象的差不多。
好處:
可以有返回值
可以拋出異常
弊端:
代碼比較復(fù)雜,所以一般不用
1.3?????線程調(diào)度
假如我們的計算機只有一個 CPU,那么 CPU 在某一個時刻只能執(zhí)行一條指令,線程只有得到 CPU時間片,也就是使用權(quán),才可以執(zhí)行指令。那么Java是如何對線程進行調(diào)用的呢?
線程有兩種調(diào)度模型:
分時調(diào)度模型?? 所有線程輪流使用 CPU 的使用權(quán),平均分配每個線程占用 CPU 的時間片
搶占式調(diào)度模型?? 優(yōu)先讓優(yōu)先級高的線程使用 CPU,如果線程的優(yōu)先級相同,那么會隨機選擇一個,優(yōu)先級高的線程獲取的 CPU 時間片相對多一些。
Java使用的是搶占式調(diào)度模型。
如何設(shè)置和獲取線程優(yōu)先級
public final int getPriority()
public final void setPriority(intnewPriority)
a:默認是5
??????????????????????????? b:范圍是1-10
1.4?????線程控制
線程休眠
public static void sleep(long millis)
線程加入
public final void join()
線程禮讓
public static void yield()
后臺線程
public final void setDaemon(boolean on)
中斷線程
public final void stop()
public void interrupt()
1.5?????線程的生命周期
?
1.6?????多線程案例
某電影院目前正在上映賀歲大片,共有100張票,而它有3個售票窗口售票,請設(shè)計一個程序模擬該電影院售票
代碼:
/*
?*
?* 同步的特點:
?* ??????????? 前提:
?* ???????????????????? 多個線程
?*????????????? 解決問題的時候要注意:
?*?????????????????????? 多個線程使用的是同一個鎖對象
?* 同步的好處
?*????????????? 同步的出現(xiàn)解決了多線程的安全問題。
?* 同步的弊端
?*????????????? 當(dāng)線程相當(dāng)多時,因為每個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會降低程序的運行效率。
?*/
public class SellTicketDemo {
???????? publicstatic void main(String[] args) {
?????????????????? //創(chuàng)建資源對象
?????????????????? SellTicketst = new SellTicket();
?
?????????????????? //創(chuàng)建三個線程對象
?????????????????? Threadt1 = new Thread(st, "窗口1");
?????????????????? Threadt2 = new Thread(st, "窗口2");
?????????????????? Threadt3 = new Thread(st, "窗口3");
?
?????????????????? //啟動線程
?????????????????? t1.start();
?????????????????? t2.start();
?????????????????? t3.start();
???????? }
}
public class SellTicket implements Runnable{
?
???????? //定義100張票
???????? privateint tickets = 100;
?
???????? //定義同一把鎖
???????? privateObject obj = new Object();
?
???????? @Override
???????? publicvoid run() {
?????????????????? while(true) {
??????????????????????????? //t1,t2,t3都能走到這里
??????????????????????????? //假設(shè)t1搶到CPU的執(zhí)行權(quán),t1就要進來
??????????????????????????? //假設(shè)t2搶到CPU的執(zhí)行權(quán),t2就要進來,發(fā)現(xiàn)門是關(guān)著的,進不去。所以就等著。
??????????????????????????? //門(開,關(guān))
??????????????????????????? synchronized(obj) { // 發(fā)現(xiàn)這里的代碼將來是會被鎖上的,所以t1進來后,就鎖了。(關(guān))
???????????????????????????????????? if(tickets > 0) {
?????????????????????????????????????????????? try{
??????????????????????????????????????????????????????? Thread.sleep(100);// t1就睡眠了
?????????????????????????????????????????????? }catch (InterruptedException e) {
??????????????????????????????????????????????????????? e.printStackTrace();
?????????????????????????????????????????????? }
?????????????????????????????????????????????? System.out.println(Thread.currentThread().getName()
???????????????????????????????????????????????????????????????? +"正在出售第" + (tickets--) + "張票 ");
?????????????????????????????????????????????? //窗口1正在出售第100張票
???????????????????????????????????? }
??????????????????????????? }//t1就出來可,然后就開門。(開)
?????????????????? }
???????? }
}
添加:每次賣票延遲100毫秒!
出現(xiàn)問題:相同的票出現(xiàn)多次,還出現(xiàn)了負數(shù)的票
1.7?????多線程安全問題
上述出現(xiàn)的問題
l? 問題
??? 相同的票出現(xiàn)多次
??? CPU的一次操作必須是原子性的
??? 還出現(xiàn)了負數(shù)的票
??? 隨機性和延遲導(dǎo)致的
l? 注意
??? 線程安全問題在理想狀態(tài)下,不容易出現(xiàn),但一旦出現(xiàn)對軟件的影響是非常大的。
解決思路:
l? 首先想為什么出現(xiàn)問題?(也是我們判斷是否有問題的標準)
??? 是否是多線程環(huán)境
??? 是否有共享數(shù)據(jù)
??? 是否有多條語句操作共享數(shù)據(jù)
l? 如何解決多線程安全問題呢?
??? 基本思想:讓程序沒有安全問題的環(huán)境。
??? 怎么實現(xiàn)呢?
??? 把多個語句操作共享數(shù)據(jù)的代碼給鎖起來,讓任意時刻只能有一個線程執(zhí)行即可。
1.7.1???????方案一同步代碼塊
同步代碼塊
格式:
?????????????????? synchronized(對象){需要同步的代碼;}
同步可以解決安全問題的根本原因就在那個對象上。該對象如同鎖的功能。
l? 同步的前提
??? 多個線程
??? 多個線程使用的是同一個鎖對象
l? 同步的好處
??? 同步的出現(xiàn)解決了多線程的安全問題。
l? 同步的弊端
??? 當(dāng)線程相當(dāng)多時,因為每個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會降低程序的運行效率。
1.7.2???????方案二
同步方法
就是把同步關(guān)鍵字加到方法上。
同步方法的鎖對象是什么呢?
這里的鎖對象是this?????
如果是靜態(tài)方法,同步方法的鎖對象又是什么呢?
靜態(tài)同步方法
??????????????????????????? 把同步加在方法上。
??????????????????????????? 這里的鎖對象是當(dāng)前類的字節(jié)碼文件對象
那么,我們到底使用誰?
如果鎖對象是this,就可以考慮使用同步方法。
否則能使用同步代碼塊的盡量使用同步代碼塊。
?
1.8?????Lock鎖
雖然我們可以理解同步代碼塊和同步方法的鎖對象問題,但是我們并沒有直接看到在哪里加上了鎖,在哪里釋放了鎖,為了更清晰的表達如何加鎖和釋放鎖,JDK5以后提供了一個新的鎖對象Lock
Lock
void lock()
void unlock()
ReentrantLock
死鎖問題:
同步弊端
效率低
如果出現(xiàn)了同步嵌套,就容易產(chǎn)生死鎖問題
死鎖問題及其代碼
是指兩個或者兩個以上的線程在執(zhí)行的過程中,因爭奪資源產(chǎn)生的一種互相等待現(xiàn)象
1.9?????線程其他概述
等待喚醒機制
線程間通信
線程的狀態(tài)轉(zhuǎn)換圖
線程組
Java中使用ThreadGroup來表示線程組,它可以對一批線程進行分類管理,Java允許程序直接對線程組進行控制。
默認情況下,所有的線程都屬于主線程組。
public final ThreadGroup getThreadGroup()
我們也可以給線程設(shè)置分組
Thread(ThreadGroup?group,Runnable?target, String?name)
線程池
程序啟動一個新線程成本是比較高的,因為它涉及到要與操作系統(tǒng)進行交互。而使用線程池可以很好的提高性能,尤其是當(dāng)程序中要創(chuàng)建大量生存期很短的線程時,更應(yīng)該考慮使用線程池。
線程池里的每一個線程代碼結(jié)束后,并不會死亡,而是再次回到線程池中成為空閑狀態(tài),等待下一個對象來使用。
在JDK5之前,我們必須手動實現(xiàn)自己的線程池,從JDK5開始,Java內(nèi)置支持線程池
JDK5新增了一個Executors工廠類來產(chǎn)生線程池,有如下幾個方法
??? public static ExecutorService newCachedThreadPool()
??? public static ExecutorService newFixedThreadPool(int nThreads)
??? public static ExecutorService newSingleThreadExecutor()
??? 這些方法的返回值是ExecutorService對象,該對象表示一個線程池,可以執(zhí)行Runnable對象或者Callable對象代表的線程。它提供了如下方法
??? Future<?> submit(Runnable task)
??? <T> Future<T> submit(Callable<T> task)
匿名內(nèi)部類方式使用多線程
匿名內(nèi)部類方式使用多線程
new Thread(){代碼…}.start();
New Thread(new Runnable(){代碼…}).start();
1.10定時器
定時器是一個應(yīng)用十分廣泛的線程工具,可用于調(diào)度多個定時任務(wù)以后臺線程的方式執(zhí)行。在Java中,可以通過Timer和TimerTask類來實現(xiàn)定義調(diào)度的功能
Timer
public Timer()
public void schedule(TimerTask?task,long?delay)
public void schedule(TimerTask task,longdelay,long period)
TimerTask
public abstract void run()
public boolean cancel()
開發(fā)中:
Quartz是一個完全由java編寫的開源調(diào)度框架。一般直接使用spring自帶的定時器。
2.?????設(shè)計模式
設(shè)計模式概述
設(shè)計模式(Design pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類編目的、代碼設(shè)計經(jīng)驗的總結(jié)。使用設(shè)計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。
設(shè)計模式不是一種方法和技術(shù),而是一種思想
設(shè)計模式和具體的語言無關(guān),學(xué)習(xí)設(shè)計模式就是要建立面向?qū)ο蟮乃枷?#xff0c;盡可能的面向接口編程,低耦合,高內(nèi)聚,使設(shè)計的程序可復(fù)用
學(xué)習(xí)設(shè)計模式能夠促進對面向?qū)ο笏枷氲睦斫?#xff0c;反之亦然。它們相輔相成
l?設(shè)計模式的幾個要素
??? 名字 必須有一個簡單,有意義的名字
??? 問題 描述在何時使用模式
??? 解決方案 描述設(shè)計的組成部分以及如何解決問題
??? 效果 描述模式的效果以及優(yōu)缺點
l?設(shè)計模式的分類
??? 創(chuàng)建型模式 對象的創(chuàng)建
??? 結(jié)構(gòu)型模式 對象的組成(結(jié)構(gòu))
??? 行為型模式 對象的行為
?
2.1?????對象的常見設(shè)計原則
(1)面試對象的常見設(shè)計原則(具體含義可參閱《大話設(shè)計模式》或百度)
單一職責(zé):其實就是開發(fā)人員經(jīng)常說的”高內(nèi)聚,低耦合”
也就是說,每個類應(yīng)該只有一個職責(zé),對外只能提供一種功能,而引起類變化的原因應(yīng)該只有一個。在設(shè)計模式中,所有的設(shè)計模式都遵循這一原則。
開閉:核心思想是:一個對象對擴展開放,對修改關(guān)閉。
其實開閉原則的意思就是:對類的改動是通過增加代碼進行的,而不是修改現(xiàn)有代碼。
也就是說軟件開發(fā)人員一旦寫出了可以運行的代碼,就不應(yīng)該去改動它,而是要保證它能一直運行下去,如何能夠做到這一點呢?這就需要借助于抽象和多態(tài),即把可能變化的內(nèi)容抽象出來,從而使抽象的部分是相對穩(wěn)定的,而具體的實現(xiàn)則是可以改變和擴展的
里氏替換:
核心思想:在任何父類出現(xiàn)的地方都可以用它的子類來替代。
其實就是說:同一個繼承體系中的對象應(yīng)該有共同的行為特征。
依賴注入:
核心思想:要依賴于抽象,不要依賴于具體實現(xiàn)。
其實就是說:在應(yīng)用程序中,所有的類如果使用或依賴于其他的類,則應(yīng)該依賴這些其他類的抽象類,而不是這些其他類的具體類。為了實現(xiàn)這一原則,就要求我們在編程的時候針對抽象類或者接口編程,而不是針對具體實現(xiàn)編程。
接口分離:
核心思想:不應(yīng)該強迫程序依賴它們不需要使用的方法。
其實就是說:一個接口不需要提供太多的行為,一個接口應(yīng)該只提供一種對外的功能,不應(yīng)該把所有的操作都封裝到一個接口中。
迪米特:
核心思想:一個對象應(yīng)當(dāng)對其他對象盡可能少的了解
其實就是說:降低各個對象之間的耦合,提高系統(tǒng)的可維護性。在模塊之間應(yīng)該只通過接口編程,而不理會模塊的內(nèi)部工作原理,它可以使各個模塊耦合度降到最低,促進軟件的復(fù)用
2.2?????設(shè)計模式概述和分類
?????????????????? A:經(jīng)驗的總結(jié)
?????????????????? B:三類
??????????????????????????? 創(chuàng)建型
??????????????????????????? 結(jié)構(gòu)型
??????????????????????????? 行為型
改進的設(shè)計模式
A:簡單工廠模式
簡單工廠模式概述
又叫靜態(tài)工廠方法模式,它定義一個具體的工廠類負責(zé)創(chuàng)建一些類的實例
優(yōu)點
客戶端不需要在負責(zé)對象的創(chuàng)建,從而明確了各個類的職責(zé)
缺點
這個靜態(tài)工廠類負責(zé)所有對象的創(chuàng)建,如果有新的對象增加,或者某些對象的創(chuàng)建方式不同,就需要不斷的修改工廠類,不利于后期的維護
B:工廠方法模式
工廠方法模式概述
工廠方法模式中抽象工廠類負責(zé)定義創(chuàng)建對象的接口,具體對象的創(chuàng)建工作由繼承抽象工廠的具體類實現(xiàn)。
優(yōu)點
客戶端不需要在負責(zé)對象的創(chuàng)建,從而明確了各個類的職責(zé),如果有新的對象增加,只需要增加一個具體的類和具體的工廠類即可,不影響已有的代碼,后期維護容易,增強了系統(tǒng)的擴展性
缺點
需要額外的編寫代碼,增加了工作量
C:單例模式
單例設(shè)計思想
保證類在內(nèi)存中只有一個對象
如何實現(xiàn)類在內(nèi)存中只有一個對象呢?
構(gòu)造私有
本身提供一個對象
通過公共的方法讓外界訪問
單例設(shè)計模式分類
l? 餓漢式(開發(fā))
l? 懶漢式(面試)
??? 線程安全問題
??? 懶加載思想(延遲加載)
單例模式應(yīng)用類:Runtime類
D:裝飾者設(shè)計模式
裝飾設(shè)計模式概述
裝飾模式就是使用被裝飾類的一個子類的實例,在客戶端將這個子類的實例交給裝飾類。是繼承的替代方案
優(yōu)點
使用裝飾模式,可以提供比繼承更靈活的擴展對象的功能,它可以動態(tài)的添加對象的功能,并且可以隨意的組合這些功能
缺點
正因為可以隨意組合,所以就可能出現(xiàn)一些不合理的邏輯
?
3.?????面試題
3.1?????多線程有幾種實現(xiàn)方案,分別是哪幾種?
參照1.2
3.2?????同步有幾種方式,分別是什么?
參考1.7
3.3?????啟動一個線程是run()還是start()?它們的區(qū)別?
參考1.2.1
3.4?????sleep()和wait()方法的區(qū)別
1、這兩個方法來自不同的類分別是Thread和Object
2、最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。
3、wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在
任何地方使用(使用范圍)
4、sleep必須捕獲異常,而wait,notify和notifyAll不需要捕獲異常
3.5?????為什么wait(),notify(),notifyAll()等方法都定義在Object類中
1,這些方法存在與同步中。
2,使用這些方法時必須要標識所屬的同步的鎖。
3,鎖可以是任意對象,所以任意對象調(diào)用的方法一定定義Object類中。
3.6?????線程的生命周期圖
參考1.5
java基礎(chǔ)系列:
java基礎(chǔ)(一)java語法
java基礎(chǔ)(二)面向?qū)ο?/span>
java基礎(chǔ)(三)繼承/多態(tài)/接口
java基礎(chǔ)(四)常用類/算法
java基礎(chǔ)(五)集合/IO流/異常
java基礎(chǔ)(六)多線程/設(shè)計模式
java基礎(chǔ)(七)網(wǎng)絡(luò)編程/反射/動態(tài)代理
轉(zhuǎn)載于:https://blog.51cto.com/wyait/1916898
總結(jié)
以上是生活随笔為你收集整理的java基础(六)多线程/设计模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: zookeeper集群自动启动脚本
- 下一篇: asp.net ajax控件工具集 Au