工作记录

来源:互联网 发布: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.rarjava文件,或者直接将文件导入本地service工程:

4、配置web.xml,添加一个Filter<filter-class>对应前面中导入的CASFilter.java文件


再给此filter配置参数:


参数说明:

edu.yale.its.tp.cas.client.filter.loginUrlCAS server认证端的 登陆地址

edu.yale.its.tp.cas.client.filter.validateUrlCAS 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);



第一个版本 SessionMap使用 HashMap

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();}}}


0 0
原创粉丝点击