使用Java實現單線程模式

咱們都知道單例模式,有不少種實現方法。今天咱們實現一個單線程實例模式,也就是說只能實例化該類的一個線程來運行,不容許有該類的多個線程實例存在。直接上代碼:java

public class SingletonThread implements Runnable
{
    /** 獲取access_token 和 expire_in 的url */
    private static final String accessTokenUrl = 
                  "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + ParameterConfig.WX_APPID + "&secret=" + ParameterConfig.WX_APPSECRET; /** 這裏使用public volatile發佈一個共享對象 */ private static volatile AccessToken accessToken; // 由於是一個線程寫多個線程讀,而引用的又是「不可變對象」,
                              // 因此使用volatile保證「可見性」
// 保證沒法實例化 SingletonThread private SingletonThread(){} // 靜態類保證thread的初始化是線程安全的,內部類實現了延遲加載的效果 private static class SingletonThreadHolder { public static SingletonThread thread = new SingletonThread(); } public static SingletonThread getInstance() { return SingletonThreadHolder.thread; } @Override public void run() { while(true) { try{ HttpsURLConnection conn = HttpUtil.initHttpsConnection(accessTokenUrl, "GET"); String result = HttpUtil.getHttpsContent(conn, "utf-8"); JSONObject json = null; if(result != null) json = JSON.parseObject(result); if(json != null){ accessToken = new AccessToken(json.getString("access_token"), json.getLong("expires_in")); }else{ System.out.println("get access_token failed----"); } }catch(IOException e){ e.printStackTrace(); } try{ if(null != accessToken){ Thread.sleep((accessToken.getExpire_in() - 200) * 1000); // 休眠7000秒 }else{ Thread.sleep(60 * 1000); // 若是access_token爲null,60秒後再獲取 } }catch(InterruptedException e){ try{ Thread.sleep(60 * 1000); }catch(InterruptedException e1){ e1.printStackTrace(); } } } }
    public static AccessToken getAccessToken() {
        return accessToken;
    }    }

也能夠擴展Thread類來實現:json

public class SingletonThread2 extends Thread
{
    /** 獲取access_token 和 expire_in 的url */
    private static final String accessTokenUrl = 
                  "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + ParameterConfig.WX_APPID + "&secret=" + ParameterConfig.WX_APPSECRET; // 這裏使用public發佈一個共享對象 private static volatile AccessToken accessToken; // 由於是一個線程寫多個線程讀,而引用的又是「不可變對象」,
                              // 因此使用volatile保證「可見性」
// 保證沒法實例化 SingletonThread private SingletonThread2(){} // 靜態類保證thread的初始化是線程安全的,內部類實現了延遲加載的效果 private static class SingletonThreadHolder { public static SingletonThread2 thread = new SingletonThread2(); } public static SingletonThread2 getInstance() { return SingletonThreadHolder.thread; } @Override public void run() { while(true) { try{ HttpsURLConnection conn = HttpUtil.initHttpsConnection(accessTokenUrl, "GET"); String result = HttpUtil.getHttpsContent(conn, "utf-8"); JSONObject json = null; if(result != null) json = JSON.parseObject(result); if(json != null){ accessToken = new AccessToken(json.getString("access_token"), json.getLong("expires_in")); }else{ System.out.println("get access_token failed----"); } }catch(IOException e){ e.printStackTrace(); } try{ if(null != accessToken){ Thread.sleep((accessToken.getExpire_in() - 200) * 1000); // 休眠7000秒 }else{ Thread.sleep(60 * 1000); // 若是access_token爲null,60秒後再獲取 } }catch(InterruptedException e){ try{ Thread.sleep(60 * 1000); }catch(InterruptedException e1){ e1.printStackTrace(); } } } }
    public static AccessToken getAccessToken() {
        return accessToken;
    }    }

這裏的場景是:微信開發中須要每隔2個小時從騰訊的微信服務器刷新access_token,因此這裏只須要使用單個線程無線循環每隔2小時刷新一次便可,咱們不但願出現該類的多個線程,每一個線程都去刷新access_token。api

注意若是在一個線程上調用屢次 start() 方法是會拋出 IllegalThreadStateException 異常的。安全

這裏的實現其實也來自於單實例模式的一種寫法,實現了線程安全和延遲加載的效果。其實對應於單例模式,單線程模式也有多種實現方法,好比使用 靜態屬性:服務器

public class SingletonThread3 extends Thread
{
	private static SingletonThread3 thread = new SingletonThread3(); // static保證線程安全
	
	// 保證沒法實例化 SingletonThread
	private SingletonThread3(){}
	
	public static SingletonThread3 getInstance()
	{
		return thread;
	}
	
	@Override
	public void run() 
	{
		// ...
	}
}

這種實現也是線程安全的,可是沒有延遲加載的效果。微信

AccessToken是一個「不可變對象」的類:微信開發

/**
 * access_token是公衆號的全局惟一票據,公衆號調用各接口時都需使用access_token。
 * 開發者須要進行妥善保存。access_token的存儲至少要保留512個字符空間。
 * access_token的有效期目前爲2個小時,需定時刷新,重複獲取將致使上次獲取的access_token失效。
 * 目前access_token的有效期經過返回的expire_in來傳達,目前是7200秒以內的值
 * @author digdeep@126.com
 * 這是一個「不可變」對象的類定義
 */
public class AccessToken 
{
    private final String access_token;
    private final long expire_in;        // access_token有效時間,單位爲妙
    
    public AccessToken(String access_token, long expire_in)
    {
        this.access_token = access_token;
        this.expire_in = expire_in;
    }
    
    public String getAccess_token() {
        return access_token;
    }

    public long getExpire_in() {
        return expire_in;
    }
    
}

其實幾乎能夠將每一種單實例模式均可以改形成一種單線程模式,改造方法就是讓其 implements Runnable 或者 extends Thread 重寫run()方法便可,所以再也不舉例...app

很顯然 單線程模式 適應的場景爲:一個始終運行(死循環)的單個線程,好比一個永不中止的單個後臺線程,在後臺實現一些輔助功能,或者實現垃圾回收之類的功能。有不容許多個線程執行的要求。好比本文中的刷新微信的access_token,就沒有必要用多個線程不斷的去刷新了,並且這樣會形成混亂,不知道那個線程得到的access_token纔是正確的(由於後一個線程得到的access_token會覆蓋前一個的)。ide

相關文章
相關標籤/搜索