歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

使用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類來實現:

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。

注意如果在一個線程上調用多次 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 [email protected]
 * 這是一個“不可變”對象的類定義
 */
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()方法即可,因此不再舉例...

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

Copyright © Linux教程網 All Rights Reserved