目錄
什么是設(shè)計(jì)模式?
單例模式
餓漢模式
懶漢模式
工廠(chǎng)模式
線(xiàn)程池
線(xiàn)程池種類(lèi)
ThreadPoolExcutor的構(gòu)造方法:
手動(dòng)實(shí)現(xiàn)一個(gè)線(xiàn)程池?
什么是設(shè)計(jì)模式?
計(jì)算機(jī)行業(yè)程序員水平層次不齊,為了讓所有人都能夠?qū)懗鲆?guī)范的代碼,于是就有了設(shè)計(jì)模式,針對(duì)一些典型的場(chǎng)景,給出一些典型的解決方案
單例模式
單例模式 ==> 單個(gè)實(shí)例(對(duì)象)
在一些場(chǎng)景中,有的特定類(lèi)只能創(chuàng)建一個(gè)實(shí)例,不能創(chuàng)建多個(gè)實(shí)例
使用了單例模式后,此時(shí)就不能創(chuàng)建多個(gè)實(shí)例了,我們想創(chuàng)建多個(gè)實(shí)例都難,單例模式就是針對(duì)上述的需求場(chǎng)景進(jìn)行了更強(qiáng)制的保證,通過(guò)巧用java的語(yǔ)法,達(dá)成某個(gè)類(lèi) 只能被創(chuàng)建出一個(gè)實(shí)例這樣的效果(當(dāng)程序員不小心創(chuàng)建了多個(gè)實(shí)例,就會(huì)報(bào)錯(cuò))
單例模式實(shí)現(xiàn)
餓漢模式
// 餓漢模式的 單例模式 實(shí)現(xiàn)
// 此處保證 Singleton 這個(gè)類(lèi)只能創(chuàng)建出一個(gè)實(shí)例
class Singleton{
// 在此處,先把實(shí)例給創(chuàng)建出來(lái)
private static Singleton instance = new Singleton();
// 如果需要使用 instance,通過(guò)統(tǒng)一的Singleton.getInstance() 方式獲取
public static Singleton getInstance(){
return instance;
}
// 為了避免 Singleton 類(lèi)不小心被復(fù)制多份
// 把構(gòu)造方法設(shè)為 private,在類(lèi)外面,就無(wú)法通過(guò)new 的方式來(lái)創(chuàng)建這個(gè) Singleton了
private Singleton(){};
}
public class Thread3 {
public static void main(String[] args) {
Singleton s = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s==s2);
Singleton s3 = new Singleton(); // 報(bào)錯(cuò),原因是Singleton的構(gòu)造方法被private修飾,因此無(wú)法通過(guò)new的方式創(chuàng)建Singleton對(duì)象
}
}
懶漢模式
class Singleton2{
private static volatile Singleton2 instance = null; //使用volatile表示instance是個(gè)易變的
public static Singleton2 getInstance(){
if (instance==null) { // 此處負(fù)責(zé)判斷是否要加鎖
synchronized (Singleton2.class) {
if(instance==null){ // 此處判斷是否要?jiǎng)?chuàng)建對(duì)象
instance = new Singleton2();
}
}
}
return instance;
}
private Singleton2(){};
}
懶漢模式下,有創(chuàng)建Singleton對(duì)象的操作(寫(xiě)操作),所以可能會(huì)出現(xiàn)線(xiàn)程安全問(wèn)題,因此我們要進(jìn)行加鎖操作,并標(biāo)注instance是一個(gè)易變的對(duì)象(避免內(nèi)存可見(jiàn)性問(wèn)題,和指令重排序問(wèn)題)
工廠(chǎng)模式
工廠(chǎng)模式: 使用普通的方法,來(lái)代替構(gòu)造方法,創(chuàng)建對(duì)象.? 在java中,構(gòu)造方法存在一定缺陷,構(gòu)造方法要求構(gòu)造名必須為類(lèi)名(方法名相同),構(gòu)造參數(shù)可以不同,沒(méi)用返回值.如果我們只構(gòu)造一種對(duì)象可以忽略這個(gè)缺陷,如果構(gòu)造多種不同的情況的對(duì)象可能會(huì)出現(xiàn)問(wèn)題,比如想要實(shí)現(xiàn)倆個(gè)不同的構(gòu)造方法,但是它們的參數(shù)類(lèi)型恰好都相同,但表達(dá)的意義不同,這時(shí)java就無(wú)法區(qū)分了.為了解決這個(gè)問(wèn)題,就可以使用工程模式
比如分別使用笛卡爾坐標(biāo)系和極坐標(biāo)系表示坐標(biāo)
import java.awt.*;
class PointFactory{
public static Point makePointByXY(double x,double y){}
public static Point makePointByRA(double r,double a){}
}
public class Thread6 {
public static void main(String[] args) {
Point p = PointFactory.makePointByXY(10,20);
Point p2 = PointFactory.makePointByRA(10,30);
}
}
線(xiàn)程池
線(xiàn)程存在的意義: 使用進(jìn)程實(shí)現(xiàn)并發(fā)編程,"太重了",引入線(xiàn)程(輕量級(jí)進(jìn)程),創(chuàng)建線(xiàn)程比創(chuàng)建線(xiàn)程更高效,銷(xiāo)毀線(xiàn)程比銷(xiāo)毀進(jìn)程更高效,調(diào)度線(xiàn)程比調(diào)度進(jìn)程更高效,此時(shí)使用多線(xiàn)程就可以在很多時(shí)候代替進(jìn)程實(shí)現(xiàn)并發(fā)編程了
線(xiàn)程池存在的意義: 當(dāng)我們需要頻繁創(chuàng)建銷(xiāo)毀線(xiàn)程的時(shí)候,就發(fā)現(xiàn)開(kāi)銷(xiāo)也很大,想要進(jìn)一步的提高效率,可以: 1.搞一個(gè)協(xié)程(輕量級(jí)線(xiàn)程) 2.使用線(xiàn)程池,事先把需要使用的線(xiàn)程創(chuàng)建好,放到池中,后面需要使用的時(shí)候,從池中獲取,如果用完了,再還給池.(創(chuàng)建線(xiàn)程和銷(xiāo)毀線(xiàn)程是交由操作系統(tǒng)內(nèi)核去完成的,從池子里獲取/還給池,是自己用戶(hù)代碼就能實(shí)現(xiàn)的,不必交給內(nèi)核操作)
public class Thread5 {
public static void main(String[] args) {
// 此處就構(gòu)造了一個(gè) 10 個(gè)線(xiàn)程的線(xiàn)程池,就可以隨時(shí)安排這些線(xiàn)程干活了
ExecutorService pool = Executors.newFixedThreadPool(10);
// 當(dāng)前往線(xiàn)程池中放了1000個(gè)任務(wù),這1000個(gè)任務(wù)由線(xiàn)程池中的10個(gè)線(xiàn)程去執(zhí)行
for (int i = 0;i < 1000;i++) {
pool.submit(()->{
System.out.println("hello");
});
}
}
}
線(xiàn)程池提供了一個(gè)重要的方法,submit,可以給線(xiàn)程池提交若干個(gè)任務(wù),這若干個(gè)任務(wù)可以由線(xiàn)程池中的線(xiàn)程去執(zhí)行完成..線(xiàn)程池中創(chuàng)建的線(xiàn)程是前臺(tái)線(xiàn)程,需要執(zhí)行完成后,主線(xiàn)程才可以結(jié)束.?
這里1000個(gè)任務(wù)相當(dāng)于在一個(gè)隊(duì)列中,線(xiàn)程池中的這10個(gè)線(xiàn)程就依次取這個(gè)隊(duì)列中的任務(wù),取一個(gè)就執(zhí)行一個(gè),執(zhí)行完成后,再在這個(gè)隊(duì)列中取任務(wù)去執(zhí)行?
線(xiàn)程池種類(lèi)
這些線(xiàn)程池,本質(zhì)上都是通過(guò)包裝 ThreadPoolExecutor 來(lái)實(shí)現(xiàn)的?
ThreadPoolExcutor的構(gòu)造方法:
corePoolSize : 核心線(xiàn)程數(shù),
maximumPoolSize:? 最大線(xiàn)程數(shù),相當(dāng)于線(xiàn)程池把線(xiàn)程分為倆大類(lèi),一類(lèi)是核心線(xiàn)程,一類(lèi)是非核心線(xiàn)程,最大線(xiàn)程數(shù)就是核心線(xiàn)程和非核心線(xiàn)程之和
一個(gè)程序有時(shí)任務(wù)多,有時(shí)任務(wù)少,如果任務(wù)多,我們就需要多一些線(xiàn)程,如果任務(wù)少,就需要線(xiàn)程盡量少,此時(shí)我們就可以保留核心線(xiàn)程,而淘汰掉一些非核心線(xiàn)程
實(shí)際開(kāi)發(fā)中,線(xiàn)程池的線(xiàn)程數(shù)設(shè)定成多少合適?
程序分為CPU密集型,每個(gè)線(xiàn)程執(zhí)行的任務(wù)都需要狂轉(zhuǎn)CPU(進(jìn)行一系列算術(shù)運(yùn)算),此時(shí)線(xiàn)程池線(xiàn)程數(shù)最多不超過(guò)CPU核數(shù),因?yàn)閏pu密集型要一直占用cpu,創(chuàng)建更多的線(xiàn)程也沒(méi)用
IO密集型,每個(gè)線(xiàn)程的工作就是等待IO(讀寫(xiě)硬盤(pán),讀寫(xiě)網(wǎng)卡,等待用戶(hù)輸入),不占CPU,此時(shí)這樣的線(xiàn)程處于阻塞狀態(tài),不參與CPU調(diào)度,這個(gè)時(shí)候創(chuàng)建多個(gè)線(xiàn)程,不再受制于CPU核數(shù)了
實(shí)踐中確定線(xiàn)程數(shù),通過(guò)實(shí)驗(yàn)的方式,康康設(shè)置幾個(gè)線(xiàn)程合適
long keepAliveTime: 非核心線(xiàn)程數(shù)不工作的最大時(shí)間,如果超過(guò)這個(gè)時(shí)間就銷(xiāo)毀
TimeUnit unit: 時(shí)間單位,ms,s,分鐘,小時(shí)......
BlockingQueue<Runnable> workQueue: 線(xiàn)程池的任務(wù)隊(duì)列
ThreadFactory threadFactory: 用于創(chuàng)建線(xiàn)程
RejectedExecutionHandler handler: 描述了線(xiàn)程池的"拒絕策略",是一個(gè)特殊的對(duì)象,描述了當(dāng)線(xiàn)程池任務(wù)隊(duì)列滿(mǎn)了之后,如果繼續(xù)添加任務(wù),線(xiàn)程池會(huì)有什么樣的行為,總共有以下4種策略
ThreadPoolExcutor.AbortPolicy: 如果任務(wù)隊(duì)列滿(mǎn)了,再新增任務(wù),直接拋出異常
ThreadPoolExcutor.CallerRunsPoliy: 如果任務(wù)隊(duì)列滿(mǎn)了,多出來(lái)的任務(wù),誰(shuí)加的就由誰(shuí)去執(zhí)行(交給調(diào)用者去執(zhí)行)
ThreadPoolExcutor.DisardOlderdestPolicy: 如果任務(wù)隊(duì)列滿(mǎn)了,就丟棄最老的任務(wù)
ThreadPoolExcutor.DiscardPolicy: 如果任務(wù)隊(duì)列滿(mǎn)了,就丟棄最新的任務(wù)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-670484.html
手動(dòng)實(shí)現(xiàn)一個(gè)線(xiàn)程池?
一個(gè)線(xiàn)程池中至少有倆個(gè)部分,一個(gè)是阻塞隊(duì)列,用來(lái)保存任務(wù),一個(gè)是若干個(gè)工作線(xiàn)程文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-670484.html
class MyThreadPool{
private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
// n 表示線(xiàn)程數(shù)量
public MyThreadPool(int n){
// 創(chuàng)建 n 個(gè)線(xiàn)程
for (int i = 0; i < n; i++) {
Thread t = new Thread(()->{
while (true){
try {
Runnable runnable = queue.take();
runnable.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
// 注冊(cè)任務(wù)交給線(xiàn)程池
public void submit(Runnable runnable) {
try {
queue.put(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
到了這里,關(guān)于設(shè)計(jì)模式(單例模式,工廠(chǎng)模式),線(xiàn)程池的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!