Java多線程基礎複習及多線程面試題彙總

Java多線程

程序、進程和線程

程序(program)

概念:是爲完成特定任務、用某種語言編寫的一組指令的集合。即指一段靜態的代碼

進程(process)

概念:程序的一次執行過程,或是正在運行的一個程序
說明:進程作爲資源分配的單位,系統在運行時會爲每個進程分配不同的內存區域

線程(thread)

概念:進程可進一步細化爲線程,是一個程序內部的一條執行路徑
說明:線程作爲調度和執行的單位,每個線程擁獨立的運行棧和程序計數器(pc),線程切換的開銷小。

JVM內存結構圖

在這裏插入圖片描述

進程可以細化爲多個線程。

每個線程,擁有自己獨立的:棧、程序計數器

多個線程,共享同一個進程中的結構:方法區、堆。

並行與併發

  1. 單核CPU與多核CPU的理解:

    單核CPU,其實是一種假的多線程,因爲在一個時間單元內,也只能執行一個線程的任務。例如:雖然有多車道,但是收費站只有一個工作人員在收費,只有收了費才能通過,那麼CPU就好比收費人員。如果某個人不想交錢,那麼收費人員可以把他“掛起”(晾着他,等他想通了,準備好了錢,再去收費),但是因爲CPU時間單元特別短,因此感覺不出來。
    如果是多核的話,才能更好的發揮多線程的效率。(現在的服務器都是多核的)
    一個Java應用程序java.exe,其實至少三個線程:main()主線程,gc()垃圾回收線程,異常處理線程。當然如果發生異常,會影響主線程。

  2. 並行與併發的理解:

    • 並行:多個CPU同時執行多個任務。比如:多個人同時做不同的事。
    • 併發:一個CPU(採用時間片)同時執行多個任務。比如:秒殺、多個人做同一件事

Thread類的構造器

  1. Thread():創建新的Thread對象
  2. Thread(String threadName):創建線程並指定線程實例名
  3. Thread(Runnable target):指定創建線程的目標對象,它實現了Runnable接口中的run方法
  4. Thread(Runnable target, String name):創建新的Thread對象

繼承Thread類方式創建多線程

  1. 創建一個繼承於Thread類的子類(extends Thread)

  2. 子類重寫Thread類的run()方法 --> 將此線程執行的操作寫在run()方法體中

  3. 創建Thread類的子類對象(創建線程對象)

  4. 通過此對象調用start():①啓動當前線程 ② 調用當前線程的run()

  • 說明:
    1. 問題一:我們啓動一個線程,必須調用start(),不能調用run()(相當於main線程調用普通方法)的方式啓動線程。
    2. 問題二:如果再啓動一個線程,必須重新創建一個Thread子類的對象,調用此對象的start()。
//1. 創建一個繼承於Thread類的子類
class MyThread extends Thread {
    //2. 重寫Thread類的run()
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class ThreadTest {
    public static void main(String[] args) {
        //3. 創建Thread類的子類的對象
        MyThread t1 = new MyThread();

        //4.通過此對象調用start():①啓動當前線程 ② 調用當前線程的run()
        t1.start();
        //問題一:我們不能通過直接調用run()的方式啓動線程。
//        t1.run();

        //問題二:再啓動一個線程,遍歷100以內的偶數。不可以還讓已經start()的線程去執行。會報IllegalThreadStateException
//        t1.start();
        //我們需要重新創建一個線程的對象
        MyThread t2 = new MyThread();
        t2.start();

        //如下操作仍然是在main線程中執行的。
        for (int i = 0; i < 100; i++) {
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i + "***********main()************");
            }
        }
    }
}

實現Runnable接口方式創建多線程

  1. 創建一個實現了Runnable接口的類(implement Runnable)

  2. 實現類去實現Runnable中的抽象方法:run()

  3. 創建實現類的對象

  4. 將此對象作爲參數傳遞到Thread類的構造器中,創建Thread類的對象

  5. 通過Thread類的對象調用start()

//1. 創建一個實現了Runnable接口的類
class MThread implements Runnable{
    //2. 實現類去實現Runnable中的抽象方法:run()
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class ThreadTest1 {
    public static void main(String[] args) {
        //3. 創建實現類的對象
        MThread mThread = new MThread();
        //4. 將此對象作爲參數傳遞到Thread類的構造器中,創建Thread類的對象
        Thread t1 = new Thread(mThread);
        t1.setName("線程1");
        //5. 通過Thread類的對象調用start():① 啓動線程 ②調用當前線程的run()-->調用了Runnable類型的target的run()
        t1.start();

        //再啓動一個線程,遍歷100以內的偶數
        Thread t2 = new Thread(mThread);
        t2.setName("線程2");
        t2.start();
    }
}

兩種方式的對比

  • 開發中:優先選擇:實現Runnable接口的方式

    原因:

    1. 實現的方式沒有類的單繼承侷限性

    2. 實現的方式更適合來處理多個線程共享數據的情況(類的屬性共享相當於添加static修飾了屬性)

  • 聯繫:public class Thread implements Runnable(Thread類本身也實現了Runnable接口 )

  • 相同點:

    兩種方式都需要重寫run(),將線程要執行的邏輯聲明在run()中。
    兩種方式啓動線程,都是調用的Thread類中的start()。

Thread類中的常用方法

  1. void start():啓動當前線程;調用當前線程的**run()**方法

  2. run(): 通常需要重寫Thread類中的此方法,將創建的線程要執行的操作聲明在此方法中

  3. static Thread currentThread():返回當前線程的靜態方法

  4. String getName():獲取當前線程的名字

  5. void setName(String name):設置當前線程的名字

  6. static void yield():線程讓步,釋放當前cpu的執行權(若隊列中沒有同優先級的線程,忽略此方法)

  7. join():在線程a中調用線程b的join(),此時線程a就進入阻塞狀態,直到線程b完全執行完以後,線程a才結束阻塞狀態

  8. stop():已過時。當執行此方法時,強制結束當前線程

  9. static void sleep(long millis):讓當前線程“睡眠”指定的millitime毫秒。在指定的millitime毫秒時間內,當前線程是阻塞狀態

  10. boolean isAlive():判斷當前線程是否存活

線程的優先級

  1. 線程的優先級等級:

    • MAX_PRIORITY:10

    • MIN_PRIORITY:1

    • NORM_PRIORITY:5 (爲默認優先級)

  2. 如何獲取和設置當前線程的優先級:

    • getPriority():獲取線程的優先級

    • setPriority(int newPriority):設置/改變線程的優先級

      說明:

      1. 線程創建時繼承父線程的優先級
      2. 高優先級的線程要搶佔低優先級線程cpu的執行權。但只是從概率上講,高優先級的線程高概率的情況下被執行。並不意味着只當高優先級的線程執行完以後,低優先級的線程才執行。
  3. 線程通信:wait() 、notify() 、notifyAll() :此三個方法定義在Object類中的

  • 線程的分類:一種是守護線程,一種是用戶線程
    • 守護線程是用來服務用戶線程的,通過在start()方法前調用**thread.setDaemon(true)**可以把一個用戶線程變成一個守護線程。
    • Java垃圾回收就是一個典型的守護線程。
    • 若JVM中都是守護線程,當前JVM將退出。

Thread的生命週期

在這裏插入圖片描述

線程同步機制

  • 通過同步機制,來解決線程的安全問題
  1. 方式一:同步代碼塊

    1. 操作共享數據的代碼,即爲需要被同步的代碼。 -->不能包含代碼多了,也不能包含代碼少了。

    2. 共享數據:多個線程共同操作的變量。比如:ticket就是共享數據。

    3. 同步監視器,俗稱:鎖。任何一個類的對象,都可以充當鎖。要求:多個線程必須要共用同一把鎖。

    •   補充:在實現Runnable接口創建多線程的方式中,我們可以考慮使用this充當同步監視器。在繼承Thread類創建多線程的方式中,慎用this充當同步監視器,考慮使用當前類充當同步監視器。
      
  2. 方式二:同步方法

    如果操作共享數據的代碼完整的聲明在一個方法中,我們可以將此方法聲明同步的。

    • 關於同步方法的總結:

      1. 同步方法仍然涉及到同步監視器,只是不需要我們顯式的聲明。

      2. 非靜態的同步方法,同步監視器是:this

      3. 靜態的同步方法,同步監視器是:當前類本身

  3. 方式三:Lock鎖 — JDK5.0新增

    class Window implements Runnable{
    
        private int ticket = 100;
        //1.實例化ReentrantLock
        private ReentrantLock lock = new ReentrantLock();
    
        @Override
        public void run() {
            while(true){
                try{
        //2.調用鎖定方法lock()
                    lock.lock();
    
                    if(ticket > 0){
    
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        System.out.println(Thread.currentThread().getName() + ":售票,票號爲:" + ticket);
                        ticket--;
                    }else{
                        break;
                    }
                }finally {
        //3.調用解鎖方法:unlock()
                    lock.unlock();
                }
    
            }
        }
    }
    

死鎖的理解

  • 不同的線程分別佔用對方需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就形成了線程的死鎖

  • 說明:

    1. 出現死鎖後,不會出現異常,不會出現提示,只是所的線程都處於阻塞狀態,無法繼續
    2. 我們使用同步時,要避免出現死鎖。
  • 解決方法

    1. 使用專門的算法、原則
    2. 儘量減少同步資源的定義
    3. 儘量避免嵌套同步

線程通信三個方法

  1. wait():一旦執行此方法,當前線程就進入阻塞狀態,並釋放同步監視器
  2. notify():一旦執行此方法,就會喚醒被wait()的一個線程。如果有多個線程被wait,就喚醒優先級高的那個。
  3. notifyAll():一旦執行此方法,就會喚醒所有被wait的線程
  • 說明:
    1. wait(),notify(),notifyAll()三個方法必須使用在同步代碼塊或同步方法中。
    2. wait(),notify(),notifyAll()三個方法的調用者必須是同步代碼塊或同步方法中的同步監視器。否則,會出現IllegalMonitorStateException異常。(同步監視器應相同!!)
    3. wait(),notify(),notifyAll()三個方法是定義在java.lang.Object類中。

釋放鎖和不釋放鎖的情況

  • 釋放鎖

    1. 當前線程的同步方法、同步代碼塊執行結束。
    2. 當前線程在同步代碼塊、同步方法中遇到break、return終止了該代碼塊、該方法的繼續執行。
    3. 當前線程在同步代碼塊、同步方法中出現了未處理的Error或Exception,導致異常結束。
    4. 當前線程在同步代碼塊、同步方法中執行了線程對象的**wait()**方法,當前線程暫停,並釋放鎖。
  • 不釋放鎖線

    1. 程執行同步代碼塊或同步方法時,程序調用**Thread.sleep()、Thread.yield()**方法暫停當前線程的執行
    2. 線程執行同步代碼塊時,其他線程調用了該線程的suspend()方法將該線程掛起,該線程不會釋放鎖(同步監視器)。應儘量避免使用suspend()和resume()來控制線程(方法已過時)

生產者消費者問題

  • 生產者(Productor)將產品交給店員(Clerk),而消費者(Customer)從店員處取走產品,店員一次只能持有固定數量的產品(比如:20),如果生產者試圖生產更多的產品,店員會叫生產者停一下,如果店中有空位放產品了再通知生產者繼續生產;如果店中沒有產品了,店員會告訴消費者等一下,如果店中有產品了再通知消費者來取走產品。

  • 分析:

  1. 是否是多線程問題?是,生產者線程,消費者線程

  2. 是否有共享數據?是,店員(或產品)

  3. 如何解決線程的安全問題?同步機制,有三種方法

  4. 是否涉及線程的通信?是

class Clerk{
    private int productCount = 0;
    //生產產品
    public synchronized void produceProduct() {

        if(productCount < 20){
            productCount++;
            System.out.println(Thread.currentThread().getName() + ":開始生產第" + productCount + "個產品");
	//喚醒
            notify();
        }else{
            //等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //消費產品
    public synchronized void consumeProduct() {
        if(productCount > 0){
            System.out.println(Thread.currentThread().getName() + ":開始消費第" + productCount + "個產品");
            productCount--;

            notify();
        }else{
            //等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Producer extends Thread{//生產者

    private Clerk clerk;
    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println(getName() + ":開始生產產品.....");
        while(true){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.produceProduct();
        }
    }
}

class Consumer extends Thread{//消費者
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }
    @Override
    public void run() {
        System.out.println(getName() + ":開始消費產品.....");
        while(true){
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.consumeProduct();
        }
    }
}

public class ProductTest {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Producer p1 = new Producer(clerk);
        p1.setName("生產者1");

        Consumer c1 = new Consumer(clerk);
        c1.setName("消費者1");
        Consumer c2 = new Consumer(clerk);
        c2.setName("消費者2");
        p1.start();
        c1.start();
        c2.start();
    }
}

實現Callable接口創建多線程

如何理解實現Callable接口的方式創建多線程比實現Runnable接口創建多線程方式強大?

  1. call()可以有返回值的。

  2. call()可以拋出異常,被外面的操作捕獲,獲取異常的信息

  3. Callable是支持泛型

//1.創建一個實現Callable的實現類
class NumThread implements Callable{
    //2.實現call方法,將此線程需要執行的操作聲明在call()中
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            if(i % 2 == 0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}


public class ThreadNew {
    public static void main(String[] args) {
        //3.創建Callable接口實現類的對象
        NumThread numThread = new NumThread();
        //4.將此Callable接口實現類的對象作爲傳遞到FutureTask構造器中,創建FutureTask的對象
        FutureTask futureTask = new FutureTask(numThread);
        //5.將FutureTask的對象作爲參數傳遞到Thread類的構造器中,創建Thread對象,並調用start()
        new Thread(futureTask).start();

        try {
            //6.獲取Callable中call方法的返回值
            //get()返回值即爲FutureTask構造器參數Callable實現類重寫的call()的返回值。
            Object sum = futureTask.get();
            System.out.println("總和爲:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

使用線程池創建多線程

好處:

  1. 提高響應速度(減少了創建新線程的時間)

  2. 降低資源消耗(重複利用線程池中線程,不需要每次都創建)

  3. 便於線程管理

    • corePoolSize:核心池的大小
    • maximumPoolSize:最大線程數
    • keepAliveTime:線程沒有任務時最多保持多長時間後會終止
public class ThreadPool {
    public static void main(String[] args) {
        //1. 提供指定線程數量的線程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //設置線程池的屬性
//        System.out.println(service.getClass());
//        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();


        //2.執行指定的線程的操作。需要提供實現Runnable接口或Callable接口實現類的對象
        service.execute(new NumberThread());//適合適用於Runnable
        service.execute(new NumberThread1());//適合適用於Runnable

//        service.submit(Callable callable);//適合使用於Callable
        //3.關閉連接池
        service.shutdown();
    }
}
class NumberThread implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

class NumberThread1 implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

面試題

談談你對程序、進程、線程的理解

  1. 程序:指一段靜態的代碼
  2. 進程:正在運行的一個程序
  3. 線程:一個程序內部的一條執行路徑

寫一個線程安全的單例模式。

餓漢式、懶漢式:

//懶漢式
class Bank{
    private Bank(){
        
    }
    private static Bank instance = null;
    
    public static Bank getInstance(){
        if(instance == null){
            synchronized (Bank.class) {
                if(instance == null){
                    instance = new Bank();
                }
            }
        }
        return instance;
    }
}

synchronized 與 Lock的異同?

  • 相同點:二者都可以解決線程安全問題

  • 不同:

    • synchronized機制在執行完相應的同步代碼以後,自動的釋放同步監視器
    • Lock需要手動的啓動同步(lock()),同時結束同步也需要手動的實現(unlock())

    優先使用順序:

    Lock --> 同步代碼塊(已經進入了方法體,分配了相應資源) --> 同步方法(在方法體之外)

sleep() 和 wait()的異同?

  • 相同點:一旦執行方法,都可以使得當前的線程進入阻塞狀態。

  • 不同點:

    1. 兩個方法聲明的位置不同:Thread類中聲明sleep() , Object類中聲明wait()
    2. 調用的要求不同:sleep()可以在任何需要的場景下調用。 wait()必須使用在同步代碼塊或同步方法中
    3. 關於是否釋放同步監視器:如果兩個方法都使用在同步代碼塊或同步方法中,sleep()不會釋放鎖,wait()會釋放鎖。

兩個線程交替打印1-100?

class Communication implements Runnable {
	int i = 1;
	public void run() {
		while (true) {
			synchronized (this) {
				notify();
				if (i <= 100) {
					System.out.println(Thread.currentThread().getName() + ":" + i++);
				} else
					break;
				try {
					wait();
				} catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
        }
    }
}

Java中多線程的創建有幾種方式?(四種)

用什麼關鍵字修飾同步方法?

stop()和suspend()方法爲何不推薦使用?

  1. 繼承Thread類方式創建多線程(1.5前)
  2. 實現Runnable接口方式創建多線程(1.5前)
  3. 實現Callable接口創建多線程
  4. 使用線程池創建多線程

synchronized關鍵字修飾同步方法


反對使用stop(),是因爲它不安全。它會解除由線程獲取的所有鎖定,而且如果對象處於一種不連貫狀態,那麼其他線程能在那種狀態下檢查和修改它們。結果很難檢查出真正的問題所在。suspend()方法容易發生死鎖。調用suspend()的時候,目標線程會停下來,但卻仍然持有在這之前獲得的鎖定。此時,其他任何線程都不能訪問鎖定的資源,除非被"掛起"的線程恢復運行。對任何線程來說,如果它們想恢復目標線程,同時又試圖使用任何一個鎖定的資源,就會造成死鎖。所以不應該使用suspend(),而應在自己的Thread類中置入一個標誌,指出線程應該活動還是掛起。若標誌指出線程應該掛起,便用wait()命其進入等待狀態。若標誌指出線程應當恢復,則用一個notify()重新啓動線程。

sleep() 和 wait() 有什麼區別?

  • sleep是線程類(Thread)的方法,導致此線程暫停執行指定時間,給執行機會給其他線程,但是監控狀態依然保持,到時後會自動恢復。調用sleep不會釋放對象鎖。
  • wait是Object類的方法,對此對象調用wait方法導致本線程放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象發出notify方法(或notifyAll)後本線程才進入對象鎖定池準備獲得對象鎖進入運行狀態。

同步和異步有何異同,在什麼情況下分別使用他們?舉例說明。

答:如果數據將在線程間共享。例如正在寫的數據以後可能被另一個線程讀到,或者正在讀的數據可能已經被另一個線程寫過了,那麼這些數據就是共享數據,必須進行同步存取。

當應用程序在對象上調用了一個需要花費很長時間來執行的方法,並且不希望讓程序等待方法的返回時,就應該使用異步編程,在很多情況下采用異步途徑往往更有效率。

啓動一個線程是用run()還是start()?

答:啓動一個線程是調用start()方法,使線程所代表的虛擬處理機處於可運行狀態,這意味着它可以由JVM調度並執行。這並不意味着線程就會立即運行。run()方法就是正常的對象調用方法的執行,並不是使用分線程來執行的。

當一個線程進入一個對象的一個synchronized方法後,其它線程是否可進入此對象的其它方法?

答:不能,一個對象的一個synchronized方法只能由一個線程訪問。

請說出你所知道的線程同步的方法。

答:wait():使一個線程處於等待狀態,並且釋放所持有的對象的lock。

sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要捕捉InterruptedException異常。

notify():喚醒一個處於等待狀態的線程,注意的是在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM確定喚醒哪個線程,而且不是按優先級。

notityAll():喚醒所有處入等待狀態的線程,注意並不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。

多線程有幾種實現方法,都是什麼?同步有幾種實現方法,都是什麼?

答:多線程有兩種實現方法,分別是繼承Thread類與實現Runnable接口

同步的實現方面有兩種,分別是synchronized,wait與notify

線程的基本概念、線程的基本狀態以及狀態之間的關係

答:線程指在程序執行過程中,能夠執行程序代碼的一個執行單位,每個程序至少都有一個線程,也就是程序本身。

Java中的線程有四種狀態分別是:創建、就緒、運行、阻塞、結束

簡述synchronized和java.util.concurrent.locks.Lock的異同 ?

答:主要相同點:Lock能完成synchronized所實現的所有功能

主要不同點:Lock有比synchronized更精確的線程語義和更好的性能。synchronized會自動釋放鎖,而Lock一定要求程序員手工釋放,並且必須在finally從句中釋放。

案例:三個線程間的通訊

public class Demo01 {
	public static void main(String[] args) {
		//三個線程間的通訊
		MyTask task = new MyTask();
		new Thread(){
			public void run() {
				while(true){
					try {
						task.task1();
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
		}.start();
		new Thread(){
			public void run() {
				while(true){
					try {
						task.task2();
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
		}.start();
		new Thread(){
			public void run() {
				while(true){
					try {
						task.task3();
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
		}.start();
	}
}

class MyTask{
	
	//標識 1:可以執行任務1,2:可以執行任務2, 3:可以執行任務3 
	int flag = 1;
	
	public synchronized void task1() throws InterruptedException{
		if(flag != 1){
			this.wait();//當前線程等待
			//this.wait(timeout);
		}
		
		System.out.println("1.銀行信用卡自動還款任務...");
		flag = 2;
		//this.notify();//喚醒隨機線程
		this.notifyAll();//喚醒所有等待線程
		
	}
	
	public synchronized void task2() throws InterruptedException{
		
		if(flag != 2){
			this.wait();//線程等待
		}
		
		System.out.println("2.銀行儲蓄卡自動結算利息任務...");
		flag = 3;
		//this.notify();//喚醒其它線程
		this.notifyAll();
	}
	
	public synchronized void task3() throws InterruptedException{
			if(flag != 3){
				this.wait();//線程等待
			}
			
			System.out.println("3.銀行短信提醒任務...");
			flag = 1;
			//this.notify();//喚醒其它線程
			this.notifyAll();
	}
}

class MyTask{
	
	//標識 1:可以執行任務1,2:可以執行任務2, 3:可以執行任務3 
	int flag = 1;
	
	public synchronized void task1() throws InterruptedException{
		if(flag != 1){
			this.wait();//當前線程等待
			//this.wait(timeout);
		}
		
		System.out.println("1.銀行信用卡自動還款任務...");
		flag = 2;
		//this.notify();//喚醒隨機線程
		this.notifyAll();//喚醒所有等待線程
		
	}
	
	public synchronized void task2() throws InterruptedException{
		
		if(flag != 2){
			this.wait();//線程等待
		}
		
		System.out.println("2.銀行儲蓄卡自動結算利息任務...");
		flag = 3;
		//this.notify();//喚醒其它線程
		this.notifyAll();
	}
	
	public synchronized void task3() throws InterruptedException{
			if(flag != 3){
				this.wait();//線程等待
			}
			
			System.out.println("3.銀行短信提醒任務...");
			flag = 1;
			//this.notify();//喚醒其它線程
			this.notifyAll();
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章