ThreadLocal及Java引用类型

一、引用类型

1、强引用(Strong Reference)

       一般用到最多的是强引用

2、软引用(Soft Reference)

       如果一个对象没有强引用,只有软引用,当JVM发现内存不够时,垃圾回收器便会回收这些对象

3、弱引用(Weak Reference)

       如果一个对象只有弱引用时,每次垃圾回收都会被回收

4、幻影引用(Phantom Reference)

       如果一个对象仅持有幻影引用,那么它就和没有引用指向它一样,在任何时候该对象都可能被垃圾回收器回收。

       幻影引用与软引用和弱引用的区别在于幻影引用必须和引用队列ReferenceQueue一起使用,幻影引用可以用来跟踪对象被回收的活动,因为当垃圾回收器准备回收一个对象的时候,如果发现它还有幻影引用,就会在回收之前,把这个幻影引用加入到与之关联的引用队列中去。

        这样,程序通过判断引用队列中是否已经加入了幻影引用,来了解被引用对象是否将要被垃圾回收器回收,如果发现某个幻影引用已经被加入到引用队列,那么就可以在引用对象被回收之前采取必要的行动。


二、ThreadLocal

1、简介

     ThreadLocal使用了弱引用,在一定程度上可以防止内存泄露。

2、ThreadLocal.ThreadLocalMap

      此map使用的key是经过WeakReference包装的ThreadLocal对象,如果ThreadLocal对象没有其他强引用和弱引用指向时,线程也不会继续持有ThreadLocal对象,根据JVM规范,它会被垃圾回收器在下次回收时被销毁,这在一定程度避免了内存泄露。

 /**
     * ThreadLocalMap is a customized hash map suitable only for
     * maintaining thread local values. No operations are exported
     * outside of the ThreadLocal class. The class is package private to
     * allow declaration of fields in class Thread.  To help deal with
     * very large and long-lived usages, the hash table entries use
     * WeakReferences for keys. However, since reference queues are not
     * used, stale entries are guaranteed to be removed only when
     * the table starts running out of space.
     */
    static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

      最好使用remove方法,自己显示释放内存。

     

三、ThreadLocal应用

(1)ThreadCache

/**
 * 利用thread local 存取servlet
 * 每次request的通用数据
 * @author caibosi
 * @created 2014-04-01
 */
public class ThreadCache {

    private static final ThreadLocal<AppThreadContext> cache = new ThreadLocal<AppThreadContext>(){
        @Override
        protected AppThreadContext initialValue() {
            return new AppThreadContext();
        }
    };

    private static class AppThreadContext{
        String ip;
        String userName;
        Integer userId;
        long startTime;
        String hostName;
        String logId;
        Map<String,Object> extraData = new ConcurrentHashMap<String,Object>();
    }

    public static void setIp(String ip){
        cache.get().ip = ip;
    }

    public static String getIp(){
        return cache.get().ip;
    }

    public static void setUserName(String userName){
        cache.get().userName = userName;
    }

    public static String getUserName(){
        return cache.get().userName;
    }

    public static void setUserId(Integer userId){
        cache.get().userId = userId;
    }

    public static Integer getUserId(){
        return cache.get().userId;
    }

    public static void setUser(Integer userId,String userName){
        cache.get().userId = userId;
        cache.get().userName = userName;
    }

    public static void setExtraData(String key, Object data) {
        cache.get().extraData.put(key, data);
    }

    public static Object getExtraData(String key) {
        return cache.get().extraData.get(key);
    }

    public static void setStartTime(){
        cache.get().startTime = System.currentTimeMillis();
    }

    public static long getStartTime(){
        return cache.get().startTime;
    }

    public static void setLogId(){
        String logId = UUID.randomUUID().toString();
        cache.get().logId = logId;
    }

    public static String getLogId(){
        return cache.get().logId;
    }

    public static void setHostName(String hostName){
        cache.get().hostName = hostName;
    }

    public static String getHostName(){
        return cache.get().hostName;
    }

    public static void release() {
        cache.remove();
    }
}


    使用及释放:

   

/**
 * @author caibosi
 * @created 2014-04-01
 */
public class AclInterceptor extends HandlerInterceptorAdapter {

    private static final Logger logger = LoggerFactory.getLogger(AclInterceptor.class);

    @Resource
    private AntPathMatcher antPathMatcher;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //记录每次访问的时间
        ThreadCache.setStartTime();

        //记录host
        String hostname = null;
        try {
            hostname = InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            logger.error(e.getMessage(), e);
        }

        if (hostname == null) {
            hostname = "";
        }

        ThreadCache.setHostName(hostname);

        //url处理
        String uri = request.getRequestURI();

        // 静态资源不处理
        if ("/favicon.ico".equals(uri)
                || antPathMatcher.match("/static/**", uri)) {
            return super.preHandle(request, response, handler);
        }

        request.setAttribute("_uri", uri);

        // 获取登录用户的ID和用户名信息
        //sso或者登录跳转

        //debug mode
        if(ConfigTool.isDebugEnv()){
                ThreadCache.setUserId(ConfigTool.getDebugUserId());
        }

        //进行权限校验
        boolean hasUrlAccess = true;
        if(hasUrlAccess == false){
            throw new AccessDeniedException("没有访问权限");
        }
        return super.preHandle(request,response,handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        super.postHandle(request, response, handler, modelAndView);
        String uri = request.getRequestURI().replaceAll(";.*", "");
        if (uri.endsWith(".ajax") && (uri.indexOf('.') != -1 || uri.indexOf("/cron/") != -1)) {
            return;
        }
        long time = ThreadCache.getStartTime();
        if (time > 0) {
            long timeCost = System.currentTimeMillis() - time;
            logger.info("time used," + request.getRequestURI() + ",logId:" + ThreadCache.getLogId()
                    + "," + timeCost + "ms");

            request.setAttribute("_timeCost", timeCost);
        }
        //thread local资源释放
        ThreadCache.release();
    }
}


(2)事务嵌套

/**
 * 封装了事务管理操作
 * 主要是实现了事务嵌套的管理
 * @author caibosi
 * @created 2013-10-29
 */
public class TransManager {

    private final static Logger logger = LoggerFactory.getLogger(TransManager.class);

    private final static ThreadLocal<Transaction> transLocal = new ThreadLocal<Transaction>();

    /**
     * 设置事务的隔离级别
     * @param level
     */
    public static void setTransactionViolation(TransactionLevel level){
        Transaction tran = transLocal.get();
        Connection conn = tran.getConn();
        try {
            conn.setTransactionIsolation(level.getValue());
        } catch (SQLException e) {
            logger.error("setting transaction violation failed",e);
            throw new RuntimeException("setting transaction violation failed",e);
        }
    }
    /**
     * 开启事务
     */
    public static void openTransaction(TransactionLevel level){
        Transaction tran = transLocal.get();
        if(tran==null){
            tran = new Transaction();
            Connection conn = DBManager.getConnection();
            try {
                conn.setAutoCommit(false);
                if(level!=null && level.getValue()!= TransactionLevel.getDefault().getValue()){
                    conn.setTransactionIsolation(level.getValue());
                }
            } catch (SQLException e) {
                logger.error("open transaction error",e);
                throw new RuntimeException("open transaction error");
            }
            tran.setConn(conn);
            tran.setCommitCount(0);
            tran.setOpenCount(1);
            tran.setCurrentCount(1);
            transLocal.set(tran);
        } else{
            tran.setOpenCount(tran.getOpenCount()+1);
            tran.setCurrentCount(tran.getCurrentCount()+1);
        }

    }

    /**
     * 提交事务
     */
    public static void commitTransaction(){
        Transaction tran = transLocal.get();
        if(tran == null)
            throw new RuntimeException("commit transaction , transaction null");
        //判断要不要真正提交
        if(tran.getCurrentCount()>1){
            tran.setCommitCount(tran.getCommitCount() + 1);
            tran.setCurrentCount(tran.getCurrentCount()-1);
            return;
        }
        Connection conn = tran.getConn();
        if(tran.canReallyCommit()){
            try {
                conn.commit();
            } catch (SQLException e) {
                logger.error("commit transaction error",e);
                throw new RuntimeException("commit transaction error");
            }
        }

    }

    /**
     * 回滚事务
     * 这里不真正回滚
     * 做个标记
     * 在close的时候判断是否要回滚
     */
    public static void rollbackTransaction(){
       Transaction tran = transLocal.get();
//        if(tran == null){
//            logger.info("rollback trans null");
//            return;
//        }

       tran.setCommitCount(0);
    }

    /**
     * 终止事务
     */
    public static void closeTransaction(){
        Transaction tran = transLocal.get();
//        if(tran == null){
//            logger.info("close trans null");
//            return;
//        }

        //如果当前还有1个以上的事务,不能真正关闭事务
        if(tran.getCurrentCount()>1){
            tran.setCommitCount(tran.getCurrentCount()-1);
            return;
        }
        //如果只剩一个事务,则真正结束事务,清理变量
        Connection conn = tran.getConn();
        if(tran.canReallyCommit()==false){
            try {
                conn.rollback();
                logger.info("#### rollback transaction");
            } catch (SQLException e) {
                logger.error("rollback transaction failed",e);
            }finally{
                transLocal.remove();
                try {
                    conn.close();
                } catch (SQLException e) {
                    logger.error("close conn failed",e);
                    throw new RuntimeException("close conn failed",e);
                }

            }
        }
    }
}

/**
 * 封装了connection
 * 事务提交次数
 * 事务嵌套层次
 * 事务开启次数
 * 用于控制事务嵌套
 * @author caibosi
 * @created 2013-10-29
 */
class Transaction {

    private Connection conn;

    /**
     * 统计事务次数
     * 每开启一次,增加一次
     * 用来与commitCount来判断
     * 事务是否全部提交
     */
    private int openCount;

    /**
     * 提交次数
     * 每提交一次,增加一次
     */
    private int commitCount;

    /**
     *  当前的事务数
     *  开启一次增加1
     *  提交一次减去1
     */
    private int currentCount;

    /**
     * 判断是否能够真正提交事务
     * @return
     */
    boolean canReallyCommit(){
       return commitCount+1 == openCount;
    }

    Connection getConn() {
        return conn;
    }

    void setConn(Connection conn) {
        this.conn = conn;
    }

    int getCommitCount() {
        return commitCount;
    }

    void setCommitCount(int commitCount) {
        this.commitCount = commitCount;
    }

    int getOpenCount() {
        return openCount;
    }

    void setOpenCount(int openCount) {
        this.openCount = openCount;
    }

    int getCurrentCount() {
        return currentCount;
    }

    void setCurrentCount(int currentCount) {
        this.currentCount = currentCount;
    }
}


你可能感兴趣的:(ThreadLocal及Java引用类型)