博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用Java实现单线程模式
阅读量:4582 次
发布时间:2019-06-09

本文共 6126 字,大约阅读时间需要 20 分钟。

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

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

 

转载于:https://www.cnblogs.com/digdeep/p/4374293.html

你可能感兴趣的文章
android的用户定位(一)
查看>>
设计模式-结构型模式,外观模式(6)
查看>>
[Java] 遍历HashMap和HashMap转换成List的两种方式
查看>>
mongodb
查看>>
LeetCode 46. Permutations
查看>>
jmeter- 性能测试3:聚合报告(Aggregate Report )
查看>>
JavaScript高级程序设计---学习笔记(二)
查看>>
vim 插件的学习
查看>>
Uncaught SyntaxError: Unexpected token ILLEGAL
查看>>
一个预处理定义的问题
查看>>
ANDROID L——Material Design综合应用(Demo)
查看>>
自我介绍以及关于软件工程的问题
查看>>
struts (一)
查看>>
【新番推荐】工作细胞
查看>>
NYOJ 16 矩形嵌套
查看>>
Leetcode中的SQL题目练习(二)
查看>>
dubbo 集群容错源码
查看>>
Collection接口的子接口——Queue接口
查看>>
LINUX安装NGINX
查看>>
服务器启动项目抛错 没有到主机的路由
查看>>