Java: 線程,線程池

(Java中線程池,你真的會(huì)用嗎掷豺?)[https://developer.aliyun.com/article/756612?scm=20140722.184.2.173]
?進(jìn)程:一個(gè)啟動(dòng)的應(yīng)用程序,是線程的載體.進(jìn)程也是程序的一次執(zhí)行過程捞烟,是系統(tǒng)運(yùn)行程序的基本單位;系統(tǒng)運(yùn)行一個(gè)程序即是一個(gè)進(jìn)程從創(chuàng)建当船、運(yùn)行到消亡的過程

?線程是系統(tǒng)中最小的執(zhí)行單元.JVM搶占式調(diào)度

?啟動(dòng)應(yīng)用程序啟動(dòng)進(jìn)程,進(jìn)程默認(rèn)情況下會(huì)啟動(dòng)主線程

進(jìn)程與線程的區(qū)別
進(jìn)程:有獨(dú)立的內(nèi)存空間题画,進(jìn)程中的數(shù)據(jù)存放空間(堆空間和棧空間)是獨(dú)立的德频,至少有一個(gè)線程苍息。
線程:堆空間是共享的,椧贾茫空間是獨(dú)立的竞思,線程消耗的資源比進(jìn)程小的多。

一.多線程的創(chuàng)建方式:

1. 繼承(extends) Thread

  1. 定義Thread類的子類蒸绩,并重寫該類的run()方法衙四,該run()方法的方法體就代表了線程需要完成的任務(wù),因此把run()方法稱為線程執(zhí)行體。
  2. 創(chuàng)建Thread子類的實(shí)例患亿,即創(chuàng)建了線程對象
  3. 調(diào)用線程對象的start()方法來啟動(dòng)該線程
    缺點(diǎn):
    直接將任務(wù)的實(shí)現(xiàn)寫到了線程類當(dāng)中传蹈,耦合度太高
//自定義中斷標(biāo)記,實(shí)現(xiàn)指定時(shí)間中斷
class MyThread extends Thread {
    private boolean needInterrupt = false;
    public void setNeedInterrupt(boolean needInterrupt) {
        this.needInterrupt = needInterrupt;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (needInterrupt) return;
            System.out.println("打印" + i);

            try {
                sleep(1000L);

            } catch (InterruptedException e) {
                System.out.println("報(bào)錯(cuò)" + e.getMessage());
                e.printStackTrace();
            }
        }
    }}
public class Main {
    public static void main(String[] args) {

        MyThread thread = new MyThread();
        thread.setName("ccc");
       // thread.setPriority(10);             設(shè)置線程優(yōu)先級
        thread.start();

        try {
            thread.sleep(3000L);
            thread.interrupt();
           thread.setNeedInterrupt(true);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }}}

2.實(shí)現(xiàn)(implements) Runnable

  1. 定義Runnable接口的實(shí)現(xiàn)類,并重寫該接口的run()方法步藕,該run()方法的方法體同樣是該線程的線程執(zhí)行體惦界。
  2. 創(chuàng)建Runnable實(shí)現(xiàn)類的實(shí)例,并以此實(shí)例作為Thread的target來創(chuàng)建Thread對象咙冗,該Thread對象才是真正的線程對象沾歪。
  3. 調(diào)用線程對象的start()方法來啟動(dòng)線程。
    • Runnable不返回結(jié)果雾消,也不能拋出被檢查的異常
//多線程賣100張票
public class MyRunnable implements Runnable {
    private int Ticket = 100;

    @Override
    public void run() {
        while (true) {
            synchronized (MyRunnable.class) { //用this也可
                if (Ticket > 0) {
                    System.out.println("賣出第" + (101 - Ticket) + "張票");
                    Ticket--;
                } else return;
            }

        }
    }
}

public class Main {
    public static void main(String[] args) {
  MyRunnable runnable = new MyRunnable();
        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        Thread thread3 = new Thread(runnable);
        thread1.setName("窗口1");
        thread1.setName("窗口2");
        thread1.setName("窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}      

如果一個(gè)類繼承Thread灾搏,則不適合資源共享。但是如果實(shí)現(xiàn)了Runnable接口的話立润,則很容易實(shí)現(xiàn)資源共享

實(shí)現(xiàn)Runnable接口比繼承Thread類所具有的優(yōu)勢

  1. 適合多個(gè)相同的程序代碼的線程去共享同一個(gè)資源狂窑。
  2. 可以避免java中的單繼承的局限性。
  3. 增加程序的健壯性桑腮,實(shí)現(xiàn)解耦操作泉哈,代碼可以被多個(gè)線程共享,代碼和線程獨(dú)立。
  4. 線程池只能放入實(shí)現(xiàn)Runable或Callable類線程丛晦,不能直接放入繼承Thread的類

2.1 匿名內(nèi)部類創(chuàng)建

使用線程的匿名內(nèi)部類方式奕纫,可以方便的實(shí)現(xiàn)每個(gè)線程執(zhí)行不同的線程任務(wù)操作。
使用匿名內(nèi)部類的方式實(shí)現(xiàn)Runnable接口烫沙,重寫Runnable接口中的run方法:

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("美美" + i);
                }
            }
        },"hello");
        new Thread(thread).start();
        System.out.println(thread.getName());

        Thread.currentThread().setName("美眉");   //設(shè)置main線程名字
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+":妹"+i);
        }

    }
}

3.Callable

有返回值(可選),可以拋出異常

import java.util.concurrent.Callable;

public class MyCallAble implements Callable<String> {

    @Override
    public String call() throws Exception {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
            
        }
        return "callable返回了數(shù)據(jù)";
    }
}

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main2 {
    public static void main(String[] args) {
        MyCallAble callAble = new MyCallAble();
        ExecutorService service = Executors.newFixedThreadPool(5);
        Future<String> submit = service.submit(callAble);
        try {
            System.out.println(submit.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

二匹层、線程安全

線程安全問題都是由全局變量及靜態(tài)變量引起的。

若每個(gè)線程中對全局變量斧吐、靜態(tài)變量只有讀操作又固,而無寫操作,一般來說煤率,這個(gè)全局變量是線程安全的;若有多個(gè)線程同時(shí)執(zhí)行寫操作乏冀,一般都需要考慮線程同步蝶糯,否則的話就可能影響線程安全

1.同步代碼塊 synchronized

部分代碼需要加鎖

synchronized(同步鎖){
              需要同步操作的代碼
}

同步鎖:

  1. 鎖對象 可以是任意類型。
  2. 多個(gè)線程對象 要使用同一把鎖辆沦。(需要保證唯一)

2.同步方法

修飾符 synchronized 返回值類型 方法名(){
           可能會(huì)產(chǎn)生線程安全問題的代碼
}

同步鎖:
對于非static方法,同步鎖就是this昼捍。
對于static方法,我們使用當(dāng)前方法所在類的字節(jié)碼對象(類名.class)

3.鎖機(jī)制(同步鎖/lock鎖)

Lock lock = new ReentrantLock();
lock.lock();

采用Lock,必須主動(dòng)去釋放鎖肢扯,并且在發(fā)生異常時(shí)妒茬,不會(huì)自動(dòng)釋放鎖。因此一般來說蔚晨,

使用Lock必須在try{}catch{}塊中進(jìn)行乍钻,并且將釋放鎖的操作放在finally塊中進(jìn)行,以保證鎖一定被被釋放铭腕,防止死鎖的發(fā)生

Lock lock = ...;

lock.lock();

try{

    //處理任務(wù)

}catch(Exception ex){

}finally{

    lock.unlock();   //釋放鎖
}

三.線程狀態(tài)

NEW(新建)

線程剛被創(chuàng)建银择,但是并未啟動(dòng)。還沒調(diào)用start方法累舷。
Runnable (可運(yùn)行)
線程可以在java虛擬機(jī)中運(yùn)行的狀態(tài)浩考,可能正在運(yùn)行自己代碼,也可能沒有被盈,這取決于操
作系統(tǒng)處理器析孽。
Blocked(鎖阻塞)
當(dāng)一個(gè)線程試圖獲取一個(gè)對象鎖,而該對象鎖被其他的線程持有只怎,則該線程進(jìn)入Blocked狀
態(tài)袜瞬;當(dāng)該線程持有鎖時(shí),該線程將變成Runnable狀態(tài)尝盼。
Waiting(無限等待)
一個(gè)線程在等待另一個(gè)線程執(zhí)行一個(gè)(喚醒)動(dòng)作時(shí)吞滞,該線程進(jìn)入Waiting狀態(tài)。進(jìn)入這個(gè)
狀態(tài)后是不能自動(dòng)喚醒的,必須等待另一個(gè)線程調(diào)用notify或者notifyAll方法才能夠喚醒裁赠。
TimedWaiting(計(jì)時(shí)等待)
同waiting狀態(tài)殿漠,有幾個(gè)方法有超時(shí)參數(shù),調(diào)用他們將進(jìn)入Timed Waiting狀態(tài)佩捞。這一狀態(tài)
將一直保持到超時(shí)期滿或者接收到喚醒通知绞幌。帶有超時(shí)參數(shù)的常用方法有Thread.sleep 、
Object.wait一忱。
Teminated(被終止)
因?yàn)閞un方法正常退出而死亡莲蜘,或者因?yàn)闆]有捕獲的異常終止了run方法而死亡

四.線程通信(等待喚醒機(jī)制)

4.1. 2個(gè)線程間使用flag變量實(shí)現(xiàn)交替執(zhí)行

//實(shí)現(xiàn)2個(gè)線程間交替執(zhí)行
public class Demo {
    int flag = 1;


    public synchronized void method1() {
        if (flag != 1) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print("學(xué)");
        System.out.print("習(xí)");
        System.out.print("了");
        System.out.print("嗎");
        System.out.println();
        this.flag = 2;
        this.notify();

    }

    public synchronized void method2() {
        if (flag != 2) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print("s");
        System.out.print("t");
        System.out.print("u");
        System.out.print("d");
        System.out.print("y");
        System.out.println();
        this.flag = 1;
        this.notify();
    }
}

public class Main {

    public static void main(String[] args) {
        Demo demo = new Demo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    demo.method1();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    demo.method2();
                }
            }
        }).start();
    }
}

4.2 替換if為 whie可實(shí)現(xiàn)3個(gè)線程間有效通信(交替執(zhí)行)

4.3 生產(chǎn)者消費(fèi)者案例

//生產(chǎn)者
public class Producter extends Thread {
    BaoZi bz;

    @Override
    public void run() {
        int num = 0;
        while (true) {
            synchronized (bz) {
                if (bz.flag == true) {
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("生產(chǎn)者喚醒,開始生產(chǎn)");
                if (num % 2 == 0) {
                    bz.pier = "面皮";
                    bz.xianer = "豬肉大蔥";

                } else {
                    bz.xianer = "黑芝麻";
                    bz.pier = "米皮";
                }
System.out.println("包子皮是:" + bz.pier + ";包子餡是:" + bz.xianer);
                num++;
                bz.flag = true;
                bz.notify();
            }
        }
    }
}

//消費(fèi)者
public class Eater extends Thread {
    BaoZi bz;

    @Override
    public void run() {
        while (true) {
            synchronized (bz) {
                if (bz.flag == false) {
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
System.out.println("吃貨開始吃包子,皮是:" + bz.pier + ";餡是:" + bz.xianer);
                bz.flag = false;
                bz.notify();
                System.out.println();
            }
        }
    }
}


public class BaoZi {
    boolean flag = false;
    String pier;
    String xianer;
}


public class Main1 {
    public static void main(String[] args) {

        BaoZi baoZi = new BaoZi();

        Producter pt = new Producter();
        pt.bz = baoZi;
        pt.start();
        Eater eater = new Eater();
        eater.bz = baoZi;
        eater.start();
    }
}

五.線程池 ExecutorService(interface)

5.1 合理使用的好處:

  1. 降低資源消耗。
  2. 提高響應(yīng)速度帘营。
  3. 提高線程的可管理性

官方建議使用Executors工程類來創(chuàng)建線程池對象
阿里巴巴開發(fā)手冊并發(fā)編程:線程池不允許使用 Executors 去創(chuàng)建票渠,而是通過 ThreadPoolExecutor 的方式

Executors 創(chuàng)建線程池的方式
根據(jù)返回的對象類型創(chuàng)建線程池可以分為三類:
創(chuàng)建返回 ThreadPoolExecutor 對象
創(chuàng)建返回 ScheduleThreadPoolExecutor 對象
創(chuàng)建返回 ForkJoinPool 對象

5.2 使用步驟

  1. 創(chuàng)建線程池對象。

  2. 創(chuàng)建Runnable接口子類對象芬迄。(task)

  3. 提交Runnable接口子類對象问顷。(take task)

    execute 無返回值

    submit 有返回值

  4. 關(guān)閉線程池(一般不做) shutdown

    public class MyRunnable implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(i+" "+Thread.currentThread().getName());
                try {
                    Thread.sleep(10L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public class Main {
        public static void main(String[] args) {
            ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);
       //Runtime.getRuntime().availableProcessors()獲取當(dāng)前cpu核心數(shù)
            MyRunnable runnable = new MyRunnable();
            service.execute(runnable);
            //execute 無返回值
            Future<?> submit = service.submit(runnable);
            //submit 有返回值
            
        }
    }
    
    
    

六.線程 join

?將線程之間的并行執(zhí)行變?yōu)榇袌?zhí)行 

七.守護(hù)線程 setDaemon

?守護(hù)線程是指在程序運(yùn)行的時(shí)候在后臺提供一種通用的服務(wù)的線程(垃圾回收線程)

?主線程執(zhí)行結(jié)束,守護(hù)線程就結(jié)束

守護(hù)線程設(shè)置在線程啟動(dòng)之前.參數(shù)為true表示是daemon thread.

thread2.setDaemon(true); 
thread2.start();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市禀梳,隨后出現(xiàn)的幾起案子杜窄,更是在濱河造成了極大的恐慌,老刑警劉巖算途,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件塞耕,死亡現(xiàn)場離奇詭異,居然都是意外死亡嘴瓤,警方通過查閱死者的電腦和手機(jī)扫外,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纱注,“玉大人畏浆,你說我怎么就攤上這事∧” “怎么了刻获?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瞎嬉。 經(jīng)常有香客問我蝎毡,道長,這世上最難降的妖魔是什么氧枣? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任沐兵,我火速辦了婚禮,結(jié)果婚禮上便监,老公的妹妹穿的比我還像新娘扎谎。我一直安慰自己碳想,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布毁靶。 她就那樣靜靜地躺著胧奔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪预吆。 梳的紋絲不亂的頭發(fā)上龙填,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機(jī)與錄音拐叉,去河邊找鬼岩遗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛凤瘦,可吹牛的內(nèi)容都是我干的宿礁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼蔬芥,長吁一口氣:“原來是場噩夢啊……” “哼窘拯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起坝茎,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎暇番,沒想到半個(gè)月后嗤放,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡壁酬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年次酌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舆乔。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡岳服,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出希俩,到底是詐尸還是另有隱情吊宋,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布颜武,位于F島的核電站璃搜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鳞上。R本人自食惡果不足惜这吻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望篙议。 院中可真熱鬧唾糯,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至芋酌,卻和暖如春增显,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脐帝。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工同云, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人堵腹。 一個(gè)月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓炸站,卻偏偏與公主長得像,于是被迫代替她去往敵國和親疚顷。 傳聞我的和親對象是個(gè)殘疾皇子旱易,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

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

  • 進(jìn)程和線程 進(jìn)程 所有運(yùn)行中的任務(wù)通常對應(yīng)一個(gè)進(jìn)程,當(dāng)一個(gè)程序進(jìn)入內(nèi)存運(yùn)行時(shí),即變成一個(gè)進(jìn)程.進(jìn)程是處于運(yùn)行過程中...
    小徐andorid閱讀 2,810評論 3 53
  • 本文是我自己在秋招復(fù)習(xí)時(shí)的讀書筆記,整理的知識點(diǎn)腿堤,也是為了防止忘記阀坏,尊重勞動(dòng)成果,轉(zhuǎn)載注明出處哦笆檀!如果你也喜歡忌堂,那...
    波波波先森閱讀 11,270評論 4 56
  • 本文主要講了java中多線程的使用方法、線程同步酗洒、線程數(shù)據(jù)傳遞士修、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法、概述等樱衷。 首先講...
    李欣陽閱讀 2,456評論 1 15
  • Java多線程學(xué)習(xí) [-] 一擴(kuò)展javalangThread類 二實(shí)現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 2,959評論 1 18
  • 【選摘】 亞當(dāng)斯密 ? “這種本性就是憐憫或同情棋嘲,就是當(dāng)我們看到或逼真地想象到他人的不幸遭遇時(shí)所產(chǎn)生的感情【毓穑”...
    Cynthia雯霏閱讀 326評論 0 0