java中的多線程

一:基本概念:

  • 1:什么叫進(jìn)程以及線程
    進(jìn)程:是一個具有獨(dú)立功能的程序關(guān)于某個數(shù)據(jù)集合的一次運(yùn)行活動。它可以申請和擁有系統(tǒng)資源,是一個動態(tài)的概念,是一個活動的實(shí)體。它不只是程序的代碼茫舶,還包括當(dāng)前的活動,通過程序計(jì)數(shù)器的值和處理寄存器的內(nèi)容來表示刹淌。進(jìn)程就是我們電腦上運(yùn)行的一個程序例如:QQ;
    線程:就是進(jìn)程的一個分支饶氏,一個進(jìn)程可以有多個線程,線程是不能擁有資源的有勾,只有一些運(yùn)行時(shí)必要的數(shù)據(jù)結(jié)構(gòu)疹启;每一個線程都會擁有一個父進(jìn)程,進(jìn)程擁有電腦或者程序分配的一些資源蔼卡,這些資源可以被進(jìn)程下面的線程所共享喊崖;
    通常在一個進(jìn)程中可以包含若干個線程,它們可以利用進(jìn)程所擁有的資源雇逞。在引入線程的操作系統(tǒng)中荤懂,通常都是把進(jìn)程作為分配資源的基本單位,而把線程作為獨(dú)立運(yùn)行和獨(dú)立調(diào)度的基本單位塘砸。由于線程比進(jìn)程更小节仿,基本上不擁有系統(tǒng)資源,故對它的調(diào)度所付出的開銷就會小得多掉蔬,能更高效的提高系統(tǒng)內(nèi)多個程序間并發(fā)執(zhí)行的程度廊宪。
    多線程程序是一種多任務(wù)多并發(fā)的程序;具有以下優(yōu)點(diǎn):
  • A:提高應(yīng)用程序的相應(yīng):例如:當(dāng)一個程序耗時(shí)很長時(shí)眉踱,如果使用單線程挤忙,此時(shí)電腦將無法為其他事件作出響應(yīng);而多線程則可以為這個耗時(shí)很長的程序單獨(dú)開辟一個線程讓其自己去完成谈喳,為其他操作開辟另外一個線程,即使響應(yīng)相關(guān)的操作戈泼;
  • B:使CPU系統(tǒng)更加的有效婿禽。當(dāng)一個電腦上運(yùn)行有多個CPU時(shí),操作系統(tǒng)就會保證當(dāng)線程數(shù)不大于CPU數(shù)目時(shí)大猛;不同的線程運(yùn)行在不同的CPU上扭倾;
  • C:改善程序的結(jié)構(gòu);將一個復(fù)雜的進(jìn)程分解為多個線程挽绩;使每一個線程成為獨(dú)立或者半獨(dú)立的狀態(tài)膛壹;有利于程序的維護(hù)或者修改
    2:多進(jìn)程和多線程
    多進(jìn)程是指一個電腦同時(shí)運(yùn)行多個任務(wù)的狀態(tài)
    多線程是指同一個程序(任務(wù))中多個順序流在執(zhí)行;

二:Java中的多線程:

  • 1:實(shí)現(xiàn)方式:

    • 1:繼承Thread類
    • 2:實(shí)現(xiàn)Runnable接口
    • 3:通過Callable 和 Future 創(chuàng)建線程

    1:集成Thread類

public class test{
  public static void main(String[] arg){
      thread1 t1 = new thread1();
       t1.start();  
  }
}
class thread1 extends Thread
      public void run(){
        System.out.println("我是繼承了Thread類實(shí)現(xiàn)的多線程!");
      }
}

B: 實(shí)現(xiàn)了Runnable接口的多線程

public class test{
  public static void main(String[] arg){
      runnable r = new runnable();  
      thread1 t1 = new thread1(r);
       t1.start();  
  }
}
class runnable1 implements Runnable{
    public void run() {
        System.out.println("我是實(shí)現(xiàn)了Runable接口的多線程模聋!");
    }
}

C:通過實(shí)現(xiàn)Callable接口并結(jié)構(gòu)FutureTask創(chuàng)建線程肩民,他和繼承Thread線程之間不會共享數(shù)據(jù);

class  callableThread implements Callable<Integer>{
    private int temp = 10;
    @Override
    public Integer call() throws Exception {
        for(int i=0;i<10;i++){
            temp--;
            System.out.println(Thread.currentThread().getName()+"="+i);
        }
        return temp;
    }
}  
public class test {
    public static void main(String[] args) {
        callableThread ct = new callableThread();
        FutureTask<Integer> ft = new FutureTask<>(ct);
        FutureTask<Integer> ft1 = new FutureTask<>(ct);
        new Thread(ft,"線程1").start();
        new Thread(ft1,"線程2").start();
    }
}

兩種方式的優(yōu)缺點(diǎn):
實(shí)現(xiàn)Runnable接口可以避免java的單繼承的缺點(diǎn)链方;
實(shí)現(xiàn)Runnable接口多線程操作下可以保證線程安全持痰;即實(shí)現(xiàn)Runable接口的對象可以被多個線程共享;進(jìn)而實(shí)現(xiàn)變量的共享祟蚀;適合于多個線程處理同一資源的情況工窍;

三:中斷線程:

每一個線程都會有一個標(biāo)志位用來判斷該線程是否被終止;所以每次調(diào)用之前我們都應(yīng)該先檢查該線程該標(biāo)志位判斷該線程是否終止前酿;
當(dāng)線程中的run方法執(zhí)行到最后一句并經(jīng)由rerturn語句返回時(shí)患雏,或者程序中存在沒有捕獲的異常時(shí)線程將終止;
調(diào)用interrup方法可以用來終止線程將其標(biāo)志置位罢维;
isInterrupt方法也可以用來判斷線程是否終止淹仑,并且它也不會改變線程的狀態(tài);另外一個Interrupted方法也具有該功能言津,但是它會改變該線程的狀態(tài)攻人;

四:線程的狀態(tài)以及各個狀態(tài)之間的轉(zhuǎn)換:

MulThread.png
  • A:注意yield方法只允許比自己優(yōu)先級高的線程或者和自己的優(yōu)先級相同的線程調(diào)用;
  • B:join方法是一個線程的方法悬槽,例如:一個程序啟動了A和B兩個線程怀吻,其中A是主線程,B是子線程初婆;現(xiàn)在我們正在執(zhí)行A線程蓬坡,如果此時(shí)A線程需要用到B線程執(zhí)行完畢時(shí)的一個數(shù)據(jù),此時(shí)我們就可以調(diào)用B線程的jion方法讓線程B執(zhí)行完畢之后再執(zhí)行線程A磅叛;可以理解為插隊(duì)屑咳;

五:守護(hù)線程:

守護(hù)線程的主要作用就是為其他線程服務(wù);例如:計(jì)時(shí)器弊琴,當(dāng)只剩下守護(hù)線程時(shí)虛擬機(jī)就會關(guān)閉自動退出兆龙;守護(hù)線程不會去訪問固定資源:文件,數(shù)據(jù)庫敲董;
通過調(diào)用t.setDaemon(true)將線程t變成守護(hù)線程

六:同步:

  • <1>:基本概念
    當(dāng)使用多個線程處理同一組數(shù)據(jù)時(shí)紫皇,就有可能出現(xiàn)兩個線程同時(shí)對這一組數(shù)據(jù)修改的情況;這樣就會造成這一組共享的數(shù)據(jù)出現(xiàn)不合理的情況腋寨;為了防止這一情況發(fā)生我們提出了同步的概念聪铺;
  • <2>:保持?jǐn)?shù)據(jù)同步的方法
  • ** A:使用鎖對象**
    java.util.concurrent下面的ReentrantLock來保護(hù)代碼的基本結(jié)構(gòu);
private Lock banklock = new ReentrantLock();
public void transfer(){
MyLock.lock();
  Try{
        //doSomething();
  }Finally{
       myLock.unlock();
  }
}

上面的結(jié)構(gòu)確保任何時(shí)刻僅僅只能有一個線程進(jìn)入臨界區(qū)萄窜;一旦一個線程封鎖了鎖對象铃剔,其他任何線程都無法通過Lock語句撒桨;當(dāng)其他線程調(diào)用lock時(shí)他們將被阻塞;直到第一個線程釋放鎖對象键兜;PS:釋放鎖的操作一定要放在finally字句中凤类;臨界區(qū)代碼拋出異常,鎖對象必須被釋放蝶押;
鎖是可以重入的踱蠢;線程可以重復(fù)的獲得已經(jīng)持有的鎖;

  • B:條件對象:
    只有當(dāng)某個條件滿足了我們才執(zhí)行接下來的操作棋电;此時(shí)我們就需要添加一個條件
private Condition sufficientFunds;
public void transfer(int from,int to,int amount){
   banklock.lock();
  try{
    //不滿足條件就一邊等著去
    while(accounts[from]<amount){
         sufficientFunds.await();
     }
    //Transfer fund;  
     ....
    suffcientFunds.signAll();
  }finally{
     banklock.unlock();
  }
}

調(diào)用signAll方法并不會立即激活一個線程茎截,他僅僅是讓你從一個對象的等待池中跳出來了;以便這個線程在當(dāng)前線程釋放同步鎖之后赶盔;可以通過競爭實(shí)現(xiàn)對對象的訪問企锌;

  • 總結(jié):
    a:鎖用來保護(hù)代碼片段,任何時(shí)刻只能有一個線程執(zhí)行被保護(hù)的代碼
    b:鎖可以管理試圖進(jìn)入被保護(hù)代碼段的線程
    c:鎖可以擁有一個或者多個條件對象
    d:每一個條件對象管理那些已經(jīng)進(jìn)入被保護(hù)的代碼段但還是不能運(yùn)行的線程于未。

  • C:synchronized關(guān)鍵字
    java的每一個對象都會有一個內(nèi)部鎖撕攒,如過一段代碼或者一個方法用synchronized關(guān)鍵字聲明,那么對象的鎖將會保護(hù)這個方法烘浦;也就是說想要調(diào)用該方法抖坪,就必須要獲得內(nèi)部的對象鎖;
    其實(shí)

public synchronized void method(){
  //dosomething;
}
等價(jià)于:
public void method(){
  this.intrinsicLock.lock();
  try{
     //dosomething;
  }
}

內(nèi)部對象鎖僅僅只有一個相關(guān)條件闷叉。wait方法添加一個線程到等待集中去擦俐,notify/notifyAll方法解除等待線程的阻塞狀態(tài);等價(jià)于Condition的notify和wait以及notifyAll方法握侧;但是notify和notifyAll以及wait方法都是Object的final方法蚯瞧;由上面的代碼可知synchronized相比ReenrantLock鎖要簡單的多;使用對象鎖最關(guān)鍵的一點(diǎn)是你要理解 每一個對象都有一個內(nèi)部鎖品擎;并且該鎖只有一個內(nèi)部條件埋合;由鎖來管理那些試圖進(jìn)入sychronized方法的線程,由條件來管理那些調(diào)用wait方法的線程萄传;
內(nèi)部鎖和條件存在的一些局限性:
a:不能中斷一個正在試圖獲得鎖的線程
b:試圖獲得鎖時(shí)不能設(shè)定超時(shí)
c:每個鎖僅有單一的條件甚颂,可能是不夠的;
那么在代碼中使用哪一種秀菱?
·1)最好既不使用Lock/Condition也不使用synchronized關(guān)鍵字西设。許多情況我們都可以使用java.util.concurrent包中的一種機(jī)制,它會為你處理所有的代碼加鎖答朋;
2)如果使用synchronized關(guān)鍵字適合你的程序,最好使用它棠笑;減少代碼量梦碗;
3)如果特別需要Lock/Condition結(jié)構(gòu)提供的都具有特性時(shí)才使用Lock/Condition;

  • D:同步阻塞
    所謂的同步阻塞其實(shí)就是使用同步代碼代碼塊;
    例如:
  private double[] accounts;
  public void transfer(Vector<Double> accouts,int to,int from){
     synchronized(accounts){//截獲這個鎖
        acounts.set(from,accounts.get(from)-amount);
        acounts.set(to,accounts.get(to)+amount);
   }
  }
  • E:監(jiān)視器的概念:
    雖然鎖和條件是十分強(qiáng)大的同步工具洪规,但是他們不是面對對象的印屁;所以人們提出了監(jiān)視器的概念,它具有以下特征:
    a:監(jiān)視器是只包含私有域的類
    b:每一個監(jiān)視器類的對象僅僅含有一個相關(guān)的鎖斩例;
    c:使用該鎖對所有的方法進(jìn)行加鎖雄人;如果客戶端調(diào)用obj.method();那么obj對象的鎖是在方法調(diào)用開始時(shí)自動獲得的;并且當(dāng)方法返回時(shí)自動釋放該鎖念赶;
    d:該鎖可以有任意多個相關(guān)條件
    但是Java設(shè)計(jì)者以不是很精確的方式采用了監(jiān)視器的概念础钠;Java的每一個對象都有一個內(nèi)部鎖和內(nèi)部條件;但是他在以下三個方面的不同于監(jiān)視器使其安全性下降:
    a:域不要求必須是private
    b:方法要求必須是synchronized;
    c:內(nèi)部鎖對客戶端是可以使用的
  • F:Volaitile域
    目前現(xiàn)狀是:
    a:多處理器的計(jì)算機(jī)能夠暫時(shí)在寄存器或本地內(nèi)存緩沖區(qū)中保存內(nèi)存中的值叉谜,這將使得運(yùn)行在不同處理器上的線程可能在同一內(nèi)存位置上取得不同的值旗吁;
    b:編譯器可以改變指令的運(yùn)行順序使得指令的吞吐量最大化;雖然這種變化不會改變代碼的語義但是停局,編譯器假定內(nèi)存中的值僅僅只是在代碼有顯示修改指令時(shí)才會改變很钓;然而內(nèi)存的值是可以被其他線程改變的;
    有時(shí)僅僅為了的讀寫一兩個域就使用同步顯然會使得程序開銷過大董栽;此時(shí)我們就提出了Volatile概念码倦;通過對一個變量(域)添加Volatile關(guān)鍵字來保證變量(域)安全相對同步來講要容易的多;例如:
  private synchronized boolean isDone;
  private synchronized void setDone;
  private boolean done;
//這種情況下我們就可以將done域聲明為Volatile類型的锭碳;
  private  boolean isDone;
  private  void setDone;
  private volatile boolean done;

線程寫volatile變量的過程
①改變線程工作內(nèi)存中volatile變量副本的值
②將改變后的副本的值從工作內(nèi)存刷新到主內(nèi)存
線程讀volatile變量的過程:
①從主內(nèi)存中讀取volatile變量的最新值到線程的工作內(nèi)存中
②從工作內(nèi)存中讀取volatile變量的副本的值
雖然volatile關(guān)鍵字給我們的省去了同步帶來的不便利性袁稽,但是同時(shí)它也存在一定的安全隱患:
a:volatile是不能保證原子性的例如:done!=done;a++等操作
b:volatile禁止指令重新排序;
總之:以下3種情況下工禾,域的并發(fā)訪問時(shí)安全的:
a:于是final运提,并且在構(gòu)造器調(diào)用完成之后被訪問;
b:對域的訪問使用共有鎖進(jìn)行保護(hù)
c:域是volatile的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末闻葵,一起剝皮案震驚了整個濱河市民泵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌槽畔,老刑警劉巖栈妆,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異厢钧,居然都是意外死亡鳞尔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門早直,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寥假,“玉大人,你說我怎么就攤上這事霞扬「馊停” “怎么了枫振?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長萤彩。 經(jīng)常有香客問我粪滤,道長,這世上最難降的妖魔是什么雀扶? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任杖小,我火速辦了婚禮,結(jié)果婚禮上愚墓,老公的妹妹穿的比我還像新娘予权。我一直安慰自己,他們只是感情好转绷,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布伟件。 她就那樣靜靜地躺著,像睡著了一般议经。 火紅的嫁衣襯著肌膚如雪斧账。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天煞肾,我揣著相機(jī)與錄音咧织,去河邊找鬼。 笑死籍救,一個胖子當(dāng)著我的面吹牛习绢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蝙昙,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼闪萄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了奇颠?” 一聲冷哼從身側(cè)響起败去,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎烈拒,沒想到半個月后厂僧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體彤蔽,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年文兢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了尔破。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吕粗。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡债蜜,死狀恐怖梨州,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情诞吱,我是刑警寧澤剂陡,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布狈涮,位于F島的核電站,受9級特大地震影響鸭栖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜握巢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一晕鹊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧暴浦,春花似錦溅话、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至独撇,卻和暖如春屑墨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纷铣。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工卵史, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搜立。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓以躯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親啄踊。 傳聞我的和親對象是個殘疾皇子忧设,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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