zoukankan      html  css  js  c++  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类来实现:

    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 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()方法即可,因此不再举例...

    很显然 单线程模式 适应的场景为:一个始终运行(死循环)的单个线程,比如一个永不停止的单个后台线程,在后台实现一些辅助功能,或者实现垃圾回收之类的功能。有不允许多个线程执行的要求。比如本文中的刷新微信的access_token,就没有必要用多个线程不断的去刷新了,而且这样会造成混乱,不知道那个线程获得的access_token才是正确的(因为后一个线程获得的access_token会覆盖前一个的)。

  • 相关阅读:
    二分+RMQ/双端队列/尺取法 HDOJ 5289 Assignment
    思维题 HDOJ 5288 OO’s Sequence
    树形DP Codeforces Round #135 (Div. 2) D. Choosing Capital for Treeland
    最大流增广路(KM算法) HDOJ 1853 Cyclic Tour
    最大流增广路(KM算法) HDOJ 1533 Going Home
    最大流增广路(KM算法) HDOJ 2255 奔小康赚大钱
    Complete the Word CodeForces
    Gadgets for dollars and pounds CodeForces
    Vasya and Basketball CodeForces
    Carries SCU
  • 原文地址:https://www.cnblogs.com/digdeep/p/4374293.html
Copyright © 2011-2022 走看看