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会覆盖前一个的)。

  • 相关阅读:
    WrapPanel虚拟化
    关于Windows执行cmd命令的坑之一
    C# .Net 获取当前电脑上的可移动磁盘
    关于WPF中资源字典的内存优化
    WPF获取ListBox、ListView之类列表的ScrollViewer实例
    C#事件与委托 yangan
    系统自动登录设置(适用所有操作系统) yangan
    Oracel小知识记录 yangan
    使用avaScript获取Url中的指定参数值 yangan
    Win7下破解ArcGIS 9.3方法 yangan
  • 原文地址:https://www.cnblogs.com/digdeep/p/4374293.html
Copyright © 2011-2022 走看看