博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ThreadLocal及Java引用类型
阅读量:6690 次
发布时间:2019-06-25

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

  hot3.png

一、引用类型

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
 {            /** 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
 cache = new ThreadLocal
(){        @Override        protected AppThreadContext initialValue() {            return new AppThreadContext();        }    };    private static class AppThreadContext{        String ip;        String userName;        Integer userId;        long startTime;        String hostName;        String logId;        Map
 extraData = new ConcurrentHashMap
();    }    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
 transLocal = new ThreadLocal
();    /**     * 设置事务的隔离级别     * @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;    }}

转载于:https://my.oschina.net/scipio/blog/282675

你可能感兴趣的文章
Linux-常用快捷键
查看>>
Java 如何产生UUID
查看>>
学习第3天
查看>>
c语言 拼接字符串
查看>>
实验:传输层:TCP协议
查看>>
gluon 实现多层感知机MLP分类FashionMNIST
查看>>
需求分析评价
查看>>
LeetCode – Refresh – Longest Substring with At Most Two Distinct Characters
查看>>
shell编程系列19--文本处理三剑客之awk中的字符串函数
查看>>
pytorch的函数中的dilation参数的作用
查看>>
数据排序
查看>>
HDU2504 又见GCD
查看>>
CenOS下安装jdk
查看>>
online_judge_1106
查看>>
MVC和三层架构的区别
查看>>
笔记分享
查看>>
B树 B+树 B*树
查看>>
转载:汇编语言里 eax, ebx, ecx, edx, esi, edi, ebp, esp这些都是什么意思啊?
查看>>
友盟添加事件统计
查看>>
洛谷P2824 排序
查看>>