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

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

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

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函數須要一個鎖;this

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

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方法中建立的實例對象設計

因此問題又來了,我須要在主線程中啓動分線程,因此不能給兩個線程分配同一個實例對象,若是每次都從新分配一個實例對象,再建立一個分線程,是否是太愚蠢了;code

對於靜態方法和非靜態方法的同步問題,靜態方法鎖住的是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 ,誰在打擾我睡覺呢!
線程執行
睡一下子
相關文章
相關標籤/搜索