使用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会覆盖前一个的)。
使用Java实现单线程模式的更多相关文章
- JAVA NIO non-blocking模式实现高并发服务器(转)
原文链接:JAVA NIO non-blocking模式实现高并发服务器 Java自1.4以后,加入了新IO特性,NIO. 号称new IO. NIO带来了non-blocking特性. 这篇文章主要 ...
- 基于Java 生产者消费者模式(详细分析)
Java 生产者消费者模式详细分析 本文目录:1.等待.唤醒机制的原理2.Lock和Condition3.单生产者单消费者模式4.使用Lock和Condition实现单生产单消费模式5.多生产多消费模 ...
- JAVA NIO non-blocking模式实现高并发服务器
JAVA NIO non-blocking模式实现高并发服务器 分类: JAVA NIO2014-04-14 11:12 1912人阅读 评论(0) 收藏 举报 目录(?)[+] Java自1.4以后 ...
- Java设计模式——组合模式
JAVA 设计模式 组合模式 用途 组合模式 (Component) 将对象组合成树形结构以表示“部分-整体”的层次结构.组合模式使得用户对单个对象和组合对象的使用具有唯一性. 组合模式是一种结构型模 ...
- java装饰者模式理解
java 装饰者模式其实就是扩展子类的功能,和继承是一个性质. 但继承是在编译时就固定扩展了父类的一些功能,而装饰者模式是在运行过程中动态绑定对象,实现一个子类可以随时扩展功能. 将方法排列组合,也可 ...
- !!转!!java 简单工厂模式
举两个例子以快速明白Java中的简单工厂模式: 女娲抟土造人话说:“天地开辟,未有人民,女娲抟土为人.”女娲需要用土造出一个个的人,但在女娲造出人之前,人的概念只存在于女娲的思想里面.女娲造人,这就是 ...
- java 双重检查模式
java 双重检查模式 在并发环境下 兼顾安全和效率 成例(Idiom)是一种代码层次上的模式,是在比设计模式的层次更具体的层次上的代码技巧.成例往往与编程语言密切相关.双重检查成例(Double C ...
- Java 抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是工厂方法模式的进一步抽象,其英文原话"Provide an interface for creating families ...
- Java之模板方法模式(Template Method)
Java之模板方法模式(Template Method) 1. 概念:定义一个算法的骨架,而将一些实现步骤延迟到子类中. 把不变的行为搬到超类,去除子类中重复的代码来体现他的优势. 2. UML图: ...
随机推荐
- HTML5之应用缓存---manifest---缓存使用----Web前端manifest缓存
相信来查这一类问题的都是遇到问题或者是初学者吧! 没关系相信你认真看过之后就会知道明白的 这是HTML5新加的特性 HTML5 引入了应用程序缓存,这意味着 web 应用可进行缓存,并可在没有因特网连 ...
- Redis之AOF备份
redis在进行备份的时候有2种方式:1.RDB:2.AOF:现在主要讲哈AOF的备份 1.找到redis.config配置文件,大部分下载下来和redis-service同目录: 2.打开redie ...
- SQL Server中的锁
死锁的类型 不同表之间的相互等待,第一个事务操作A->B, 第二个事务操作B->A - 每个事务都锁定对方下一步将要操作的表 同一张表之间的相互等待, 无索引导致的全表扫描,下文中说到的情 ...
- 1.Kali安装到移动硬盘或者U盘中~Linux系通用方法(包括Android)
0.1.保证这个服务必须启动(虚拟机服务最好都启动) 0.2.看看U盘接口类型是否对应 1.安装第一步 2.安装第二步,选择kali镜像 3.设置存放位置(上面的名字无所谓,最后不会用它的,虚拟机只是 ...
- SQL Server 进制转换函数
一.背景 前段时间群里的朋友问了一个问题:“在查询时增加一个递增序列,如:0x00000001,即每一个都是36进位(0—9,A--Z),0x0000000Z后面将是0x00000010,生成一个像下 ...
- Python开发环境配置
好久没有写博客了,自从6月份毕业后,进入一家做书法.字画文化宣传的互联网公司(www.manyiaby.com),这段时间一直在进行前端开发,对于后端的使用很少了,整天都是什么html.css.jav ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(20)-权限管理系统-根据权限获取菜单
系列目录 不知不觉到20讲,真是漫长的日子,可惜最近工作挺忙,要不可以有更多的时间来更新,多谢大家的一路支持.如果你觉得好,记得帮我点击推荐^-^ 我们在之前已经插入一些真实数据,其中包含了一个用户和 ...
- 网站文件系统发展&&分布式文件系统fastDFS
网站文件系统发展 1.单机时代的图片服务器架构 初创时期由于时间紧迫,开发人员水平也很有限等原因.所以通常就直接在website文件所在的目录下,建立1个upload子目录,用于保存用户上传的图片文件 ...
- WebComponent魔法堂:深究Custom Element 之 标准构建
前言 通过<WebComponent魔法堂:深究Custom Element 之 面向痛点编程>,我们明白到其实Custom Element并不是什么新东西,我们甚至可以在IE5.5上定 ...
- iOS 触摸事件与UIResponder(内容根据iOS编程编写)
触摸事件 因为 UIView 是 UIResponder 的子类,所以覆盖以下四个方法就可以处理四种不同的触摸事件: 1. 一根手指或多根手指触摸屏幕 - (void)touchesBegan:(N ...