欧美另类日韩中文色综合,天堂va亚洲va欧美va国产,www.av在线播放,大香视频伊人精品75,奇米777888,欧美日本道免费二区三区,中文字幕亚洲综久久2021

Java線(xiàn)程及多線(xiàn)程技術(shù)及應(yīng)用 -電腦資料

電腦資料 時(shí)間:2019-01-01 我要投稿
【www.lotusphilosophies.com - 電腦資料】

   

第6 章 Java線(xiàn)程及多線(xiàn)程技術(shù)及應(yīng)用

6.1線(xiàn)程基本概念

    1、進(jìn)程和線(xiàn)程的基礎(chǔ)知識(shí)

    l 進(jìn)程:運(yùn)行中的應(yīng)用程序稱(chēng)為進(jìn)程,擁有系統(tǒng)資源(cpu、內(nèi)存)

    l 線(xiàn)程:進(jìn)程中的一段代碼,一個(gè)進(jìn)程中可以哦有多段代碼,

Java線(xiàn)程及多線(xiàn)程技術(shù)及應(yīng)用

。本身不擁有資源(共享所在進(jìn)程的資源)

    在java中,程序入口被自動(dòng)創(chuàng)建為主線(xiàn)程,在主線(xiàn)程中可以創(chuàng)建多個(gè)子線(xiàn)程。

    區(qū)別: 1、是否占有資源問(wèn)題

    2、創(chuàng)建或撤銷(xiāo)一個(gè)進(jìn)程所需要的開(kāi)銷(xiāo)比創(chuàng)建或撤銷(xiāo)一個(gè)線(xiàn)程所需要的開(kāi)銷(xiāo)大。

    3、進(jìn)程為重量級(jí)組件,線(xiàn)程為輕量級(jí)組件

    l 多進(jìn)程: 在操作系統(tǒng)中能同時(shí)運(yùn)行多個(gè)任務(wù)(程序)

    l 多線(xiàn)程: 在同一應(yīng)用程序中有多個(gè)功能流同時(shí)執(zhí)行

    2、線(xiàn)程的主要特點(diǎn)

    l 不能以一個(gè)文件名的方式獨(dú)立存在在磁盤(pán)中;

    l 不能單獨(dú)執(zhí)行,只有在進(jìn)程啟動(dòng)后才可啟動(dòng);

    l 線(xiàn)程可以共享進(jìn)程相同的內(nèi)存(代碼與數(shù)據(jù))。

    3、線(xiàn)程的主要用途

    l 利用它可以完成重復(fù)性的工作(如實(shí)現(xiàn)動(dòng)畫(huà)、聲音等的播放)。

    l 從事一次性較費(fèi)時(shí)的初始化工作(如網(wǎng)絡(luò)連接、聲音數(shù)據(jù)文件的加載)。

    l 并發(fā)執(zhí)行的運(yùn)行效果(一個(gè)進(jìn)程多個(gè)線(xiàn)程)以實(shí)現(xiàn)更復(fù)雜的功能

    4、多線(xiàn)程(多個(gè)線(xiàn)程同時(shí)運(yùn)行)程序的主要優(yōu)點(diǎn)

    l 可以減輕系統(tǒng)性能方面的瓶頸,因?yàn)榭梢圆⑿胁僮鳎?/p>

    l 提高CPU的處理器的效率,在多線(xiàn)程中,通過(guò)優(yōu)先級(jí)管理,可以使重要的程序優(yōu)先操作,提高了任務(wù)管理的靈活性;另一方面,在多CPU系統(tǒng)中,可以把不同的線(xiàn)程在不同的CPU中執(zhí)行,真正做到同時(shí)處理多任務(wù)。

6.2 線(xiàn)程創(chuàng)建與啟動(dòng)

    1、與線(xiàn)程編程有關(guān)的一些概念

    創(chuàng)建方式: 1 繼承java.lang.Thread類(lèi) 2 實(shí)現(xiàn)java.lang.Runnable接口

    線(xiàn)程體:public void run()方法,其內(nèi)的程序代碼決定了線(xiàn)程的行為和功能。

    線(xiàn)程啟動(dòng): public void start () , 線(xiàn)程啟動(dòng)后,需要獲取cpu才能自動(dòng)調(diào)用run()運(yùn)行。

    線(xiàn)程休眠: public void sleep(long ms), 線(xiàn)程將暫停,放棄cpu

    2、利用繼承Thread類(lèi)創(chuàng)建線(xiàn)程的示例

    package com.px1987.j2se.thread.base;

    /**通過(guò)Thread類(lèi)實(shí)現(xiàn)多線(xiàn)程 定義一個(gè)Thread的子類(lèi)并重寫(xiě)其run方法.*/

    public class MyThread extends Thread {

    @Override

    public void run() {

    while (true) {

    System.out.println(invoke MyThread run method);

    }

    }

    public static void main(String[] args) { // main方法測(cè)試線(xiàn)程的創(chuàng)建與啟動(dòng)

    MyThread myThread = new MyThread(); // 實(shí)例化MyThread的對(duì)象

    myThread.start(); // 調(diào)用myThread對(duì)象的start方法啟動(dòng)一個(gè)線(xiàn)程

    }

    }

    3、利用實(shí)現(xiàn)Runable接口創(chuàng)建線(xiàn)程的示例

    package com.px1987.j2se.thread.base;

    /**通過(guò)Runable接口實(shí)現(xiàn)多線(xiàn)程 定義MyRunable類(lèi)實(shí)現(xiàn)Runnable接口,并實(shí)現(xiàn)接口中的run方法。*/

    public class MyRunable implements Runnable {

    public void run() {

    while (true)

    System.out.println(invoke MyRunable run method);

    }

    public static void main(String[] args) { // main方法測(cè)試線(xiàn)程的創(chuàng)建與啟動(dòng)

    // 建立MyRunable類(lèi)的對(duì)象,以此對(duì)象為參數(shù)建立Thread類(lèi)的對(duì)象

    Thread thread = new Thread(new MyRunable());

    thread.start(); // 調(diào)用thread對(duì)象的start方法啟動(dòng)一個(gè)線(xiàn)程

    }

    }

6.3 線(xiàn)程的狀態(tài)控制

    1、新建狀態(tài)

    用new關(guān)鍵字和Thread類(lèi)或其子類(lèi)建立一個(gè)線(xiàn)程對(duì)象后,該線(xiàn)程對(duì)象就處于新生狀態(tài)。處于新生狀態(tài)的線(xiàn)程有自己的內(nèi)存空間,通過(guò)調(diào)用start方法進(jìn)入就緒狀態(tài)(runnable)。

    2、就緒狀態(tài)

    處于就緒狀態(tài)的線(xiàn)程已經(jīng)具備了運(yùn)行條件,但還沒(méi)有分配到CPU,處于線(xiàn)程就緒隊(duì)列,等待系統(tǒng)為其分配CPU。等待狀態(tài)并不是執(zhí)行狀態(tài),當(dāng)系統(tǒng)選定一個(gè)等待執(zhí)行的Thread對(duì)象后,它就會(huì)從等待執(zhí)行狀態(tài)進(jìn)入執(zhí)行狀態(tài),系統(tǒng)挑選的動(dòng)作稱(chēng)之為“cpu調(diào)度”。一旦獲得CPU,線(xiàn)程就進(jìn)入運(yùn)行狀態(tài)并自動(dòng)調(diào)用自己的run方法。

    3、死亡狀態(tài)

    死亡狀態(tài)是線(xiàn)程生命周期中的最后一個(gè)階段。線(xiàn)程死亡的原因有兩個(gè):

    一個(gè)是正常運(yùn)行的線(xiàn)程完成了它的全部工作;

    另一個(gè)是線(xiàn)程被強(qiáng)制性地終止,如通過(guò)執(zhí)行stop或destroy方法來(lái)終止一個(gè)線(xiàn)程。

    Method stop() & destroy() in the class Thread is deprecated。

    當(dāng)一個(gè)線(xiàn)程進(jìn)入死亡狀態(tài)以后,就不能再回到其它狀態(tài)了。 讓一個(gè)Thread對(duì)象重新執(zhí)行一次的唯一方法,就是重新產(chǎn)生一個(gè)Thread對(duì)象。

    4、體現(xiàn)線(xiàn)程狀態(tài)轉(zhuǎn)變的代碼示例

    package com.px1987.j2se.thread.base;

    public class MyRunable1 implements Runnable {

    public void run() {

    while (true)

    System.out.println(invoke MyRunable run method);

    }

    public static void main(String[] args) {

    Thread thread = new Thread(new MyRunable()); // 新生狀態(tài)

    thread.start(); // 就緒狀態(tài),獲得CPU后就能運(yùn)行

    try {

    Thread.sleep(5000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    thread.stop(); // 死亡狀態(tài)

    }

    }

    通過(guò)查API可以看到stop方法和destory方法已經(jīng)過(guò)時(shí)了,所以不能再用,那要怎樣做才能強(qiáng)制的銷(xiāo)毀一個(gè)線(xiàn)程呢?

    1、在run方法中執(zhí)行return 線(xiàn)程同樣結(jié)束

    2、可以在while循環(huán)的條件中設(shè)定一個(gè)標(biāo)志位,當(dāng)它等于false的時(shí)候,while循環(huán)就不在運(yùn)行,這樣線(xiàn)程也就結(jié)束了。代碼為實(shí)現(xiàn)的代碼示例:

    package com.px1987.j2se.thread.StateControl;

    public class MyRunable2 implements Runnable {

    private boolean isStop; //線(xiàn)程是否停止的標(biāo)志位

    public void run() {

    while (!isStop)

    System.out.println(invoke MyRunable run method);

    }

    public void stop(){ //終止線(xiàn)程

    isStop=true;

    }

    public static void main(String[] args) {

    MyRunable myRunable=new MyRunable();

    Thread thread = new Thread(myRunable);

    thread.start();

    try {

    Thread.sleep(5000);

    }

    catch (InterruptedException e) {

    e.printStackTrace();

    }

    myRunable.stop(); //正確的停止線(xiàn)程的方法

    }

    }

    5、阻塞狀態(tài)

    處于運(yùn)行狀態(tài)的線(xiàn)程在某些情況下,如執(zhí)行了sleep(睡眠)方法,或等待I/O設(shè)備等資源,將讓出CPU并暫時(shí)停止自己的運(yùn)行,進(jìn)入阻塞狀態(tài)。

    在阻塞狀態(tài)的線(xiàn)程不能進(jìn)入就緒隊(duì)列。只有當(dāng)引起阻塞的原因消除時(shí),如睡眠時(shí)間已到,或等待的I/O設(shè)備空閑下來(lái),線(xiàn)程便轉(zhuǎn)入就緒狀態(tài),重新到就緒隊(duì)列中排隊(duì)等待,被系統(tǒng)選中后從原來(lái)停止的位置開(kāi)始繼續(xù)運(yùn)行。有三種方法可以暫停Threads執(zhí)行:

    (1)sleep方法

    可以調(diào)用Thread的靜態(tài)方法:public static void sleep(long millis) throws InterruptedException 使得當(dāng)前線(xiàn)程休眠(暫時(shí)停止執(zhí)行millis毫秒)。由于是靜態(tài)方法,sleep可以由類(lèi)名直接調(diào)用:Thread.sleep(…)。下面為代碼示例:

    package com.px1987.j2se.thread.p5;

    import java.util.Date;

    import java.text.SimpleDateFormat;

    class SleepTest implements Runnable {

    private static SimpleDateFormat format = new SimpleDateFormat(yyyy-MM-dd hh:mm:ss);

    public void run() {

    System.out.println(child thread begin);

    int i = 0;

    while (i++ < 5) {

    System.out.println(format.format(new Date()));

    try {

    Thread.sleep(5000);

    }

    catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    System.out.println(child thread dead at: + format.format(new Date()));

    }

    public static void main(String[] args) {

    Runnable r = new SleepTest();

    Thread thread = new Thread(r);

    thread.start();

    try {

    Thread.sleep(20000);

    }

    catch (InterruptedException e) {

    e.printStackTrace();

    }

    thread.interrupt();

    System.out.println(main method dead!);

    }

    }

    該程序的運(yùn)行結(jié)果如下:

    child thread begin

    2009-02-06 04:50:29

    2009-02-06 04:50:34

    2009-02-06 04:50:39

    2009-02-06 04:50:44

    main method dead!

    java.lang.InterruptedException: sleep interrupted

    at java.lang.Thread.sleep(Native Method)

    at com.px1987.j2se.thread.p5.Thread4.run(Thread4.java:17)

    at java.lang.Thread.run(Unknown Source)

    2009-02-06 04:50:49

    child thread dead at: 2009-02-06 04:50:54

    (2)yield方法

    讓出CPU的使用權(quán),從運(yùn)行態(tài)直接進(jìn)入就緒態(tài)。下面為代碼示例:

    package com.px1987.j2se.thread.StateControl;

    class Thread5 implements Runnable {

    private String name;

    Thread5(String s) {

    this.name = s;

    }

    public void run() {

    for (int i = 1; i <= 50; i++) {

    System.out.println(name + : + i);

    if (i % 10 == 0) {

    Thread.yield();

    }

    }

    }

    }

    package com.px1987.j2se.thread.StateControl;

    public class YieldTest {

    public static void main(String[] args) {

    Runnable r1 = new Thread5(S1);

    Runnable r2 = new Thread5(S2);

    Thread t1 = new Thread(r1);

    Thread t2 = new Thread(r2);

    t1.start();

    t2.start();

    try {

    Thread.sleep(2);

    }

    catch (InterruptedException e) {

    e.printStackTrace();

    }

    System.out.println(main method over!);

    }

    }

    該程序的部分運(yùn)行結(jié)果如下:

    S1: 20

    S2: 7

    S2: 8

    S2: 9

    S2: 10

    S1: 41

    S1: 42

    S1: 43

    S1: 44

    S1: 45

    S1: 46

    S1: 47

    S1: 48

    S1: 49

    S1: 50

    S2: 11

    S2: 12

    (3)join方法

    當(dāng)某個(gè)(A)線(xiàn)程等待另一個(gè)線(xiàn)程(B)執(zhí)行結(jié)束后,才繼續(xù)執(zhí)行時(shí),使用join方法。A的 run方法調(diào)用b.join()。下面為代碼示例。

    package com.px1987.j2se.thread.join;

    class FatherThread implements Runnable {

    public void run() {

    System.out.println(爸爸想抽煙,發(fā)現(xiàn)煙抽完了);

    System.out.println(爸爸讓兒子去買(mǎi)包紅塔山);

    Thread son = new Thread(new SonThread());

    son.start();

    System.out.println(爸爸等兒子買(mǎi)煙回來(lái));

    try { //join含義:等待son線(xiàn)程執(zhí)行完畢,father線(xiàn)程才繼續(xù)執(zhí)行

    son.join();

    }

    catch (InterruptedException e) {

    System.out.println(爸爸出門(mén)去找兒子跑哪去了);

    System.exit(1);

    }

    System.out.println(爸爸高興的接過(guò)煙開(kāi)始抽,并把零錢(qián)給了兒子);

    }

    }

    package com.px1987.j2se.thread.join;

    class SonThread implements Runnable {

    public void run() {

    String tabs= ;

    System.out.println(tabs+兒子出門(mén)去買(mǎi)煙);

    System.out.println(tabs+兒子買(mǎi)煙需要10分鐘);

    try {

    for (int i = 0; i < 10;) {

    Thread.sleep(1000);

    System.out.println(tabs+兒子出去第 + ++i + 分鐘);

    }

    }

    catch (InterruptedException e) {

    e.printStackTrace();

    }

    System.out.println(tabs+兒子買(mǎi)煙回來(lái)了);

    }

    }

    package com.px1987.j2se.thread.join;

    public class JoinTest {

    public static void main(String[] args) {

    System.out.println(爸爸和兒子的故事);

    Thread father = new Thread(new FatherThread());

    father.start();

    // try {

    // Thread.sleep(5000);

    // } catch (InterruptedException e) {

    // e.printStackTrace();

    // }

    // father.interrupt();

    }

    }

    該程序的運(yùn)行結(jié)果如下:

    爸爸和兒子的故事

    爸爸想抽煙,發(fā)現(xiàn)煙抽完了

    爸爸讓兒子去買(mǎi)包紅塔山

    爸爸等兒子買(mǎi)煙回來(lái)

    兒子出門(mén)去買(mǎi)煙

    兒子買(mǎi)煙需要10分鐘

    兒子出去第1分鐘

    兒子出去第2分鐘

    兒子出去第3分鐘

    兒子出去第4分鐘

    兒子出去第5分鐘

    兒子出去第6分鐘

    兒子出去第7分鐘

    兒子出去第8分鐘

    兒子出去第9分鐘

    兒子出去第10分鐘

    兒子買(mǎi)煙回來(lái)了

    爸爸高興的接過(guò)煙開(kāi)始抽,并把零錢(qián)給了兒子

    當(dāng)時(shí)間來(lái)到兒子出去買(mǎi)煙的時(shí)候,F(xiàn)ather線(xiàn)程調(diào)用interrupt方法就會(huì)打斷son線(xiàn)程的正常執(zhí)行,從而father線(xiàn)程也就不必等待son線(xiàn)程執(zhí)行完畢再行動(dòng)了,運(yùn)行結(jié)果如下:

    爸爸和兒子的故事

    爸爸想抽煙,發(fā)現(xiàn)煙抽完了

    爸爸讓兒子去買(mǎi)包紅塔山

    爸爸等兒子買(mǎi)煙回來(lái)

    兒子出門(mén)去買(mǎi)煙

    兒子買(mǎi)煙需要10分鐘

    兒子出去第1分鐘

    兒子出去第2分鐘

    兒子出去第3分鐘

    兒子出去第4分鐘

    爸爸出門(mén)去找兒子跑哪去了

6.4線(xiàn)程的調(diào)度和優(yōu)先級(jí)

    1、線(xiàn)程的基本信息

    方 法

    功 能

    isAlive()

    判斷線(xiàn)程是否還“活”著,即線(xiàn)程是否還未終止。

    getPriority()

    獲得線(xiàn)程的優(yōu)先級(jí)數(shù)值

    setPriority()

    設(shè)置線(xiàn)程的優(yōu)先級(jí)數(shù)值

    setName()

    給線(xiàn)程一個(gè)名字

    getName()

    取得線(xiàn)程的名字

    currentThread()

    取得當(dāng)前正在運(yùn)行的線(xiàn)程對(duì)象,也就是取得自己本身

    2、操作線(xiàn)程的基本信息代碼示例

    package com.px1987.j2se.thread.priority;

    public class ThreadInfoTest {

    public static void main(String[] argc) throws Exception {

    Runnable r = new MyThread();

    Thread t = new Thread(r, Name test);

    t.start();

    System.out.println(name is: + t.getName());

    Thread.currentThread().sleep(5000);

    System.out.println(t.isAlive());

    System.out.println(over!);

    }

    }

    class MyThread implements Runnable {

    public void run() {

    for (int i = 0; i < 100; i++)

    System.out.println(i);

    }

    }

    該程序的運(yùn)行結(jié)果如下:

    name is: Name test

    0

    1

    2

    3

    . . .

    97

    98

    99

    false

    over!

    3、線(xiàn)程的優(yōu)先級(jí)

    (1)優(yōu)先級(jí)(共10級(jí)):

    它們決定線(xiàn)程執(zhí)行的先后次序(優(yōu)先級(jí)高者先執(zhí)行)并可以通過(guò)Thread類(lèi)中的setPriority()和getPriority()方法來(lái)改變和獲取優(yōu)先級(jí)。典型的優(yōu)先級(jí)碼

    n Thread.MIN_PRIORITY (1級(jí))

    n Thread.MAX_PRIORITY(10級(jí))

    n Thread.NORM_PRIORITY(5級(jí))

    (2)調(diào)度規(guī)則

    Java是不支持線(xiàn)程時(shí)間片輪換的調(diào)度模型,而采用的是線(xiàn)程優(yōu)先級(jí)高低的搶占調(diào)度模型。具有高優(yōu)先級(jí)的線(xiàn)程可以搶占低優(yōu)先級(jí)線(xiàn)程運(yùn)行的機(jī)會(huì)。高優(yōu)先級(jí)的線(xiàn)程將始終獲得線(xiàn)程執(zhí)行時(shí)間。但是這也不是絕對(duì)的,java線(xiàn)程調(diào)度器有可能會(huì)調(diào)用長(zhǎng)期處于等待的線(xiàn)程進(jìn)行執(zhí)行,所以不要依靠線(xiàn)程的高優(yōu)先級(jí)搶占模型去完成某些功能。

    Java線(xiàn)程調(diào)度器支持不同優(yōu)先級(jí)線(xiàn)程的搶先方式,但其本身不支持相同優(yōu)先級(jí)線(xiàn)程的時(shí)間片輪換。但是如果java運(yùn)行時(shí)系統(tǒng)所在的操作系統(tǒng)(如windows2000)支持時(shí)間片的輪換,則線(xiàn)程調(diào)度器就支持相同優(yōu)先級(jí)線(xiàn)程的時(shí)間片輪換。

    (3)代碼示例

    package com.px1987.j2se.thread.priority;

    public class ThreadPriorityTest {

    public static void main(String[] args) {

    Thread t1 = new Thread(new MyThread2(), t1);

    Thread t2 = new Thread(new MyThread2(), t2);

    t1.setPriority(1);

    t2.setPriority(10);

    t1.start();

    t2.start();

    }

    }

    class MyThread2 extends Thread {

    public void run() {

    for (int i = 0; i < 10; i++) {

    System.out.println(Thread.currentThread().getName() + : + i);

    yield();

    }

    }

    }

    該程序的運(yùn)行結(jié)果如下:

    t1: 0

    t2: 0

    t2: 1

    t2: 2

    t2: 3

    t2: 4

    t2: 5

    t2: 6

    t2: 7

    t2: 8

    t2: 9

    t1: 1

    t1: 2

    t1: 3

    t1: 4

    t1: 5

    t1: 6

    t1: 7

    t1: 8

    t1: 9

6.5線(xiàn)程同步互斥

    1、線(xiàn)程同步互斥的一個(gè)示例

    多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)或操作同一資源時(shí),很容易出現(xiàn)數(shù)據(jù)前后不一致的問(wèn)題。請(qǐng)看下面的例子:

    男孩拿著折子去北京銀行海淀分行取錢(qián)

    女孩拿著男孩的銀行卡去西單百貨瘋狂購(gòu)物

    男孩走到柜臺(tái)錢(qián)詢(xún)問(wèn)帳戶(hù)余額

    銀行的業(yè)務(wù)員小姐親切地告訴他:您還有10000元!。

    女孩看上了一件時(shí)髦的衣裳,準(zhǔn)備買(mǎi)下

    男孩在思考要取多少錢(qián)呢?

    女孩到收銀臺(tái)準(zhǔn)備刷卡消費(fèi)

    收銀臺(tái)刷卡機(jī)讀取銀行卡余額為10000元

    女孩買(mǎi)衣服刷卡消費(fèi)5000元

    消費(fèi)清單打印出來(lái),消費(fèi):5000元 余額:5000元

    女孩離開(kāi)商場(chǎng)

    男孩思考了1毫秒

    男孩決定取5000元

    銀行的業(yè)務(wù)員小姐為男孩辦理相關(guān)業(yè)務(wù)手續(xù)

    交易完成

    銀行的業(yè)務(wù)員小姐告訴男孩:您的余額為5000元。

    男孩離開(kāi)銀行

    男孩帳戶(hù)中一共有10000元,男孩拿著存折從銀行取走5000元,女孩拿著男孩的銀行卡購(gòu)物刷卡消費(fèi)5000元,最后男孩的帳戶(hù)里卻還剩5000元。顯然這是不正確的,但是為什么會(huì)發(fā)生這樣的情況呢?我們可以這樣分析:男孩可以看作是一條線(xiàn)程,女孩也可以看作是一條線(xiàn)程,在同一時(shí)刻,兩個(gè)線(xiàn)程都操作了同一個(gè)資源,那就是男孩的帳戶(hù)。男孩從查看帳戶(hù)余額到取走現(xiàn)金應(yīng)該被看作是個(gè)原子性操作,是不可再分的,然而當(dāng)男孩查看完余額正思考取多少錢(qián)的時(shí)候,女孩購(gòu)物消費(fèi)了5000元,也就是說(shuō)女孩這條線(xiàn)程打斷了男孩這條線(xiàn)程所要執(zhí)行的任務(wù)。所以男孩剛查看完的余額10000元就不正確了,最終導(dǎo)致帳戶(hù)中少減了5000元。

    為了避免這樣的事情發(fā)生,我們要保證線(xiàn)程同步互斥,所謂同步互斥就是:并發(fā)執(zhí)行的多個(gè)線(xiàn)程在某一時(shí)間內(nèi)只允許一個(gè)線(xiàn)程在執(zhí)行以訪(fǎng)問(wèn)共享數(shù)據(jù)

    2、Java中線(xiàn)程互斥的實(shí)現(xiàn)機(jī)制

    由多線(xiàn)程帶來(lái)的性能改善是以可靠性為代價(jià)的,所以編程出線(xiàn)程安全的類(lèi)代碼是十分必要的。當(dāng)多個(gè)線(xiàn)程可以訪(fǎng)問(wèn)共享資源(調(diào)用單個(gè)對(duì)象的屬性和方法,對(duì)數(shù)據(jù)進(jìn)行讀、寫(xiě)、修改、刪除等操作)時(shí),應(yīng)保證同時(shí)只有一個(gè)線(xiàn)程訪(fǎng)問(wèn)共享數(shù)據(jù),Java對(duì)此提出了有效的解決方案—同步鎖。任何線(xiàn)程要進(jìn)入同步互斥方法(訪(fǎng)問(wèn)共享資源的方法或代碼段)時(shí),就必須得到這個(gè)共享資源對(duì)象的鎖,線(xiàn)程進(jìn)入同步互斥方法后其它線(xiàn)程則不能再進(jìn)入同步互斥方法,直到擁有共享資源對(duì)象鎖的線(xiàn)程執(zhí)行完同步互斥方法釋放了鎖,下一個(gè)線(xiàn)程才能進(jìn)入同步互斥方法被執(zhí)行。

    Java的這一線(xiàn)程互斥的實(shí)現(xiàn)機(jī)制可以用一個(gè)最通俗的比方來(lái)說(shuō)明:比如公共衛(wèi)生間就是一個(gè)共享資源,每個(gè)人都可以使用,但又不能同時(shí)使用,所以衛(wèi)生間里有一把鎖。一個(gè)人進(jìn)去了,會(huì)把門(mén)鎖上,其他人就不能進(jìn)去。當(dāng)Ta出來(lái)的時(shí)候,要打開(kāi)鎖,下一個(gè)人才能繼續(xù)使用。

    3、利用Synchronized關(guān)鍵字用于修飾同步互斥方法

    (1)同步互斥方法

    public synchronized void method(){

    //允許訪(fǎng)問(wèn)控制的代碼

    }

    (2)同步互斥代碼塊

    synchronized(syncObject){

    //允許訪(fǎng)問(wèn)控制的代碼

    }

    (3)鎖定整個(gè)類(lèi)

    public synchronized class SyncObject{

    }

    由于synchronized 塊可以針對(duì)任意的代碼塊,且可任意指定上鎖的對(duì)象,因此靈活性較高。但要注意:

    l synchronized可以用來(lái)限定一個(gè)方法或一小段語(yǔ)句或整個(gè)類(lèi)(該類(lèi)中的所有方法都是synchronized方法)

    l 將訪(fǎng)問(wèn)共享數(shù)據(jù)的代碼設(shè)計(jì)為synchronized方法

    l 由于可以通過(guò) private 關(guān)鍵字來(lái)保證數(shù)據(jù)對(duì)象只能被方法訪(fǎng)問(wèn),所以只需針對(duì)方法提出一套同步鎖定機(jī)制。通過(guò)synchronized 方法來(lái)控制對(duì)類(lèi)中的成員變量(共享數(shù)據(jù))的訪(fǎng)問(wèn)。

    l 編寫(xiě)線(xiàn)程安全的代碼會(huì)使系統(tǒng)的總體效率會(huì)降低,要適量使用

    l 只有某一個(gè)線(xiàn)程的synchronized方法執(zhí)行完后其它線(xiàn)程的synchronized方法才能被執(zhí)行。

    l 當(dāng)前時(shí)間,只有一個(gè)線(xiàn)程訪(fǎng)問(wèn)被鎖定的代碼段,但不能保證其他線(xiàn)程去訪(fǎng)問(wèn)其他沒(méi)有被鎖定的代碼段。因此所有對(duì)共享資源進(jìn)行操作的代碼段都應(yīng)該加鎖。

    l 對(duì)數(shù)據(jù)庫(kù)操作時(shí),修改數(shù)據(jù)的線(xiàn)程要加鎖,而讀數(shù)據(jù)的線(xiàn)程可以不加鎖

    有了這種解決方案,我們用線(xiàn)程安全的代碼來(lái)重新實(shí)現(xiàn)一下男孩和女孩取錢(qián)的故事。以下是核心代碼:

    package com.px1987.j2se.thread.synchronous.v2;

    /** 帳戶(hù)類(lèi) */

    public class Account {

    /** 余額 */

    private int balance;

    public Account(int balance) {

    this.balance = balance;

    }

    }

    package com.px1987.j2se.thread.synchronous.v2;

    /** 男孩類(lèi),實(shí)現(xiàn)Runnable接口*/

    public class Boy implements Runnable {

    /** 銀行帳戶(hù)*/

    Account account;

    public Boy(Account account) {

    this.account = account;

    }

    /** 男孩拿著折子去北京銀行海淀分行取錢(qián)*/

    public void run() {

    System.out.println(男孩拿著折子去北京銀行海淀分行取錢(qián));

    synchronized (account) {

    System.out.println(男孩走到柜臺(tái)錢(qián)詢(xún)問(wèn)帳戶(hù)余額);

    int balance = account.getBalance();

    System.out.println(銀行的業(yè)務(wù)員小姐親切地告訴他:您還有 +

    balance + 元!。);

    try {

    System.out.println(男孩在思考要取多少錢(qián)呢?);

    Thread.sleep(1);

    System.out.println(男孩思考了1毫秒);

    }

    catch (InterruptedException e) {

    e.printStackTrace();

    }

    int money = 5000;

    System.out.println(男孩決定取 + money + 元);

    System.out.println(銀行的業(yè)務(wù)員小姐為男孩辦理相關(guān)業(yè)務(wù)手續(xù));

    account.setBalance(balance - money);

    System.out.println(交易完成);

    System.out.println(銀行的業(yè)務(wù)員小姐告訴男孩:您的余額為 +

    account.getBalance()+ 元。);

    }

    System.out.println(男孩離開(kāi)銀行);

    }

    }

    package com.px1987.j2se.thread.synchronous.v2;

    /** 女孩類(lèi),實(shí)現(xiàn)runnable接口*/

    public class Girl implements Runnable {

    /** 女孩持有男孩的銀行卡*/

    Account account;

    public Girl(Account account) {

    this.account = account;

    }

    /*** 女孩拿著小軍的銀行卡去西單百貨瘋狂購(gòu)物*/

    public void run() {

    String tabs = ;

    System.out.println(tabs + 女孩拿著小軍的銀行卡去西單百貨瘋狂購(gòu)物);

    System.out.println(tabs + 女孩看上了一件時(shí)髦的衣裳,準(zhǔn)備買(mǎi)下);

    synchronized (account) {

    System.out.println(tabs + 女孩到收銀臺(tái)準(zhǔn)備刷卡消費(fèi));

    int balance = account.getBalance();

    System.out.println(tabs + 收銀臺(tái)刷卡機(jī)讀取銀行卡余額為 + balance + 元);

    int payout = 5000;

    System.out.println(tabs + 女孩買(mǎi)衣服刷卡消費(fèi) + payout + 元);

    account.setBalance(balance - payout);

    System.out.println(tabs + 消費(fèi)清單打印出來(lái),消費(fèi): + payout + 元 + 余額:

    + account.getBalance() + 元);

    }

    System.out.println(tabs + 女孩離開(kāi)商場(chǎng));

    }

    }

    package com.px1987.j2se.thread.synchronous.v2;

    public class Bank {

    public static void main(String[] args) {

    Account account=new Account(10000);

    Thread boyThread=new Thread(new Boy(account));

    Thread girlThread=new Thread(new Girl(account));

    boyThread.start();

    girlThread.start();

    }

    }

    修改后的代碼運(yùn)行結(jié)果如下圖:

    男孩拿著折子去北京銀行海淀分行取錢(qián)

    女孩拿著小軍的銀行卡去西單百貨瘋狂購(gòu)物

    女孩看上了一件時(shí)髦的衣裳,準(zhǔn)備買(mǎi)下

    女孩到收銀臺(tái)準(zhǔn)備刷卡消費(fèi)

    收銀臺(tái)刷卡機(jī)讀取銀行卡余額為10000元

    女孩買(mǎi)衣服刷卡消費(fèi)5000元

    消費(fèi)清單打印出來(lái),消費(fèi):5000元 余額:5000元

    女孩離開(kāi)商場(chǎng)

    男孩走到柜臺(tái)錢(qián)詢(xún)問(wèn)帳戶(hù)余額

    銀行的業(yè)務(wù)員小姐親切地告訴他:您還有5000元!,

電腦資料

Java線(xiàn)程及多線(xiàn)程技術(shù)及應(yīng)用》(http://www.lotusphilosophies.com)。

    男孩在思考要取多少錢(qián)呢?

    男孩思考了1毫秒

    男孩決定取5000元

    銀行的業(yè)務(wù)員小姐為男孩辦理相關(guān)業(yè)務(wù)手續(xù)

    交易完成

    銀行的業(yè)務(wù)員小姐告訴男孩:您的余額為0元。

    男孩離開(kāi)銀行

    從結(jié)果中可以看出來(lái),男孩從查看余額到取錢(qián),女孩沒(méi)有操作帳戶(hù),所以最后的余額是正確的。

    4、線(xiàn)程死鎖

    使用互斥鎖容易產(chǎn)生死鎖問(wèn)題。比如:一個(gè)線(xiàn)程需要鎖定兩個(gè)對(duì)象才能完成,線(xiàn)程1擁有對(duì)象A的鎖,線(xiàn)程1如果再擁有對(duì)象B的鎖就能完成操作,線(xiàn)程2擁有對(duì)象B的鎖,線(xiàn)程2如果再擁有對(duì)象A的鎖就能完成操作。

    很不幸的是線(xiàn)程1執(zhí)行不下去了,因?yàn)榫(xiàn)程1等待的資源對(duì)象B被線(xiàn)程2鎖住了,線(xiàn)程2也執(zhí)行不下去了,因?yàn)榫(xiàn)程2等待的資源對(duì)象A被線(xiàn)程1鎖住了,這樣就造成了死鎖。

    閱讀一段文字:由多線(xiàn)程帶來(lái)的性能改善是以可靠性為代價(jià)的,主要是因?yàn)橛锌赡墚a(chǎn)生線(xiàn)程死鎖。死鎖是這樣一種情形:多個(gè)線(xiàn)程同時(shí)被阻塞,它們中的一個(gè)或者全部都在等待某個(gè)資源被釋放。由于線(xiàn)程被無(wú)限期地阻塞,因此程序不能正常運(yùn)行。簡(jiǎn)單的說(shuō)就是:線(xiàn)程死鎖時(shí),第一個(gè)線(xiàn)程等待第二個(gè)線(xiàn)程釋放資源,而同時(shí)第二個(gè)線(xiàn)程又在等待第一個(gè)線(xiàn)程釋放資源。這里舉一個(gè)通俗的例子:如在人行道上兩個(gè)人迎面相遇,為了給對(duì)方讓道,兩人同時(shí)向一側(cè)邁出一步,雙方無(wú)法通過(guò),又同時(shí)向另一側(cè)邁出一步,這樣還是無(wú)法通過(guò)。假設(shè)這種情況一直持續(xù)下去,這樣就會(huì)發(fā)生死鎖現(xiàn)象。

    導(dǎo)致死鎖的根源在于不適當(dāng)?shù)剡\(yùn)用“synchronized”關(guān)鍵詞來(lái)管理線(xiàn)程對(duì)特定對(duì)象的訪(fǎng)問(wèn)。“synchronized”關(guān)鍵詞的作用是,確保在某個(gè)時(shí)刻只有一個(gè)線(xiàn)程被允許執(zhí)行特定的代碼塊,因此,被允許執(zhí)行的線(xiàn)程首先必須擁有對(duì)變量或?qū)ο蟮呐潘栽L(fǎng)問(wèn)權(quán)。當(dāng)線(xiàn)程訪(fǎng)問(wèn)對(duì)象時(shí),線(xiàn)程會(huì)給對(duì)象加鎖,而這個(gè)鎖導(dǎo)致其它也想訪(fǎng)問(wèn)同一對(duì)象的線(xiàn)程被阻塞,直至第一個(gè)線(xiàn)程釋放它加在對(duì)象上的鎖。

    (1)死鎖問(wèn)題的一個(gè)代碼示例

    package com.px1987.j2se.thread.DeadLock;

    class Thread1 implements Runnable {

    private Object a;

    private Object b;

    public Thread1(Object a, Object b) {

    super();

    this.a = a;

    this.b = b;

    }

    public void run() {

    synchronized (a) {

    System.out.println(Thread1獲得對(duì)象a的鎖);

    try {

    Thread.sleep(1);

    }

    catch (InterruptedException e) {

    e.printStackTrace();

    }

    synchronized (b) {

    System.out.println(Thread1獲得對(duì)象b的鎖);

    try {

    Thread.sleep(1);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    }

    }

    package com.px1987.j2se.thread.DeadLock;

    class Thread2 implements Runnable {

    private Object a;

    private Object b;

    public Thread2(Object a, Object b) {

    super();

    this.a = a;

    this.b = b;

    }

    public void run() {

    synchronized (b) {

    System.out.println(Thread2獲得對(duì)象b的鎖);

    try {

    Thread.sleep(1);

    }

    catch (InterruptedException e) {

    e.printStackTrace();

    }

    synchronized (a) {

    System.out.println(Thread2獲得對(duì)象a的鎖);

    try {

    Thread.sleep(1);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    }

    }

    package com.px1987.j2se.thread.DeadLock;

    public class TestDeadLock {

    public static void main(String[] args) {

    Object a=new Object();

    Object b=new Object();

    Thread thread1=new Thread(new Thread1(a,b));

    Thread thread2=new Thread(new Thread2(a,b));

    thread1.start();

    thread2.start();

    }

    }

    下面是運(yùn)行結(jié)果:

    (2)死鎖問(wèn)題的另一個(gè)代碼示例

    package com.px1987.j2se.thread.DeadLock;

    public class ThreadDeadLock {

    public static void main(String[] args) {

    ThreadOne threadOne=new ThreadOne();

    ThreadTwo threadTwo=new ThreadTwo();

    String s1=s;

    String s2=sss;

    threadOne.op1=s1;

    threadTwo.op1=s1;

    threadOne.op2=s2;

    threadTwo.op2=s2;

    threadOne.start();

    threadTwo.start();

    }

    }

    class ThreadOne extends Thread{

    String op1;

    String op2;

    public void run(){// 同步中又有同步,就可能死鎖

    synchronized(op1){

    System.out.println(Thread.currentThread().getName()+鎖定op1);

    try {

    Thread.sleep(1000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    synchronized(op2){

    System.out.println(Thread.currentThread().getName()+鎖定op2);

    }

    }

    }

    }

    class ThreadTwo extends Thread{

    String op1;

    String op2;

    public void run(){

    synchronized(op2){

    System.out.println(Thread.currentThread().getName()+鎖定op2);

    try {

    Thread.sleep(1000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    synchronized(op1){

    System.out.println(Thread.currentThread().getName()+鎖定op1);

    }

    }

    }

    }

6.6生產(chǎn)者消費(fèi)者問(wèn)題

    1、生產(chǎn)者消費(fèi)者問(wèn)題的示例

    生產(chǎn)者消費(fèi)者問(wèn)題也是一個(gè)典型的線(xiàn)程問(wèn)題。我們舉一個(gè)這方面的實(shí)例來(lái)說(shuō)明:在一個(gè)果園里,有農(nóng)夫和小孩,農(nóng)夫會(huì)不停的采摘水果放入果園中心的一個(gè)水果筐直到水果筐滿(mǎn),而小孩會(huì)不停的從水果筐里拿水果來(lái)吃,直到水果拿完。分析這個(gè)模型我們可以看出:農(nóng)夫可以看成是一個(gè)生產(chǎn)者的線(xiàn)程,小孩可以看成是一個(gè)消費(fèi)者的線(xiàn)程,而大水果筐是共享資源。

    2、用Java程序表述的代碼示例

    package com.px1987.j2se.thread.ProducerConsumer;

    import java.util.Random;

    /*** 水果類(lèi)*/

    public class Fruit {

    /*** 水果編號(hào)*/

    private int id;

    /*** 水果編號(hào)計(jì)數(shù)器*/

    private static int number = 0;

    /*** 水果品種 */

    private String variety;

    /*** 水果品種數(shù)組 */

    private String[] varietys = 蘋(píng)果,桃子,梨子,香蕉,西瓜,荔枝,葡萄.split(,);

    public Fruit() {

    super();

    this.variety = varietys[new Random().nextInt(7)];

    this.id = ++number;

    }

    }

    水果筐應(yīng)該設(shè)計(jì)成類(lèi)似于棧的數(shù)據(jù)結(jié)構(gòu),其中包含一個(gè)數(shù)組來(lái)存放筐里的水果,而數(shù)組的下標(biāo)就是水果筐的容量。設(shè)定一個(gè)索引index表示指向下一個(gè)將要放入水果的位置。類(lèi)中的push方法模擬農(nóng)夫向水果筐中放入水果,pop方法模擬小孩從水果筐中拿水果。這兩個(gè)方法都要操作共享資源,所以push和pop方法都是同步互斥方法。

    3、如何避免出現(xiàn)死鎖

    那同步的問(wèn)題解決后是否會(huì)出現(xiàn)死鎖呢?大家試想一下,如果生產(chǎn)的速度大于消費(fèi)的速度就會(huì)導(dǎo)致功大于求,水果筐很容易就滿(mǎn)了,然而生產(chǎn)者又一直抱著水果筐不放,沒(méi)有機(jī)會(huì)給消費(fèi)者使用,消費(fèi)者不消費(fèi)生產(chǎn)者就無(wú)法生產(chǎn),所以就造成了死鎖。

    怎樣解決呢?在兩個(gè)同步互斥方法中用到了wait和notify方法,這兩個(gè)方法是為了防止死鎖的。

    l wait是Object類(lèi)的方法,它的作用是擁有互斥鎖的線(xiàn)程放棄鎖的使用權(quán),進(jìn)入wait池進(jìn)行等待,那么互斥鎖就有可能被其他線(xiàn)程獲得以執(zhí)行其他任務(wù)。

    l notify也是Object類(lèi)的方法,它的作用是從wait池中喚醒一條正在等待的線(xiàn)程進(jìn)入就緒狀態(tài),被喚醒的這條線(xiàn)程就很可能重新獲得cup和互斥鎖來(lái)完成它的任務(wù)。

    l notifyAll和Notify很相似,它是從wait池中喚醒所有正在等待的線(xiàn)程進(jìn)入就緒狀態(tài)。

    需要注意的是以上三個(gè)方法都只能在synchronized方法中應(yīng)用,否者會(huì)出現(xiàn)下面的異常信息:IllegalMonitorStateException:current thread not owner。

    4、實(shí)現(xiàn)的代碼示例

    package com.px1987.j2se.thread.ProducerConsumer;

    import java.text.DecimalFormat;

    import java.util.Arrays;

    /*** 水果框類(lèi),類(lèi)似一個(gè)棧的模型 */

    public class FruitBasket {

    /*** 容量為10的水果數(shù)組,也就是說(shuō)水果框最多能放下10個(gè)水果 */

    private Fruit[] fruits = new Fruit[10];

    /*** 下一個(gè)將要放入水果的位置*/

    private int index = 0;

    /*** 水果框中是否為空 @return true為空,false為不空 */

    public boolean isEmpty() {

    return index == 0 ? true : false;

    }

    /*** 水果框是否裝滿(mǎn)* @return true為滿(mǎn),false為未滿(mǎn)*/

    public boolean isFull() {

    return index == fruits.length ? true : false;

    }

    /*** 進(jìn)棧方法,模擬農(nóng)夫把水果放入筐中,@param name 農(nóng)夫的名字,@param fruit 水果對(duì)象 */

    public synchronized void push(String name, Fruit fruit) {

    //用while循環(huán),不用if,避免IndexOutOfBoundsException異常的產(chǎn)生

    while (isFull()) {

    //如果水果筐滿(mǎn)了,需要等待

    try {

    this.wait();

    }

    catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    //將水果放入index指示的位置,index再上移一格

    fruits[index++] = fruit;

    System.out.println(name + 向水果框中放入編號(hào)為 + fruit.getId() + 的+

    fruit.getVariety());

    display();

    this.notify(); //通知其他等待的農(nóng)夫或孩子可以開(kāi)始工作啦

    }

    /*** 出棧方法,模擬小孩從水果筐中取出水果,@param name 小孩的名字,@return 取出的水果*/

    public synchronized Fruit pop(String name) {

    //用while循環(huán),不用if,避免IndexOutOfBoundsException異常的產(chǎn)生

    while (isEmpty()) {

    try { //如果水果筐空,需要等待

    this.wait();

    }

    catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    Fruit fruit = null;

    fruit = fruits[--index]; //index下移一位,取出指示位置上的水果

    System.out.println(name + 從水果框中拿出編號(hào)為 + fruit.getId() + 的+

    fruit.getVariety());

    display();

    this.notify();

    return fruit;

    }

    /*** 顯示水果筐中水果存放情況*/

    public void display() {

    for (int i = 0; i < index; i++)

    System.out.printf(%-10s, NO: +

    new DecimalFormat(00).format(fruits[i].getId())

    + fruits[i].getVariety() + |);

    for (int i = index; i < fruits.length; i++) {

    System.out.printf(%-10s, 【 + (i + 1) + 】 |);

    }

    System.out.println();

    }

    }

    package com.px1987.j2se.thread.ProducerConsumer;

    import java.util.Random;

    /** 果園里的農(nóng)夫類(lèi),他是生產(chǎn)者,實(shí)現(xiàn)Runnable接口*/

    public class Farmer implements Runnable {

    /** 姓名*/

    private String name;

    /** 水果框*/

    private FruitBasket fruitBasket;

    /** 農(nóng)夫會(huì)不停地重復(fù)這一系列動(dòng)作:從水果樹(shù)上采摘一個(gè)水果放入水果框中,然后隨機(jī)的休息0-2秒*/

    public void run() {

    while (true) {

    fruitBasket.push(name, new Fruit());

    try {

    Thread.sleep(new Random().nextInt(2000));

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    public Farmer(String name, FruitBasket fruitBasket) {

    super();

    this.name = name;

    this.fruitBasket = fruitBasket;

    }

    }

    package com.px1987.j2se.thread.ProducerConsumer;

    import java.util.Random;

    /*** 果園中的小孩類(lèi),他是消費(fèi)者,實(shí)現(xiàn)Runnable接口*/

    public class Child implements Runnable {

    /*** 姓名*/

    private String name;

    /*** 水果框*/

    private FruitBasket fruitBasket;

    /*** 小孩會(huì)不停地重復(fù)這一系列動(dòng)作:從水果框中拿出水果吃,然后隨機(jī)休息0-5秒鐘*/

    public void run() {

    while (true) {

    fruitBasket.pop(name);

    try {

    Thread.sleep(new Random().nextInt(5000));

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    public Child(String name, FruitBasket fruitBasket) {

    super();

    this.name = name;

    this.fruitBasket = fruitBasket;

    }

    }

    package com.px1987.j2se.thread.ProducerConsumer;

    import java.util.Random;

    /*** 果園中的小孩類(lèi),他是消費(fèi)者,實(shí)現(xiàn)Runnable接口*/

    public class Child implements Runnable {

    /*** 姓名*/

    private String name;

    /*** 水果框*/

    private FruitBasket fruitBasket;

    /*** 小孩會(huì)不停地重復(fù)這一系列動(dòng)作:從水果框中拿出水果吃,然后隨機(jī)休息0-5秒鐘*/

    public void run() {

    while (true) {

    fruitBasket.pop(name);

    try {

    Thread.sleep(new Random().nextInt(5));

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    public Child(String name, FruitBasket fruitBasket) {

    super();

    this.name = name;

    this.fruitBasket = fruitBasket;

    }

    }

    測(cè)試時(shí)使用多個(gè)生產(chǎn)者線(xiàn)程和多個(gè)消費(fèi)者線(xiàn)程

    package com.px1987.j2se.thread.ProducerConsumer;

    /** 果園類(lèi),測(cè)試*/

    public class Orchard {

    public static void main(String[] args) {

    FruitBasket fruitBasket = new FruitBasket();

    Thread farmerThread1 = new Thread(new Farmer(農(nóng)夫1, fruitBasket));

    Thread farmerThread2 = new Thread(new Farmer(農(nóng)夫2, fruitBasket));

    Thread farmerThread3 = new Thread(new Farmer(農(nóng)夫3, fruitBasket));

    Thread childThread1 = new Thread(new Child(小孩1, fruitBasket));

    Thread childThread2 = new Thread(new Child(小孩2, fruitBasket));

    Thread childThread3 = new Thread(new Child(小孩3, fruitBasket));

    farmerThread1.start();

    farmerThread2.start();

    farmerThread3.start();

    childThread1.start();

    childThread2.start();

    childThread3.start();

    }

    }

    程序的運(yùn)行結(jié)果如下:

    農(nóng)夫1 向水果框中放入編號(hào)為1的蘋(píng)果

    NO:01蘋(píng)果 | 【2】 | 【3】 | 【4】 | 【5】 | 【6】 |

    農(nóng)夫3 向水果框中放入編號(hào)為2的荔枝

    NO:01蘋(píng)果 | NO:02荔枝 | 【3】 | 【4】 | 【5】 | 【6】 |

    小孩2 從水果框中拿出編號(hào)為2的荔枝

    NO:01蘋(píng)果 | 【2】 | 【3】 | 【4】 | 【5】 | 【6】 |

    農(nóng)夫2 向水果框中放入編號(hào)為3的香蕉

    NO:01蘋(píng)果 | NO:03香蕉 | 【3】 | 【4】 | 【5】 | 【6】 |

    小孩1 從水果框中拿出編號(hào)為3的香蕉

    NO:01蘋(píng)果 | 【2】 | 【3】 | 【4】 | 【5】 | 【6】 |

    小孩3 從水果框中拿出編號(hào)為1的蘋(píng)果

    【1】 | 【2】 | 【3】 | 【4】 | 【5】 | 【6】 |

    農(nóng)夫2 向水果框中放入編號(hào)為4的蘋(píng)果

    NO:04蘋(píng)果 | 【2】 | 【3】 | 【4】 | 【5】 | 【6】 |

    小孩1 從水果框中拿出編號(hào)為4的蘋(píng)果

    【1】 | 【2】 | 【3】 | 【4】 | 【5】 | 【6】 |

    農(nóng)夫1 向水果框中放入編號(hào)為5的蘋(píng)果

    NO:05蘋(píng)果 | 【2】 | 【3】 | 【4】 | 【5】 | 【6】 |

    農(nóng)夫3 向水果框中放入編號(hào)為6的西瓜

    NO:05蘋(píng)果 | NO:06西瓜 | 【3】 | 【4】 | 【5】 | 【6】 |

    農(nóng)夫2 向水果框中放入編號(hào)為7的蘋(píng)果

    NO:05蘋(píng)果 | NO:06西瓜 | NO:07蘋(píng)果 | 【4】 | 【5】 | 【6】 |

    小孩3 從水果框中拿出編號(hào)為7的蘋(píng)果

    NO:05蘋(píng)果 | NO:06西瓜 | 【3】 | 【4】 | 【5】 | 【6】 |

    小孩3 從水果框中拿出編號(hào)為6的西瓜

    NO:05蘋(píng)果 | 【2】 | 【3】 | 【4】 | 【5】 | 【6】 |

    小孩2 從水果框中拿出編號(hào)為5的蘋(píng)果

    【1】 | 【2】 | 【3】 | 【4】 | 【5】 | 【6】 |

    農(nóng)夫2 向水果框中放入編號(hào)為8的桃子

    NO:08桃子 | 【2】 | 【3】 | 【4】 | 【5】 | 【6】 |

    農(nóng)夫1 向水果框中放入編號(hào)為9的荔枝

    NO:08桃子 | NO:09荔枝 | 【3】 | 【4】 | 【5】 | 【6】 |

    農(nóng)夫3 向水果框中放入編號(hào)為10的香蕉

    NO:08桃子 | NO:09荔枝 | NO:10香蕉 | 【4】 | 【5】 | 【6】 |

    農(nóng)夫1 向水果框中放入編號(hào)為11的桃子

    NO:08桃子 | NO:09荔枝 | NO:10香蕉 | NO:11桃子 | 【5】 | 【6】 |

    農(nóng)夫1 向水果框中放入編號(hào)為12的荔枝

    NO:08桃子 | NO:09荔枝 | NO:10香蕉 | NO:11桃子 | NO:12荔枝 | 【6】 |

    農(nóng)夫3 向水果框中放入編號(hào)為13的西瓜

    NO:08桃子 | NO:09荔枝 | NO:10香蕉 | NO:11桃子 | NO:12荔枝 | NO:13西瓜 |

    小孩1 從水果框中拿出編號(hào)為13的西瓜

    NO:08桃子 | NO:09荔枝 | NO:10香蕉 | NO:11桃子 | NO:12荔枝 | 【6】 |

    農(nóng)夫2 向水果框中放入編號(hào)為14的西瓜

    NO:08桃子 | NO:09荔枝 | NO:10香蕉 | NO:11桃子 | NO:12荔枝 | NO:14西瓜 |

6.7 課后復(fù)習(xí)題

    1、線(xiàn)程的應(yīng)用示例——繼承Thread類(lèi)

    import java.util.*;

    public class ThreadExampleOne{

    public static void main(String args[]){

    TimeThread timeThread = new TimeThread();

    timeThread.start();

    }

    }

    class TimeThread extends Thread{

    Date nowTime;

    public void run(){ //重寫(xiě)run方法

    while(true){

    nowTime=new Date();

    System.out.println(現(xiàn)在的時(shí)間為:+nowTime.getHours()+

    :+nowTime.getMinutes()+:+nowTime.getSeconds());

    try{

    sleep(1000);

    }

    catch(InterruptedException e){

    System.out.println(e.toString());

    }

    }

    }

    }

    2、線(xiàn)程的應(yīng)用示例——實(shí)現(xiàn)Runnable接口

    import java.util.*;

    public class ThreadExampleTwo{

    public static void main(String args[]){

    TimeThread timeThreadObj = new TimeThread();

    }

    }

    class TimeThread extends Object implements Runnable{

    Date nowTime;

    Thread timeThread=null;

    public TimeThread(){

    timeThread=new Thread(this);

    timeThread.start();

    }

    public void run(){ //重寫(xiě)run方法

    while(true){

    nowTime=new Date();

    System.out.println(現(xiàn)在的時(shí)間為:+nowTime.getHours()+

    :+nowTime.getMinutes()+:+nowTime.getSeconds());

    try{

    timeThread.sleep(1000);

    }

    catch(InterruptedException e){

    System.out.println(e.toString());

    }

    }

    }

    }

    3、線(xiàn)程優(yōu)先級(jí)的應(yīng)用示例

    class ThreadPriorityDemo extends Thread{

    ThreadPriorityDemo(String strName){

    System.out.println(線(xiàn)程名稱(chēng):+strName);

    }

    public void run(){

    //打印出當(dāng)前線(xiàn)程的優(yōu)先級(jí)

    System.out.println(this.getPriority());

    }

    }

    class PriorityDemo{

    public static void main(String args[]){

    ThreadPriorityDemo aThreadDemo = new ThreadPriorityDemo(Thread A);

    aThreadDemo.setPriority(Thread.MAX_PRIORITY);

    aThreadDemo.start();

    ThreadPriorityDemo bThreadDemo = new ThreadPriorityDemo(Thread B);

    bThreadDemo.setPriority(Thread.MIN_PRIORITY);

    bThreadDemo.start();

    ThreadPriorityDemo cThreadDemo = new ThreadPriorityDemo(Thread C);

    cThreadDemo.setPriority(Thread.NORM_PRIORITY);

    cThreadDemo.start();

    ThreadPriorityDemo dThreadDemo = new ThreadPriorityDemo(Thread D);

    dThreadDemo.setPriority(Thread.MIN_PRIORITY);

    dThreadDemo.start();

    ThreadPriorityDemo eThreadDemo = new ThreadPriorityDemo(Thread E);

    eThreadDemo.setPriority(Thread.MIN_PRIORITY);

    eThreadDemo.start();

    }

    }

最新文章