多線程練習(xí)題

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


思路

  1. 搞兩條線程,一條線程打印奇數(shù)任務(wù),一條線程打印偶數(shù)任務(wù)。
  2. 為了防止線程間的無(wú)序爭(zhēng)搶?zhuān)褂胹ynchronized鎖。
  3. 為了保證交互打印,使用wait/notify,打印完一條先等待,讓別的線程打印。以此循環(huán)。

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

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ù)方法里面上來(lái)先notify,然后結(jié)束調(diào)用wait?

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

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

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

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();
        
    }
}

說(shuō)明:通過(guò)余數(shù)判斷來(lái)執(zhí)行,需要循環(huán)30次,有點(diǎn)不合理。想了下,執(zhí)行10次,循環(huán)10次,但是讓線程按照ABC執(zhí)行就可以了。根據(jù)這個(gè)想法寫(xiě)出了改進(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)前對(duì)象的。而this在線程的環(huán)境下,代表的是當(dāng)前線程實(shí)例對(duì)象本身。所以this.getName是當(dāng)前線程實(shí)例對(duì)象的線程名稱是什么。

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

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


② CountOperate初始化代碼說(shuō)明:

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類(lèi),所以當(dāng)前線程的名稱是main,狀態(tài)是true。而當(dāng)前CountOperate線程實(shí)例為T(mén)hread-0,沒(méi)有啟動(dòng)所以狀態(tài)為false。


③ Thread1線程說(shuō)明:

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

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


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

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容