【多線程】基礎

1. 概念

1.1 使用多線程目的

提高系統(tǒng)的吞吐速度(提高CPU的利用率):充分利用多CUP多核(一個核可以對應多個線程數(shù)樟氢,如四核八線程)焙贷,一個核對應一個線程堵第。單核CPU時代蜘腌,只能有一個線程利用CPU,使用多線程提高系統(tǒng)利用率不明顯辰如。

1.2 多任務多進程

過去單核CPU時代赞咙,計算機能在同一時間點并行執(zhí)行多任務或多進程责循。并不是真正意義上的“同一時間點”,而是多個任務或進程共享一個CPU谋逻,并交由操作系統(tǒng)來完成多任務間對CPU的運行切換终惑,以使得每個任務都有機會獲得一定的時間片運行失驶。
而現(xiàn)在多核CPU的情況下,同一時間點可以執(zhí)行多個任務歹垫,具體到這個任務在CPU哪個核上運行,這個就跟操作系統(tǒng)和CPU本身的設計相關了

1.3 程序線程數(shù)

比如說tomcat為每個請求啟動一個線程處理颠放,其中maxThreads設置為1000排惨,有1000個用戶并發(fā),那么Tomcat就會起1000個線程來處理慈迈。那么假如實際CPU線程數(shù)只有30若贮,1000個線程分時間片段使用30個CPU線程處理省有,從外部上看是同一時間。類似單核CUP多任務谴麦。

1.4如何設置程序線程數(shù)

(1)活躍線程數(shù)為 CPU(核)數(shù)時最佳蠢沿。
過少的活躍線程導致 CPU 無法被充分利用,過多的活躍線程導致過大的線程上下文切換開銷匾效,同時更多的內存開銷舷蟀,更多的CPU開銷。
活躍線程數(shù):處于 IO 的線程面哼,休眠的線程等均不消耗 CPU野宜,不屬于活躍線程。在實際環(huán)境中魔策,當前活躍線程數(shù)一直在變化匈子,很多活躍線程可能因為需要進行 IO 處理或等待資源而處于非活躍狀態(tài),假如說線程的數(shù)量等于 CPU(核)數(shù)闯袒,這就意味著活躍線程數(shù)小于 CPU(核)數(shù)虎敦。
(2)確定線程數(shù)的原則
提高CPU的中簽率:多線程編程中,在確保內存不溢出的情況下提升線程數(shù)是可以提高CPU中簽率的政敢,也就是能提高你的程序處理數(shù)據(jù)的速度其徙。
不是線程數(shù)越大越好:即使沒有溢出,也不是線程數(shù)越大越好喷户,線程切換畢竟需要時間唾那,應該找到瓶頸所在
(3)依據(jù)cpu核數(shù)設置合適的線程數(shù)
獲取cpu核心數(shù):Runtime.getRuntime().availableProcessors();

// 根據(jù)CPU數(shù)量創(chuàng)建線程池
 private static ExecutorService printJobthreadPool = Executors
                        .newFixedThreadPool(Runtime.getRuntime().availableProcessors());

2. 線程安全

2.1 線程安全是由什么引起

線程安全問題都是由全局變量及靜態(tài)變量引起的。
若每個線程中對全局變量褪尝、靜態(tài)變量只有讀操作闹获,而無寫操作,一般來說恼五,這個全局變量是線程安全的昌罩;
若有多個線程同時執(zhí)行寫操作,一般都需要考慮線程同步灾馒,否則就可能影響線程安全茎用。以下情況是線程安全的:

  • 常量始終是線程安全的,因為只存在讀操作睬罗。
  • 每次調用方法前都新建一個實例是線程安全的轨功,因為不會訪問共享的資源。
  • 局部變量是線程安全的容达。因為每執(zhí)行一個方法古涧,都會在獨立的空間創(chuàng)建局部變量,它不是共享的資源花盐。局部變量包括方法的參數(shù)變量和方法內變量羡滑。

2.2 解決線程安全的方式

為了解決多線程中相同變量的訪問沖突問題菇爪,有兩種方法:ThreadLocal和線程同步機制。
(1)同步機制
對于多線程資源共享的問題柒昏,同步機制采用了“以時間換空間”的方式凳宙,在同步機制中,通過對象的鎖機制保證同一時間只有一個線程訪問變量职祷。這時該變量是多個線程共享的氏涩,使用同步機制要求程序慎密地分析什么時候對變量進行讀寫,什么時候需要鎖定某個對象有梆,什么時候釋放對象鎖等繁雜的問題是尖,程序設計和編寫難度相對較大。
(2)ThreadLocal
ThreadLocal采用了“以空間換時間”的方式泥耀。前者僅提供一份變量饺汹,讓不同的線程排隊訪問,而后者為每一個線程都提供了一份變量爆袍,因此可以同時訪問而互不影響首繁。ThreadLocal會為每一個線程提供一個獨立的變量副本,從而隔離了多個線程對數(shù)據(jù)的訪問沖突陨囊。因為每一個線程都擁有自己的變量副本,從而也就沒有必要對該變量進行同步了夹攒。ThreadLocal提供了線程安全的共享對象蜘醋,在編寫多線程代碼時,可以把不安全的變量封裝進ThreadLocal咏尝。
ThreadLocal具體使用:

private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>()
//我們有時候會使用靜態(tài)變量來存放某些值压语。但是并發(fā)情況下靜態(tài)變量是不安全的。因此使用ThreadLocal創(chuàng)建獨立的副本编检。
private static final ThreadLocal contextHolder=new ThreadLocal();

然后使用常用方法操作:
set:設置當前線程的線程局部變量的值胎食。
get:返回當前線程所對應的線程局部變量。
remove:將當前線程局部變量的值刪除允懂。

(3) 什么是線程安全的類
線程安全的類 厕怜,指的是類內共享的全局變量的訪問必須保證是不受多線程形式影響的。如果由于多線程的訪問(比如修改蕾总、遍歷粥航、查看)而使這些變量結構被破壞或者針對這些變量操作的原子性被破壞,則這個類就不是線程安全的生百。

2.4 無狀態(tài)Bean

(1)無狀態(tài)bean和有狀態(tài)的定義

  • 無狀態(tài)就是一次操作递雀,不能保存數(shù)據(jù)。無狀態(tài)對象(Stateless Bean)蚀浆,就是沒有實例變量的對象 ,不能保存數(shù)據(jù)缀程,是不變類搜吧,是線程安全的。無狀態(tài)的Bean適合用不變模式杨凑,技術就是單例模式赎败,這樣可以共享實例,提高性能蠢甲。
  • 有狀態(tài)對象:有狀態(tài)的Bean僵刮,多線程環(huán)境下不安全,那么適合用Prototype原型模式鹦牛。
package com.sw;  
 
public class TestManagerImpl implements TestManager{  
    private User user;    //實例變量
    public void deleteUser(User e) throws Exception {  
        user = e ;           //1  
        prepareData(e);  
    }  
    public void prepareData(User e) throws Exception {  
        user = getUserByID(e.getId());            //2  
        .....  
        //使用user.getId();                       //3  
        .....  
        .....  
    }     
}  

TestManagerImpl是一個有狀態(tài)的Bean搞糕,如果該Bean配置為singleton,會出現(xiàn)什么樣的狀況呢?

3. 使用流程

多線程使用步驟主要有:

3.1 建立線程任務

實現(xiàn)方式主要有兩種:實現(xiàn)Runnable接口和繼承Thread類曼追。實現(xiàn)Runnable接口相比繼承Thread類有如下好處:

  • 避免繼承的局限窍仰,一個類可以繼承多個接口。
  • 適合于資源的共享礼殊。

在程序開發(fā)中只要是多線程肯定永遠以實現(xiàn)Runnable接口為主驹吮。兩種方式都要實現(xiàn)run()方法。
(1)實現(xiàn)Runnable接口
主要的實現(xiàn)步驟很簡單:構造函數(shù)和實現(xiàn)run方法

public class RefundNotify implements Runnable
{
    private RefundNotifyMapper mRefundNotifyMapper;
    private LinkedBlockingQueue<String> queue;
    private String url;
    private static Logger logger = LoggerFactory.getLogger(AutoRepayServiceImpl.class);
    //構造函數(shù)
    public RefundNotify(LinkedBlockingQueue<String> queue, RefundNotifyMapper mRefundNotifyMapper, String url01)
    {
        System.out.println("退款通知線程開始工作晶伦!");
        this.queue = queue;
        this.mRefundNotifyMapper = mRefundNotifyMapper;
        this.url = url01;
    }
    // 實現(xiàn)run方法
    @SneakyThrows
    @Override
    public void run()
    {
    }   
}

Runnable里面沒有start方法可以通過Thread類進行啟動Runnable多線程碟狞,Runnable可以用同一個對象實例化的,可以資源共享婚陪,Thread不可以

//同一個實例
MyThreadWithImplements myRunnable = new MyThreadWithImplements();
hread thread1 = new Thread(myRunnable, "窗口一");
Thread thread2 = new Thread(myRunnable, "窗口二");
Thread thread3 = new Thread(myRunnable, "窗口三");
thread1.start();
thread2.start();
thread3.start();

(2)繼承Thread類
使用不多族沃,不詳細介紹

3.2 創(chuàng)建線程池

使用ThreadPoolExecutor創(chuàng)建線程池并執(zhí)行

//獲取系統(tǒng)處理器個數(shù),作為線程池數(shù)量
int nThreads = Runtime.getRuntime().availableProcessors();
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("member-pool-%d").build();
//創(chuàng)建線程池
ExecutorService pool = new ThreadPoolExecutor(5, 200,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
//傳入任務泌参,執(zhí)行
pool.execute(refundNotify);
pool.execute(invoiceNotify);
pool.execute(timingRetryNotify);

3.3 線程池調度執(zhí)行

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末脆淹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子沽一,更是在濱河造成了極大的恐慌盖溺,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铣缠,死亡現(xiàn)場離奇詭異烘嘱,居然都是意外死亡,警方通過查閱死者的電腦和手機攘残,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門拙友,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人歼郭,你說我怎么就攤上這事遗契。” “怎么了病曾?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵牍蜂,是天一觀的道長漾根。 經(jīng)常有香客問我,道長鲫竞,這世上最難降的妖魔是什么辐怕? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮从绘,結果婚禮上寄疏,老公的妹妹穿的比我還像新娘。我一直安慰自己僵井,他們只是感情好陕截,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著批什,像睡著了一般农曲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驻债,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天乳规,我揣著相機與錄音,去河邊找鬼合呐。 笑死暮的,一個胖子當著我的面吹牛,可吹牛的內容都是我干的合砂。 我是一名探鬼主播青扔,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼翩伪!你這毒婦竟也來了?” 一聲冷哼從身側響起谈息,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤缘屹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后侠仇,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體轻姿,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年逻炊,在試婚紗的時候發(fā)現(xiàn)自己被綠了互亮。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡余素,死狀恐怖豹休,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情桨吊,我是刑警寧澤威根,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布凤巨,位于F島的核電站,受9級特大地震影響洛搀,放射性物質發(fā)生泄漏敢茁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一留美、第九天 我趴在偏房一處隱蔽的房頂上張望彰檬。 院中可真熱鬧,春花似錦谎砾、人聲如沸逢倍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓶堕。三九已至,卻和暖如春症歇,著一層夾襖步出監(jiān)牢的瞬間郎笆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工忘晤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宛蚓,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓设塔,卻偏偏與公主長得像凄吏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子闰蛔,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內容