多線程練習(xí)題

多線程交替打印1~10的奇偶數(shù)


思路

  1. 搞兩條線程匕垫,一條線程打印奇數(shù)任務(wù)狰腌,一條線程打印偶數(shù)任務(wù)除破。
  2. 為了防止線程間的無序爭搶,使用synchronized鎖琼腔。
  3. 為了保證交互打印瑰枫,使用wait/notify,打印完一條先等待,讓別的線程打印光坝。以此循環(huán)尸诽。

實(shí)現(xiàn)1:synchronized方法對象鎖

public class MyTask {

    public synchronized void printNumber(int i) {
        try {
            this.notify();
            System.out.println(Thread.currentThread().getName() + " " + i);
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class SwapPrint {

    public static void main(String[] args) {
        final MyTask myTask = new MyTask();
        
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 1 ; i <= 10 ; i+=2) {
                    myTask.printNumber(i);
                }
            }
        });
        
        t1.setName("Thread t1");
        t1.start();
        
        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 2 ; i <= 10 ; i+=2) {
                    myTask.printNumber(i);
                }
            }
        });
        
        t2.setName("Thread t2");
        t2.start();
    }
}

思考:
有的人會(huì)搞不懂業(yè)務(wù)方法里面上來先notify,然后結(jié)束調(diào)用wait盯另?

  1. 首先調(diào)用notify方法性含,將另一個(gè)線程喚醒。但是另一個(gè)線程根本進(jìn)不來當(dāng)前的方法(因?yàn)橛衧ynchronized鎖)鸳惯。
  2. 后面調(diào)用wait方法商蕴,將當(dāng)前線程等待,wait方法會(huì)釋放鎖芝发,另一個(gè)線程就可以進(jìn)來了绪商。

實(shí)現(xiàn)2:synchronized(this)對象鎖

這種實(shí)現(xiàn)和實(shí)現(xiàn)1沒什么區(qū)別,實(shí)現(xiàn)1雖然是synchronized修飾方法的方式辅鲸,實(shí)際上還是使用當(dāng)前對象的鎖格郁。

public class MyTask {

    public void printNumber(int i) {
        try {
            synchronized (this) {
                this.notify();
                System.out.println(Thread.currentThread().getName() + " " + i);
                this.wait();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

思考:
synchronized關(guān)鍵字只是控制當(dāng)前代碼區(qū)域,只能有一個(gè)線程進(jìn)入瓢湃。
wait/notify方法可以讓當(dāng)前線程等待理张,也可以喚醒其他線程。

實(shí)現(xiàn)3:等待隊(duì)列Condition實(shí)現(xiàn)

public class MyTask {

    private ReentrantLock rl = new ReentrantLock();
    private Condition condition = rl.newCondition();
    
    public void printNumber(int i) {
        try {
            rl.lock();
            condition.signal();
            System.out.println(Thread.currentThread().getName() + " " + i);
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rl.unlock();
        }
    }
}

思考:

  1. 調(diào)用lock方法绵患,當(dāng)前線程設(shè)置同步狀態(tài),加入到aqs同步隊(duì)列中悟耘,獲取重入鎖落蝙。
  2. 調(diào)用signal方法,喚醒等待隊(duì)列中的線程暂幼,然后加入到aqs同步隊(duì)列筏勒。
  3. 調(diào)用await方法,將當(dāng)前線程加入到aqs等待隊(duì)列旺嬉。

ABC三個(gè)線程如何保證順序執(zhí)行


題目

ABC三個(gè)線程如何保證順序執(zhí)行管行。三個(gè)線程同時(shí)啟動(dòng),然后按照順序執(zhí)行邪媳,每個(gè)線程執(zhí)行10次捐顷。

思路

首先想到了等待隊(duì)列Condition喚醒部分線程,使用ReentrantLock進(jìn)行加鎖雨效。

初始版實(shí)現(xiàn)

/**
 * @description A\B\C三個(gè)線程順序執(zhí)行10次
 * 
 * @author sunpy
 * @date 2018年11月28日  下午2:23:45
 */
public class MyTest {

    static class MyTask {
        private static ReentrantLock rl = new ReentrantLock();
        private static Condition conditionA = rl.newCondition();
        private static Condition conditionB = rl.newCondition();
        private static Condition conditionC = rl.newCondition();
        private static int number = 0;
    
        public void execute() {
            rl.lock();
            
            try {
                while (number < 30) {
                    if (number % 3 == 0) {
                        System.out.println(Thread.currentThread().getName() + " - " + number);
                        number++;
                        conditionB.signal();
                        conditionA.await();
                    }
                    
                    if (number % 3 == 1) {
                        System.out.println(Thread.currentThread().getName() + " - " + number);
                        number++;
                        conditionC.signal();
                        conditionB.await();
                    }
                    
                    if (number % 3 == 2) {
                        System.out.println(Thread.currentThread().getName() + " - " + number);
                        number++;
                        conditionA.signal();
                        conditionC.await();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                rl.unlock();
            }
        }
    }
    
    
    public static void main(String[] args) {
        final MyTask myTask = new MyTask();
        new Thread(new Runnable() {

            @Override
            public void run() {
                myTask.execute();
            }
        }, "A").start();
        Thread.sleep(1000);
        new Thread(new Runnable() {

            @Override
            public void run() {
                myTask.execute();
            }
        }, "B").start();
        Thread.sleep(1000);
        new Thread(new Runnable() {

            @Override
            public void run() {
                myTask.execute();
            }
        }, "C").start();
        
    }
}

說明:通過余數(shù)判斷來執(zhí)行迅涮,需要循環(huán)30次,有點(diǎn)不合理徽龟。想了下叮姑,執(zhí)行10次,循環(huán)10次据悔,但是讓線程按照ABC執(zhí)行就可以了传透。根據(jù)這個(gè)想法寫出了改進(jìn)版耘沼。

改進(jìn)版實(shí)現(xiàn)

/**
 * @description A\B\C三個(gè)線程順序執(zhí)行10次
 * 
 * @author sunpy
 * @date 2018年11月28日  下午2:23:45
 */
public class MyTest {

    static class MyTask {
        private static ReentrantLock rl = new ReentrantLock();
        private static Condition conditionA = rl.newCondition();
        private static Condition conditionB = rl.newCondition();
        private static Condition conditionC = rl.newCondition();
    
        public void execute(String flag) {
            rl.lock();
            
            try {
                for (int i = 1 ; i <= 10 ; i++) {
                    if ("A".equals(flag)) {
                        System.out.println(Thread.currentThread().getName() + " - " + i);
                        conditionB.signal();
                        conditionA.await();
                    }
                    
                    if ("B".equals(flag)) {
                        System.out.println(Thread.currentThread().getName() + " - " + i);
                        conditionC.signal();
                        conditionB.await();
                    }
                    
                    if ("C".equals(flag)) {
                        System.out.println(Thread.currentThread().getName() + " - " + i);
                        conditionA.signal();
                        conditionC.await();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                rl.unlock();
            }
        }
    }
}

Thread.currentThread.getName和this.getName的區(qū)別


this.getName

this的意思是代表當(dāng)前對象的。而this在線程的環(huán)境下朱盐,代表的是當(dāng)前線程實(shí)例對象本身耕拷。所以this.getName是當(dāng)前線程實(shí)例對象的線程名稱是什么。

Thread.currentThread.getName

Thread.currentThread.getName意思是在當(dāng)前代碼塊中執(zhí)行的線程名稱是什么托享。

例子

public class CountOperate extends Thread {
    
    public CountOperate() {
        System.out.println("CountOperate---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("CountOperate---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 Test {
    
    public static void main(String[] args) {
        CountOperate c = new CountOperate();
        Thread t1 = new Thread(c);
        System.out.println("main begin t1 isAlive=" + t1.isAlive());
        t1.start();
        System.out.println("main end t1 isAlive=" + t1.isAlive());
    }
}

結(jié)果:

CountOperate---begin
Thread.currentThread().getName()=main
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
CountOperate---end
main begin t1 isAlive=false
main end t1 isAlive=true
run---begin
Thread.currentThread().getName()=Thread-1
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
run---end

說明:
① 首先搞明白該例子中一共有三條線程:
main線程:執(zhí)行main方法的線程骚烧。
Thread-0線程:當(dāng)前CountOperate線程實(shí)例的線程。
Thread-1線程:new Thread類創(chuàng)建的線程闰围。


② CountOperate初始化代碼說明:

CountOperate---begin
Thread.currentThread().getName()=main
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
CountOperate---end

執(zhí)行構(gòu)造器赃绊,然后當(dāng)前main方法中執(zhí)行初始化CountOperate類,所以當(dāng)前線程的名稱是main羡榴,狀態(tài)是true碧查。而當(dāng)前CountOperate線程實(shí)例為Thread-0,沒有啟動(dòng)所以狀態(tài)為false校仑。


③ Thread1線程說明:

main begin t1 isAlive=false
main end t1 isAlive=true

Thread-1線程的線程執(zhí)行器沒有啟動(dòng)忠售,那么狀態(tài)為false。執(zhí)行start方法迄沫,Thread-1線程的線程執(zhí)行器啟動(dòng)稻扬,那么狀態(tài)就為true了。


④ run方法執(zhí)行說明:

run---begin
Thread.currentThread().getName()=Thread-1
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
run---end

首先執(zhí)行run方法的線程為Thread-1羊瘩,所以當(dāng)前代碼塊中的線程為Thread1(Thread.currentThread().getName()=Thread-1)泰佳,狀態(tài)自然為true。
但是this.getName()是代表當(dāng)前線程Thread-0的實(shí)例尘吗,因?yàn)镃ountOperate線程根本沒有啟動(dòng)逝她,所以狀態(tài)為false。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末睬捶,一起剝皮案震驚了整個(gè)濱河市黔宛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌擒贸,老刑警劉巖臀晃,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異酗宋,居然都是意外死亡积仗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進(jìn)店門蜕猫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寂曹,“玉大人,你說我怎么就攤上這事÷≡玻” “怎么了漱挚?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵,是天一觀的道長渺氧。 經(jīng)常有香客問我旨涝,道長,這世上最難降的妖魔是什么侣背? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任白华,我火速辦了婚禮,結(jié)果婚禮上贩耐,老公的妹妹穿的比我還像新娘弧腥。我一直安慰自己,他們只是感情好潮太,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布管搪。 她就那樣靜靜地躺著,像睡著了一般铡买。 火紅的嫁衣襯著肌膚如雪更鲁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天奇钞,我揣著相機(jī)與錄音澡为,去河邊找鬼。 笑死蛇券,一個(gè)胖子當(dāng)著我的面吹牛缀壤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纠亚,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼筋夏!你這毒婦竟也來了蒂胞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤条篷,失蹤者是張志新(化名)和其女友劉穎骗随,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赴叹,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鸿染,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了乞巧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涨椒。...
    茶點(diǎn)故事閱讀 40,928評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蚕冬,到底是詐尸還是另有隱情免猾,我是刑警寧澤,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布囤热,位于F島的核電站猎提,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏旁蔼。R本人自食惡果不足惜锨苏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望棺聊。 院中可真熱鬧伞租,春花似錦、人聲如沸躺屁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽犀暑。三九已至驯击,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間耐亏,已是汗流浹背徊都。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留广辰,地道東北人暇矫。 一個(gè)月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像择吊,于是被迫代替她去往敵國和親李根。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,937評論 2 361

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