Java多線程學習一 Thread常用方法

一、概念

進程
進程:具有一定獨立功能的程序關于某個數據集合上的一次運行活動,進程是系統(tǒng)進行資源分配和調度的一個獨立單位.
線程
線程:是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程自己基本上不擁有系統(tǒng)資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源.
進程和線程的關系
進程(process)和線程(thread)是操作系統(tǒng)的基本概念试浙,但是它們比較抽象董瞻,不容易掌握。
最近田巴,我讀到一篇材料力细,發(fā)現有一個很好的類比,可以把它們解釋地清晰易懂固额。

計算機的核心是CPU眠蚂,它承擔了所有的計算任務。它就像一座工廠斗躏,時刻在運行逝慧。
假定工廠的電力有限,一次只能供給一個車間使用啄糙。也就是說笛臣,一個車間開工的時候,其他車間都必須停工隧饼。背后的含義就是沈堡,單個CPU一次只能運行一個任務。
進程就好比工廠的車間燕雁,它代表CPU所能處理的單個任務诞丽。任一時刻鲸拥,CPU總是運行一個進程,其他進程處于非運行狀態(tài)僧免。
一個車間里刑赶,可以有很多工人。他們協(xié)同完成一個任務懂衩。
線程就好比車間里的工人撞叨。一個進程可以包括多個線程。
車間的空間是工人們共享的浊洞,比如許多房間是每個工人都可以進出的牵敷。這象征一個進程的內存空間是共享的,每個線程都可以使用這些共享內存法希。
可是劣领,每間房間的大小不同,有些房間最多只能容納一個人铁材,比如廁所尖淘。里面有人的時候,其他人就不能進去了著觉。這代表一個線程使用某些共享內存時村生,其他線程必須等它結束,才能使用這一塊內存饼丘。
一個防止他人進入的簡單方法趁桃,就是門口加一把鎖。先到的人鎖上門肄鸽,后到的人看到上鎖卫病,就在門口排隊,等鎖打開再進去典徘。這就叫"互斥鎖"(Mutual exclusion蟀苛,縮寫 Mutex),防止多個線程同時讀寫某一塊內存區(qū)域逮诲。
還有些房間帜平,可以同時容納n個人,比如廚房梅鹦。也就是說裆甩,如果人數大于n,多出來的人只能在外面等著齐唆。這好比某些內存區(qū)域嗤栓,只能供給固定數目的線程使用。
這時的解決方法箍邮,就是在門口掛n把鑰匙茉帅。進去的人就取一把鑰匙叨叙,出來時再把鑰匙掛回原處。后到的人發(fā)現鑰匙架空了担敌,就知道必須在門口排隊等著了摔敛。這種做法叫做"信號量"(Semaphore)廷蓉,用來保證多個線程不會互相沖突全封。
不難看出,mutex是semaphore的一種特殊情況(n=1時)桃犬。也就是說刹悴,完全可以用后者替代前者。但是攒暇,因為mutex較為簡單土匀,且效率高,所以在必須保證資源獨占的情況下形用,還是采用這種設計就轧。
操作系統(tǒng)的設計,因此可以歸結為三點:
(1)以多進程形式田度,允許多個任務同時運行妒御;
(2)以多線程形式,允許單個任務分成不同的部分運行镇饺;
(3)提供協(xié)調機制乎莉,一方面防止進程之間和線程之間產生沖突,另一方面允許進程之間和線程之間共享資源奸笤。

以上轉自阮一峰的個人網站

二惋啃、創(chuàng)建線程的方式

創(chuàng)建線程共有三種方式,一是繼承Thread類重寫run方法监右、二是實現Runnable接口重寫run方法(推薦使用)边灭、三是實現Callable接口重寫run方法

1. 繼承Thread類

package com.yjj.test01;

/**
* @Description:
* @Author: YinJunjie
* @CreateDate: 2018/9/21 17:53
* @Version: 1.0
*/
class Thread1 extends Thread {

   /**
    * 實現多線程第一種方式,繼承Thread健盒,實現run方法
    */
   @Override
   public void run() {
       for (int i = 0; i < 10; i++) {
           System.out.println(Thread.currentThread().getName() + "運行" + i);
       }
   }

}

public class ThreadMain {

   public static void main(String[] args) {
       Thread1 thread1 = new Thread1();
       thread1.setName("A");
       Thread1 thread2 = new Thread1();
       thread2.setName("B");
       thread1.start();
       thread2.start();

   }
}

輸出

B運行0
A運行0
A運行1
B運行1
A運行2
B運行2
A運行3
B運行3
A運行4
B運行4

2. 實現Runnable接口

package com.yjj.test01;

/**
 * @Description:
 * @Author: YinJunjie
 * @CreateDate: 2018/9/21 18:03
 * @Version: 1.0
 */
class Thread2 implements Runnable {

    private String name;

    public Thread2(String name) {
        this.name = name;
    }

    /**
     * 實現多線程第二種方式存筏,實現Runnable,實現run方法
     */
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(this.name + "運行" + i);
        }
    }
}

public class ThreadMain2 {
    public static void main(String[] args) {
        Thread thread1=new Thread(new Thread2("A"));
        Thread thread2=new Thread(new Thread2("b"));
        thread1.start();
        thread2.start();
    }
}

輸出

A運行0
b運行0
b運行1
b運行2
b運行3
b運行4
A運行1
A運行2
A運行3
A運行4

注意:

  1. 啟動一個線程只能通過start()方法味榛,而不能直接調用run方法椭坚,調用run方法相當于執(zhí)行了一個普通的方法,而沒有啟動線程搏色,代碼將會同步執(zhí)行善茎。
  2. 如果多次調用start()方法,則會出現異常Exception in thread "main" java.lang.IllegalThreadStateException频轿。
  3. 執(zhí)行start()方法的順序并不代表線程啟動的順序垂涯,線程執(zhí)行的順序是隨機的烁焙,某個線程搶到cpu資源就會執(zhí)行,其他則等待

三耕赘、線程安全

例:

public class MyThread extends Thread {

    private int count=5;

    public MyThread() {
    }

    public MyThread(String name) {
        this.setName(name);
    }

    @Override
    public void run() {
        while (count>0){
            count--;
            System.out.println(Thread.currentThread().getName()+":"+count);
        }
    }
}
public class TestMain {
    public static void main(String[] args) {
        shareVariable();
    }

    /**
     * 不共享變量 各自減各自的
     */
    public static void notShareVariable (){
        MyThread a=new MyThread("A");
        MyThread b=new MyThread("B");
        MyThread c=new MyThread("C");
        a.start();
        b.start();
        c.start();
    }

    /**
     * 共享變量
     *
     */
    public static void shareVariable(){
        MyThread myThread=new MyThread();
        Thread a=new Thread(myThread,"A");
        Thread b=new Thread(myThread,"B");
        Thread c=new Thread(myThread,"C");
        a.start();
        b.start();
        c.start();
    }
}

輸出

A:3
B:3
C:2
A:1
B:0

從輸出可看出骄蝇,A和B打印的都是3,說明A和B同事對count進行處理操骡,產生了非線程安全問題九火。
在某些JVM中,i--其實是由如下3步組成:

  1. 讀取原有的i值
  2. 計算i-1
  3. 對i賦新值
    在這3個步驟中册招,如果有多個線程同事訪問岔激,那么就會出現非線程安全問題。

四是掰、常用方法

  1. currentThread()
    返回代碼段正在被哪個線程調用
  2. this()和Thread.currentThread()的區(qū)別
package com.yjj.chapter01.test05;

/**
 * @Description:
 * @Author: YinJunjie
 * @CreateDate: 2018/9/22 18:07
 * @Version: 1.0
 */
public class MyThread extends Thread {

    public MyThread() {
        System.out.println("構造方法----begin");
        System.out.println("Thread.currentThread().getName()="+Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive()="+Thread.currentThread().isAlive());
        System.out.println("this.getName()="+this.getName());
        System.out.println("this.isAlive()="+this.isAlive());
        System.out.println("構造方法----end");
    }

    @Override
    public void run() {
        System.out.println("run方法-----begin");
        System.out.println("Thread.currentThread().getName()="+Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive()="+Thread.currentThread().isAlive());
        System.out.println("this.getName()="+this.getName());
        System.out.println("this.isAlive()="+this.isAlive());
        System.out.println("run方法----end");
    }
}
public class TestMain {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        myThread.setName("myThread");
        Thread thread=new Thread(myThread);
        System.out.println("main begin thread is Alive="+thread.isAlive());
        thread.setName("thread");
        thread.start();
        System.out.println("main end thread is Alive="+thread.isAlive());
    }
}

當一個Thread子類被當成參數傳入Thread對象時虑鼎,this和currentThread指向不同的對象,輸出如下

構造方法----begin
Thread.currentThread().getName()=main
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
構造方法----end
main begin thread is Alive=false
main end thread is Alive=true
run方法-----begin
Thread.currentThread().getName()=thread
Thread.currentThread().isAlive()=true
this.getName()=myThread
this.isAlive()=false
run方法----end

  1. isAlive()是否活動狀態(tài)
  2. sleep()方法
    讓當前“正在執(zhí)行的線程”休眠指定毫秒數键痛,這個“正在執(zhí)行的線程”是指this.currentThread()返回的線程
  3. getId()獲取線程的唯一標識
  4. 停止線程
    有3種方法可以終止正在運行的線程
    1). 使用退出標志炫彩,使線程正常退出,也就是當run方法完成后線程終止
    2). 使用stop方法強行終止線程強烈不推薦使用絮短,stop和suspend及resume都是作廢過期的方法江兢,使用它們可能會產生不可預料的結果。
    3). 使用interrupt方法中斷線程
  1. yield()方法
    yield()方法的作用是放棄當前的CPU資源戚丸,將它讓給其他的任務划址,但放棄時間不確定,有可能剛剛放棄限府,馬上又獲得CPU時間片夺颤。
  2. 線程優(yōu)先級
    在操作系統(tǒng)中,線程可以劃分優(yōu)先級胁勺,優(yōu)先級高的線程得到的CPU資源較多世澜,也就CPU有限執(zhí)行優(yōu)先級較高的線程對象中的任務。
    設置優(yōu)先級有助于線程規(guī)劃期確定下次選擇哪一個線程來優(yōu)先執(zhí)行署穗,但是也并不是優(yōu)先級越高就一定越先執(zhí)行寥裂。
    設置線程優(yōu)先級使用setPriority()方法,線程優(yōu)先級分為1~10這10個等級
    Java中線程優(yōu)先級具有如下幾個特性
  • 優(yōu)先級具有繼承性
    在Java中案疲,線程的優(yōu)先級具有繼承性封恰,比如A線程啟動B線程,則B線程優(yōu)先級與A相同
  • 優(yōu)先級具有規(guī)則性
        MyThread myThread=new MyThread();
        myThread.setPriority(10);
        MyThread2 myThread2=new MyThread2();
        myThread2.setPriority(1);
        myThread2.start();
        myThread.start();

雖然我是先啟動的myThread2褐啡,再啟動myThread诺舔,但是最先執(zhí)行完的總是myThread,因為他的優(yōu)先級較高,這是前幾次的輸出低飒,可以看到许昨,雖然先啟動的mythread2但是myThread的優(yōu)先級更高,所以搶到CPU的概率也越大

MyThread  i=0
MyThread2  i=0
MyThread  i=1
MyThread2  i=1
MyThread  i=2
MyThread  i=3
MyThread  i=4
MyThread  i=5
MyThread  i=6
MyThread2  i=2
MyThread  i=7
MyThread2  i=3
MyThread  i=8
MyThread2  i=4
MyThread  i=9
MyThread  i=10
  • 優(yōu)先級具有隨機性
    優(yōu)先級高的并不一定每一次都先執(zhí)行完褥赊,不一定不一定哦~
  1. 守護線程
    守護線程就是守護非守護線程的糕档。。emm拌喉。速那。典型的就是GC辣雞回收器線程
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市司光,隨后出現的幾起案子琅坡,更是在濱河造成了極大的恐慌悉患,老刑警劉巖残家,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異售躁,居然都是意外死亡坞淮,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門陪捷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來回窘,“玉大人,你說我怎么就攤上這事市袖》戎保” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵苍碟,是天一觀的道長酒觅。 經常有香客問我,道長微峰,這世上最難降的妖魔是什么舷丹? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蜓肆,結果婚禮上颜凯,老公的妹妹穿的比我還像新娘。我一直安慰自己仗扬,他們只是感情好症概,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著早芭,像睡著了一般彼城。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天精肃,我揣著相機與錄音秤涩,去河邊找鬼。 笑死司抱,一個胖子當著我的面吹牛筐眷,可吹牛的內容都是我干的。 我是一名探鬼主播习柠,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼匀谣,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了资溃?” 一聲冷哼從身側響起武翎,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎溶锭,沒想到半個月后宝恶,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡趴捅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年垫毙,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拱绑。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡综芥,死狀恐怖,靈堂內的尸體忽然破棺而出猎拨,到底是詐尸還是另有隱情膀藐,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布额各,位于F島的核電站,受9級特大地震影響类腮,放射性物質發(fā)生泄漏臊泰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一蚜枢、第九天 我趴在偏房一處隱蔽的房頂上張望缸逃。 院中可真熱鬧,春花似錦厂抽、人聲如沸需频。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昭殉。三九已至苞七,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間挪丢,已是汗流浹背蹂风。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留乾蓬,地道東北人惠啄。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像任内,于是被迫代替她去往敵國和親撵渡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容