多線程控制線程的等待和喚醒


最近做注冊的時候,發現同步發送注冊郵件多了一個耗時,就想到異步處理郵件發送,直接返回成功給用戶。

設計了一個線程,用來發送郵件,需要發送的時候再來喚醒就好了,但是對於沒有系統了解過多線程的我來說,想的太簡單了。

public class MailSendThread  extends Thread{

    private static Logger    log    = Logger.getLogger(MailSendThread.class);
    public final static long mail_user_time = 48 * 1800000L;//一天運行一次
    public void run(){
        log.error("MailSendThread is running!");
        try {
            MailUtil.sendMailInfo();
            sleep(mail_user_time);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            log.error("MailSendThread run error", e);
        }
    }
}
private static MailSendThread mailSender = new MailSendThread();
public static void notifyMailSender(){
        mailSender.notify();
    }

多傻的代碼!!!!

仔細研究后發現,首先sleep只能用作線程內部等待使用,指定時間段內休眠,不能外部喚醒;

其次,nofity方法必須依托與一個線程正在等待的對象,就是鎖住的對象,不能直接對線程操作,因為wait函數需要一個鎖;

研究了一下生產者和消費者,這里用到的鎖是對象實例

package com.thread;
/**
 * 生產者,制造饅頭
 * @author huangjc
 *
 */
public class A extends Thread{
    private C c;
    public A(C c){
        this.c = c;
    }
    public void run(){
        int i=0;
        while(i < 10){
            try {
                c.add();
                i++;
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}
package com.thread;

/**
 * 消費者,消耗饅頭
 * @author huangjc
 *
 */
public class B extends Thread{

    private C c;
    public B(C c){
        this.c = c;
    }
    public void run(){
        int i=0;
        while(i<10){
            i++;
            c.minus();
        }
    }
    public static void main(String[] args) {
        C c = new C();
        new Thread(new B(c)).start();
        new Thread(new A(c)).start();
    }
}

具體的鎖就在下面

package com.thread;

public class C {

    private static int i=0;
    public synchronized  void add() throws InterruptedException{
        System.out.println("增加饅頭:現在有"+(i)+"個饅頭");
        if(i == 5){
            System.out.println("饅頭夠多了,趕快吃吧!");
            wait();
        }else{
            i++;
            System.out.println("放了一個饅頭");
            notify();//喚醒消耗線程
        }
    }
    public synchronized  void minus(){
        System.out.println("取出饅頭:現在有"+(i)+"個饅頭");
        if (i == 0){
            System.out.println("饅頭沒有了,等會兒吧!");
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }else{
            i--;
            System.out.println("取出一個饅頭");
            notify();
        }
    }
}

這里的notify和wait方法鎖住的都是在B的main方法中創建的實例對象

所以問題又來了,我需要在主線程中啟動分線程,所以不能給兩個線程分配同一個實例對象,如果每次都重新分配一個實例對象,再創建一個分線程,是不是太愚蠢了;

對於靜態方法和非靜態方法的同步問題,靜態方法鎖住的是Class,例如A.class,而非靜態方法鎖住的是當前實例。

所以靜態方法和非靜態方法並不適用同一個鎖。一個類中的所有靜態方法使用同一個鎖。

所以,是否可以考慮使用靜態方法來發送郵件,鎖住類本身,然后再主線程中依托類本身進行喚醒;

太坑爹了,完全不是這回事,查了API后發現,wait方法是Object對象的,那不就是說必須依托於一個對象實例嗎?

 

再來看api中關於thread類下面的方法,其中有個

interrupt()
interrupt

public void interrupt()
中斷線程。
如果當前線程沒有中斷它自己(這在任何情況下都是允許的),則該線程的 checkAccess 方法就會被調用,這可能拋出 SecurityException。

如果線程在調用 Object 類的 wait()、wait(long) 或 wait(long, int) 方法,或者該類的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法過程中受阻,則其中斷狀態將被清除,它還將收到一個 InterruptedException。

如果該線程在可中斷的通道上的 I/O 操作中受阻,則該通道將被關閉,該線程的中斷狀態將被設置並且該線程將收到一個 ClosedByInterruptException。

如果該線程在一個 Selector 中受阻,則該線程的中斷狀態將被設置,它將立即從選擇操作返回,並可能帶有一個非零值,就好像調用了選擇器的 wakeup 方法一樣。

如果以前的條件都沒有保存,則該線程的中斷狀態將被設置。

中斷一個不處於活動狀態的線程不需要任何作用。

拋出:
SecurityException - 如果當前線程無法修改該線程

線程A正在使用sleep()暫停着: Thread.sleep(100000);
如果要取消他的等待狀態,可以在正在執行的線程里(比如這里是B)調用
a.interrupt();
令線程A放棄睡眠操作,這里a是線程A對應到的Thread實例
執行interrupt()時,並不需要獲取Thread實例的鎖定.任何線程在任何時刻,都可以調用其他線程interrupt().當sleep中的線程被調用interrupt()時,就會放棄暫停的狀態.並拋出InterruptedException.丟出異常的,是A線程

package com.thread;

/**
 * 消費者,消耗饅頭
 * @author huangjc
 *
 */
public class B extends Thread{

    public void run(){
        while(true){
            System.out.println("線程執行");
            try {
                System.out.println("睡一會兒");
                sleep(180000);
            } catch (InterruptedException e) {
                System.out.println("cao ,誰在打擾我睡覺呢!");
            }
        }
    }
    public static void main(String[] args) {
        B b = new B()
;        b.start();
        int i=0;
        while(i < 100000000){
            i++;
        }
        System.out.println("別睡了");
        b.interrupt();
    }
}

執行main函數

線程執行
睡一會兒
別睡了
cao ,誰在打擾我睡覺呢!
線程執行
睡一會兒

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM