我们都知道单例模式,有很多种实现方法。今天我们实现一个单线程实例模式,也就是说只能实例化该类的一个线程来运行,不允许有该类的多个线程实例存在。直接上代码:

  1. public class SingletonThread implements Runnable
  2. {
  3. /** 获取access_token 和 expire_in 的url */
  4. private static final String accessTokenUrl =
                      "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
  5. + ParameterConfig.WX_APPID + "&secret=" + ParameterConfig.WX_APPSECRET;
  6.  
  7. /** 这里使用public volatile发布一个共享对象 */
  8. private static volatile AccessToken accessToken; // 因为是一个线程写多个线程读,而引用的又是“不可变对象”,
                                  // 所以使用volatile保证“可见性”
  9.  
  10. // 保证无法实例化 SingletonThread
  11. private SingletonThread(){}
  12.  
  13. // 静态类保证thread的初始化是线程安全的,内部类实现了延迟加载的效果
  14. private static class SingletonThreadHolder
  15. {
  16. public static SingletonThread thread = new SingletonThread();
  17. }
  18.  
  19. public static SingletonThread getInstance()
  20. {
  21. return SingletonThreadHolder.thread;
  22. }
  23.  
  24. @Override
  25. public void run()
  26. {
  27. while(true)
  28. {
  29. try{
  30. HttpsURLConnection conn = HttpUtil.initHttpsConnection(accessTokenUrl, "GET");
  31. String result = HttpUtil.getHttpsContent(conn, "utf-8");
  32.  
  33. JSONObject json = null;
  34. if(result != null)
  35. json = JSON.parseObject(result);
  36.  
  37. if(json != null){
  38. accessToken = new AccessToken(json.getString("access_token"), json.getLong("expires_in"));
  39. }else{
  40. System.out.println("get access_token failed----");
  41. }
  42. }catch(IOException e){
  43. e.printStackTrace();
  44. }
  45.  
  46. try{
  47. if(null != accessToken){
  48. Thread.sleep((accessToken.getExpire_in() - 200) * 1000); // 休眠7000秒
  49. }else{
  50. Thread.sleep(60 * 1000); // 如果access_token为null,60秒后再获取
  51. }
  52. }catch(InterruptedException e){
  53. try{
  54. Thread.sleep(60 * 1000);
  55. }catch(InterruptedException e1){
  56. e1.printStackTrace();
  57. }
  58. }
  59. }
  60. }
        public static AccessToken getAccessToken() {
            return accessToken;
        }   
  61. }

也可以扩展Thread类来实现:

  1. public class SingletonThread2 extends Thread
  2. {
  3. /** 获取access_token 和 expire_in 的url */
  4. private static final String accessTokenUrl =
                      "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
  5. + ParameterConfig.WX_APPID + "&secret=" + ParameterConfig.WX_APPSECRET;
  6.  
  7. // 这里使用public发布一个共享对象
  8. private static volatile AccessToken accessToken; // 因为是一个线程写多个线程读,而引用的又是“不可变对象”,
                                  // 所以使用volatile保证“可见性”
  9.  
  10. // 保证无法实例化 SingletonThread
  11. private SingletonThread2(){}
  12.  
  13. // 静态类保证thread的初始化是线程安全的,内部类实现了延迟加载的效果
  14. private static class SingletonThreadHolder
  15. {
  16. public static SingletonThread2 thread = new SingletonThread2();
  17. }
  18.  
  19. public static SingletonThread2 getInstance()
  20. {
  21. return SingletonThreadHolder.thread;
  22. }
  23.  
  24. @Override
  25. public void run()
  26. {
  27. while(true)
  28. {
  29. try{
  30. HttpsURLConnection conn = HttpUtil.initHttpsConnection(accessTokenUrl, "GET");
  31. String result = HttpUtil.getHttpsContent(conn, "utf-8");
  32.  
  33. JSONObject json = null;
  34. if(result != null)
  35. json = JSON.parseObject(result);
  36.  
  37. if(json != null){
  38. accessToken = new AccessToken(json.getString("access_token"), json.getLong("expires_in"));
  39. }else{
  40. System.out.println("get access_token failed----");
  41. }
  42. }catch(IOException e){
  43. e.printStackTrace();
  44. }
  45.  
  46. try{
  47. if(null != accessToken){
  48. Thread.sleep((accessToken.getExpire_in() - 200) * 1000); // 休眠7000秒
  49. }else{
  50. Thread.sleep(60 * 1000); // 如果access_token为null,60秒后再获取
  51. }
  52. }catch(InterruptedException e){
  53. try{
  54. Thread.sleep(60 * 1000);
  55. }catch(InterruptedException e1){
  56. e1.printStackTrace();
  57. }
  58. }
  59. }
  60. }
        public static AccessToken getAccessToken() {
            return accessToken;
        }   
  61. }

这里的场景是:微信开发中需要每隔2个小时从腾讯的微信服务器刷新access_token,所以这里只需要使用单个线程无线循环每隔2小时刷新一次即可,我们不希望出现该类的多个线程,每个线程都去刷新access_token。

注意如果在一个线程上调用多次 start() 方法是会抛出 IllegalThreadStateException 异常的。

这里的实现其实也来自于单实例模式的一种写法,实现了线程安全和延迟加载的效果。其实对应于单例模式,单线程模式也有多种实现方法,比如使用 静态属性:

  1. public class SingletonThread3 extends Thread
  2. {
  3. private static SingletonThread3 thread = new SingletonThread3(); // static保证线程安全
  4.  
  5. // 保证无法实例化 SingletonThread
  6. private SingletonThread3(){}
  7.  
  8. public static SingletonThread3 getInstance()
  9. {
  10. return thread;
  11. }
  12.  
  13. @Override
  14. public void run()
  15. {
  16. // ...
  17. }
  18. }

这种实现也是线程安全的,但是没有延迟加载的效果。

AccessToken是一个“不可变对象”的类:

  1. /**
  2. * access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。
  3. * 开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。
  4. * access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
  5. * 目前access_token的有效期通过返回的expire_in来传达,目前是7200秒之内的值
  6. * @author digdeep@126.com
  7. * 这是一个“不可变”对象的类定义
  8. */
  9. public class AccessToken
  10. {
  11. private final String access_token;
  12. private final long expire_in; // access_token有效时间,单位为妙
  13.  
  14. public AccessToken(String access_token, long expire_in)
  15. {
  16. this.access_token = access_token;
  17. this.expire_in = expire_in;
  18. }
  19.  
  20. public String getAccess_token() {
  21. return access_token;
  22. }
  23.  
  24. public long getExpire_in() {
  25. return expire_in;
  26. }
  27.  
  28. }

其实几乎可以将每一种单实例模式都可以改造成一种单线程模式,改造方法就是让其 implements Runnable 或者 extends Thread 重写run()方法即可,因此不再举例...

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

使用Java实现单线程模式的更多相关文章

  1. JAVA NIO non-blocking模式实现高并发服务器(转)

    原文链接:JAVA NIO non-blocking模式实现高并发服务器 Java自1.4以后,加入了新IO特性,NIO. 号称new IO. NIO带来了non-blocking特性. 这篇文章主要 ...

  2. 基于Java 生产者消费者模式(详细分析)

    Java 生产者消费者模式详细分析 本文目录:1.等待.唤醒机制的原理2.Lock和Condition3.单生产者单消费者模式4.使用Lock和Condition实现单生产单消费模式5.多生产多消费模 ...

  3. JAVA NIO non-blocking模式实现高并发服务器

    JAVA NIO non-blocking模式实现高并发服务器 分类: JAVA NIO2014-04-14 11:12 1912人阅读 评论(0) 收藏 举报 目录(?)[+] Java自1.4以后 ...

  4. Java设计模式——组合模式

    JAVA 设计模式 组合模式 用途 组合模式 (Component) 将对象组合成树形结构以表示“部分-整体”的层次结构.组合模式使得用户对单个对象和组合对象的使用具有唯一性. 组合模式是一种结构型模 ...

  5. java装饰者模式理解

    java 装饰者模式其实就是扩展子类的功能,和继承是一个性质. 但继承是在编译时就固定扩展了父类的一些功能,而装饰者模式是在运行过程中动态绑定对象,实现一个子类可以随时扩展功能. 将方法排列组合,也可 ...

  6. !!转!!java 简单工厂模式

    举两个例子以快速明白Java中的简单工厂模式: 女娲抟土造人话说:“天地开辟,未有人民,女娲抟土为人.”女娲需要用土造出一个个的人,但在女娲造出人之前,人的概念只存在于女娲的思想里面.女娲造人,这就是 ...

  7. java 双重检查模式

    java 双重检查模式 在并发环境下 兼顾安全和效率 成例(Idiom)是一种代码层次上的模式,是在比设计模式的层次更具体的层次上的代码技巧.成例往往与编程语言密切相关.双重检查成例(Double C ...

  8. Java 抽象工厂模式

    抽象工厂模式(Abstract Factory Pattern)是工厂方法模式的进一步抽象,其英文原话"Provide an interface for creating families ...

  9. Java之模板方法模式(Template Method)

    Java之模板方法模式(Template Method) 1. 概念:定义一个算法的骨架,而将一些实现步骤延迟到子类中. 把不变的行为搬到超类,去除子类中重复的代码来体现他的优势. 2. UML图: ...

随机推荐

  1. The type java.lang.CharSequence cannot be resolved. It is indirectly referenced from required .class files.

    参照 http://stackoverflow.com/questions/24301986/the-type-java-lang-charsequence-cannot-be-resolved-in ...

  2. (实例篇)PHP实现HTTP断点续传的方法

    PHP实现HTTP断点续传的方法. <?php /** * PHP-HTTP断点续传实现 * @param string $path: 文件所在路径 * @param string $file: ...

  3. 异步与并行~List<T>是线程安全的吗?

    返回目录 题目有点意思,大家都知道Dictionary<K,V>不是线程安全的类型,而List<T>是线程安全的吗?在今天之前大叔没有去测试过,而就在今天也是一个VIP问我,说 ...

  4. iOS中数据库应用基础

    iOS 数据库入门 一.数据库简介 1.什么是数据库? 数据库(Database) 是按照数据结构来组织,存储和管理数据的仓库 数据库可以分为2大种类 关系型数据库(主流) PC端 Oracle My ...

  5. 《JS设计模式笔记》 5,适配器模式

    //适配器模式的作用就像一个转接口. jQuery("#"+id); $id=function (id) { return jQuery("#"+id)[0]; ...

  6. 创建 Monitor 并测试 - 每天5分钟玩转 OpenStack(124)

    前面我们创建了 Pool,VIP 并添加了 Member.今天将创建 Monitor,然后测试 LBaaS 是否能够正常工作. 创建 Monitor LBaaS 可以创建 monitor,用于监控 P ...

  7. JMS学习之路(一):整合activeMQ到SpringMVC

    JMS的全称是Java Message Service,即Java消息服务.它主要用于在生产者和消费者之间进行消息传递,生产者负责产生消息,而消费者负责接收消息.把它应用到实际的业务需求中的话我们可以 ...

  8. 读书笔记--SQL必知必会09--汇总数据

    9.1 聚集函数 聚集函数(aggregate function),对某些行运行的函数,计算并返回一个值. 使用聚集函数可以汇总数据而不必将涉及的数据实际检索出来. 可利用标准的算术操作符,实现更高级 ...

  9. [入门级] visual studio 2010 mvc4开发,用ibatis作为数据库访问媒介(一)

    [入门级] visual studio 2010 mvc4开发,用ibatis作为数据库访问媒介(一) Date  周二 06 一月 2015 By 钟谢伟 Tags mvc4 / asp.net 示 ...

  10. .net两个对象比较,抛出不一样字段的结果

    现在应该经常用到记录操作日志,修改和新增必定涉及到两个实体的属性值的变动. 利用反射,将变动记录下来. 切记,类中的属性字段上面需要打上Description标签: 例如: /// <summa ...