工作记录
来源:互联网 发布:dnf数据芯片刷哪里好 编辑:程序博客网 时间:2024/06/08 10:08
一、避免两个double类型数值直接相加
工作中遇到一个问题java中double+double 经常得出一些意想不到的的结果:
package test;public class Test {public static void main(String[] args) {double a = 1.1;double b = 2.45;double c = a+b;System.out.println(c);}}结果为:3.5500000000000003
查阅了一些信息发现原因其实很简单:
我们回忆一下1.1在计算机中是如何以二进制保存的,我们知道数字在计算机中是以二进制保存,比如IEEE754标准
double是属于长实数的类型,那么带有小数的十进制数是怎么转换成二进制数呢?我们回忆一下:
比如0.8125转换为二进制小数乘以2,取整,小数部分继续乘以2,取整,得到小数部分0为止,将整数顺序排列。0.8125x2=1.625 取整1,小数部分是0.6250.625x2=1.25 取整1,小数部分是0.250.25x2=0.5 取整0,小数部分是0.50.5x2=1.0 取整1,小数部分是0,结束
按照这种方法我们会发现0.1转换为二进制是一个无限循环
我们来看一下0.1的二进制代码
public class Test {public static void main(String[] args) {double a = 0.1;long l = Double.doubleToLongBits(a);System.out.println(Long.toBinaryString(l));}}结果为按照IEEE754的标准显示如下:
11111110111001100110011001100110011001100110011001100110011010
加上一位隐藏的符号位0在最开始
刚好是64位
因为double的长度是64位的 所以后面的地位精度就丢失了
我们再来看看0.25.用上面计算二进制的方法可以得出一个确切的数
package test;public class Test {public static void main(String[] args) {double a = 0.25;long l = Double.doubleToLongBits(a);System.out.println(Long.toBinaryString(l));}}结果如下:
11111111010000000000000000000000000000000000000000000000000000
我们发现没有精度丢失。
所以我们发现了问题所在。有些小数并不能用IEEE754完整的表示,所以会照成上述的问题。
那么联系到实际中,在商业的计算中 一般不能用double+double
需要用bigdecimal类来解决
BigDecimal volumn = new BigDecimal("1.1");volumn = volumn.add(new BigDecimal("2.45"));
得到结果:3.55
二、iText生成pdf byte流
项目需要在socket之间传递一个pdf文件
决定使用iText进行开发
iText写一个pdf一般分以下几步:
// 1.创建 Document 对象Document _document = new Document(); // 2.创建书写器 PdfWriter _pdfWriter = PdfWriter.getInstance(_document, OutputStream); // 3.打开文档 _document.open(); // 4.向文档中添加内容 _document.add(new Paragraph("Hi")); // 5.关闭文档_document.close()
看到网上的例子都是直接生成一个pdf到硬盘中:
try { PdfWriter.getInstance(doc, new FileOutputStream("c:/hello.pdf")); doc.open(); doc.add(new Paragraph("HelloWorld")); doc.close(); } catch (DocumentException e) { e.printStackTrace();}
项目只需要把生成的pdf对象保存在内存流中然后直接socket发送给服务器
其实在第二部输出流输出到内存中就可以了,
PdfWriter _pdfWriter = PdfWriter.getInstance(_document, OutputStream);
第二个参数是一个输出流,用ByteArrayOutputStream就可以做到我们想要的
try { ByteArrayOutputStream ba = new ByteArrayOutputStream(); PdfWriter.getInstance(doc, ba); doc.open(); doc.add(new Paragraph("HelloWorld")); doc.close(); //得到byte数组 Byte[] = ba.toByteArray(); } catch (DocumentException e) { e.printStackTrace();}
三、iText生成pdf byte流
单点登录(Single Sign On , 简称 SSO )是目前比较流行的服务于企业业务整合的解决方案之一, SSO 使得在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。CAS(Central Authentication Service)是一款不错的针对 Web 应用的单点登录框架,本文介绍了 CAS 的原理、协议、在 Tomcat 中的配置和使用,对于采用 CAS 实现轻量级单点登录解决方案的入门读者具有一定指导作用。
参考链接:
http://www.ibm.com/developerworks/cn/opensource/os-cn-cas/
http://www.blogjava.net/security/archive/2006/10/02/sso_in_action.html
http://www.coin163.com/java/cas/cas.html
读以下内容前 请先大概浏览一下参考链接的内容
简单实例,利用CAS搭建简单SSO系统:
1.部署SSO认证SERVER端,认证所有的请求,如果用户没有登录,会重定向到一个登录界面。
下载 cas-server-4.0.0.rar (可以在本人资源中找到) ,解压拿到cas-server-uber-webapp-4.0.0.war部署在服务器tomcat或其他server容器上
2.下载CAS开源包,clientFilter.rar(可以在本人资源中找到)
3.建立一个CASClient工程,导入clientFilter.rar中java文件,或者直接将文件导入本地service工程:
4、配置web.xml,添加一个Filter,<filter-class>对应前面中导入的CASFilter.java文件
参数说明:
edu.yale.its.tp.cas.client.filter.loginUrl:CAS server认证端的 登陆地址
edu.yale.its.tp.cas.client.filter.validateUrl:CAS server认证端 ticket验证地址
edu.yale.its.tp.cas.client.filter.serverName:自己部署的应用的service地址
5.创建一个servlet验证一下sso效果
6.三种方式可以获得sso登录后的用户名
1.访问本地service
2.由于用户未登陆,跳转cas server验证
2.输入用户名密码登陆
2.重定向到用户访问的service,并且3种方式都成功打印出了用户信息
四、用户超时机制
一个session管理机制
1.用户登录时,系统随机生成一个字符串作为sessionId 用户相关信息生成User对象. 插入到全局对象SessionMap<String,User>中。User对象中记录用户的最后活动时间
String sessionId = ""+UUID.randomUUID()+new Random().nextLong()+cal.getTimeInMillis();Long nowTime = cal.getTimeInMillis();Long expiredTime = nowTime + Constants.Timeout_hour*60*60*1000;u.setActiveTime(nowTime);u.setExpiredTime(expiredTime);this.sessionMap.put(sessionId, u);
2.添加Servlet Filter 截获任何操作。请求中需要带上sessionID.从SessionMap<String,User>中根据sessionId取出User对象。从User对象中取出用户最后活动时间。
如果 当前时间 > 系统设定超时时间+用户最后活动时间
则用户超时。
如果用户没有操作,就把当前时间作为参数更新用户的最后活动时间
Long currentTime=cal.getTimeInMillis();User u = this.sessionMap.get(sessionId);Long expireTime = u.getExpiredTime();if(currentTime>=expireTime){this.sessionMap.remove(sessionId);}else{u.setActiveTime(currentTime);u.setExpiredTime(currentTime + Constants.Timeout_hour*60*60*1000);}
3.添加一个Thread执行定时任务,从SessionMap中清除超时的Entry
Runnable runnable = new Runnable() { public void run() { Long now = cal.getTimeInMillis(); Iterator<Entry<String, User>> iter = sessionMap.entrySet().iterator();while (iter.hasNext()) { Map.Entry<String,User> entry = (Map.Entry<String,User>)iter.next(); String key = (String)entry.getKey(); long expiredTime = entry.getValue().getExpiredTime(); if (expiredTime <= now) { sessionMap.remove(key); } } };}ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); service.scheduleAtFixedRate(runnable, 24, 1, TimeUnit.HOURS);
HashMap为不安全对象
第二个版本改成线程安全的ConcurrentHashMap
ConcurrentHashMap虽然是线程安全的类。但不能保证完整的一个业务操作的线程安全
如果 一个用户还差1s过期。
线程1:该用户登录系统 filter判断未过期。执行到此 系统调度开始执行线程2
线程2:1s过后 定时清除器执行任务判断此用户过期。从SessionMap中清除此用户相关信息。执行到此 系统调度开始执行线程1
线程1:用当前时间 更新用户时间的时候发现 该用户对于User对象不存在 被线程2清除。出错
所以需要在最外层的方法上是实现同步。
由于定时清除器是令起一个线程执行任务。所以使用一个ReentrantLock对象作为锁
最终完整代码如下:
import java.util.Calendar;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;import java.util.Random;import java.util.UUID;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReentrantLock;import org.apache.log4j.Logger;import bean.User;import constant.Constants;public class SessionManager {private static SessionManager instance = new SessionManager();private Map<String,User> sessionMap = new HashMap<String,User>();private Calendar cal = Calendar.getInstance();private ReentrantLock lock = new ReentrantLock();private static Logger log = Logger.getLogger("SessionManager");public static SessionManager getInstance(){return instance;}private SessionManager(){Runnable runnable = new Runnable() { public void run() { try {if(lock.tryLock(2, TimeUnit.SECONDS)){try{Long now = cal.getTimeInMillis(); log.info("execute the cleaning expired sessionId action in "+ cal.getTime()); Iterator<Entry<String, User>> iter = sessionMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<String,User> entry = (Map.Entry<String,User>)iter.next(); String key = (String)entry.getKey(); long expiredTime = entry.getValue().getExpiredTime(); if (expiredTime <= now) { sessionMap.remove(key); } }}finally{lock.unlock();}}} catch (InterruptedException e) {e.printStackTrace();} } }; ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); service.scheduleAtFixedRate(runnable, 24, 1, TimeUnit.HOURS);}public String createNewSessionId(User u){lock.lock();try{String sessionId = ""+UUID.randomUUID()+new Random().nextLong()+cal.getTimeInMillis();Long nowTime = cal.getTimeInMillis();Long expiredTime = nowTime + Constants.Timeout_hour*60*60*1000;u.setActiveTime(nowTime);u.setExpiredTime(expiredTime);this.sessionMap.put(sessionId, u);return sessionId;}finally{lock.unlock();}} public User getUser(String sessionId){lock.lock();try{Long currentTime=cal.getTimeInMillis();User u = this.sessionMap.get(sessionId);if(u != null){Long expireTime = u.getExpiredTime();if(currentTime>=expireTime){this.sessionMap.remove(sessionId);return null;}else{u.setActiveTime(currentTime);u.setExpiredTime(currentTime + Constants.Timeout_hour*60*60*1000);return u;}}else{return null;}}finally{lock.unlock();}}public void removeSession(String sessionId){lock.lock();try{sessionMap.remove(sessionId);}finally{lock.unlock();}}}
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- 工作记录
- IO 读写操作
- 8.工作区和暂存区
- 安卓(android) github上那些精彩的 开源项目
- 杭电1020 Encoding
- PostgreSQL 9.4文档 第6章 数据操作
- 工作记录
- 9.管理修改
- JavaScript图片无缝轮播代码
- 淘宝内部分享:怎么跳出MySQL的10个大坑
- [转载]2014十大的安全工具(ToolsWatch.org投票选出)
- 【web++_第四阶_Ui_照片轮播】
- 不加入域直接修改域用户密码
- 10.撤销修改
- (笔记)关于CSS一个样式display:block;的换行问题()