最近作註冊的時候,發現同步發送註冊郵件多了一個耗時,就想到異步處理郵件發送,直接返回成功給用戶。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 ,誰在打擾我睡覺呢!
線程執行
睡一下子