《ThreadLocal》

1.ThreadLocal线程隔离和内存泄漏

1.get方法:是用来获取ThreadLocal在线程中保存的变量副本

2.set方法:是用来设置当前线程中的变量副本

3.remove方法:是用来移除当前线程的变量副本

4.initialValue方法:被protected修饰,一般在使用的时候可以重写,初始化变量副本

5.ThreadLocalMap:ThreadLocal的静态内部类,里面维护了一个Entry数组,主要用来保存线程的本地变量副本

6.Entry:Entry是ThreadLocalMap的静态内部类,继承了弱引用 WeakReference<ThreadLocal<?>> ,弱引用可以帮助程序更好的GC,
  更好的做垃圾回收,而ThreadLocal<?>是作为Entry的key,Entry的key被GC掉,会导致KNULL的Entry无法被移除,这就造成了内存泄漏。
  
7.内存泄漏的避免:在使用完ThreadLocal中的本地线程变量副本后清除掉副本。 

8.ThreadLocal和synchronized关键字完全相反,ThreadLocal是空间换时间,synchronized是时间换空间,
  ThreadLocal用于线程隔离,实现每个线程使用自己的本地变量副本,以解决并发问题,  synchronized用于多线程间共享数据

9.内存溢出(memory overflow):是指不能申请到足够的内存进行使用,就会发生内存溢出,比如出现的OOM(Out Of Memory)

10.内存泄漏(memory lack):内存泄露是指在程序中已经动态分配的堆内存由于某种原因未释放或者无法释放(已经没有用处了,但是没有释放),造成系统内存的浪费,这种现象叫“内存泄露”。
  当内存泄露到达一定规模后,造成系统能申请的内存较少,甚至无法申请内存,最终导致内存溢出,所以内存泄露是导致内存溢出的一个原因。

2.ThreadLocal的源码

public class ThreadLocal<T> {

	public T get() {
	       //获取当前线程
	        Thread t = Thread.currentThread();
	        //获取当前线程下的ThreadLocalMap 
	        ThreadLocalMap map = getMap(t);
	        if (map != null) {
	             //根据当前ThreadLocal对象获取Entry
	            ThreadLocalMap.Entry e = map.getEntry(this);
	            if (e != null) {
	                 //从Entry中获取本地线程变量副本
	                @SuppressWarnings("unchecked")
	                T result = (T)e.value;
	                return result;
	            }
	        }
	        //如果当前线程的ThreadLocalMap等于null,
	        //就为当前线程创建一个ThreadLocalMap
	        return setInitialValue();
	    }

	 private T setInitialValue() {
	        T value = initialValue();
	        Thread t = Thread.currentThread();
	        ThreadLocalMap map = getMap(t);
	        if (map != null)
	            map.set(this, value);
	        else
	            createMap(t, value);
	        return value;
	    }

	 public void set(T value) {
	        Thread t = Thread.currentThread();
	        ThreadLocalMap map = getMap(t);
	        if (map != null)
	            map.set(this, value);
	        else
	            createMap(t, value);
	    }    

	public void remove() {
	         ThreadLocalMap m = getMap(Thread.currentThread());
	         if (m != null)
	             m.remove(this);
	     }    

	ThreadLocalMap getMap(Thread t) {
		        return t.threadLocals;
	}
	
   void createMap(Thread t, T firstValue) {
         t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
}    
static class ThreadLocalMap {
        //Entry数组初始化容量
        private static final int INITIAL_CAPACITY = 16;
        //ThreadLocalMap下一次扩容的阈值
        private int threshold;
        //ThreadLocalMap存储数据的容器
        private Entry[] table;

       ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
        
         private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }
        
   static class Entry extends WeakReference<ThreadLocal<?>> {
        
          Object value;

          Entry(ThreadLocal<?> k, Object v) {
              super(k);
              value = v;
          }
   }
        
     private Entry getEntry(ThreadLocal<?> key) {
         int i = key.threadLocalHashCode & (table.length - 1);
         Entry e = table[i];
         if (e != null && e.get() == key)
             return e;
         else
             return getEntryAfterMiss(key, i, e);
     }

3.ThreadLocal使用示例

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.pool.HikariPool;

import java.sql.Connection;
import java.sql.SQLException;

public class JdbcUtils {

    private static final ThreadLocal<Connection> localConnection = new ThreadLocal<>();
    private static final HikariPool hikariPool;
    static 
    {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/day1106");
        config.setDriverClassName("com.mysql.jdbc.Driver");
        config.setUsername("root");
        config.setPassword("root");
        config.setMinimumIdle(1);
        config.setMaximumPoolSize(10);
        hikariPool = new HikariPool(config);
    }

    public static Connection getInstance() throws SQLException {
        Connection connection = localConnection.get();
        if (connection!=null)
        {
            return connection;
        }
        else
        {
            Connection cc = hikariPool.getConnection();
            localConnection.set(cc);
            return cc;
        }
    }

    /**
     * 防止内存泄漏,使用完毕后 可清除线程本地变量
     */
    public static void removeLocalConnection(){
        localConnection.remove();
    }
}



import lombok.extern.slf4j.Slf4j;

import java.sql.*;

@Slf4j
public class CRUD {
    public static void main(String[] args)  {

        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet rs = null;
        try
        {
            @SuppressWarnings("SqlResolve")
             //可以修改为写操作 模拟事务
            String sql = "SELECT * FROM role WHERE role_id=?";

            connection = JdbcUtils.getInstance();
            //开启事务
            connection.setAutoCommit(false);

            statement = connection.prepareStatement(sql);
            statement.setInt(1,1);
            rs = statement.executeQuery();
            while (rs.next()){
                int role_id = rs.getInt("role_id");
                System.out.println("role_id = " + role_id);
                String role_name = rs.getString("role_name");
                System.out.println("role_name = " + role_name);
                Date create_time = rs.getDate("create_time");
                System.out.println("create_time = " + create_time);
                Date update_time = rs.getDate("update_time");
                System.out.println("update_time = " + update_time);
            }
            //提交事务
            connection.commit();
        }
        catch (SQLException e)
        {
           log.error(e.getLocalizedMessage(),e);
            try 
            {
                if (connection!=null)
                {
                    //异常进行事务回滚
                    connection.rollback();
                }
            }
            catch (SQLException ex)
            {
             log.error(ex.getLocalizedMessage(),ex);
            }
        }
        finally
        {
            //清除ThreadLocal中的本地线程变量副本,防止内存泄漏
           JdbcUtils.removeLocalConnection();
          if (connection!=null)
          {
              try 
              {
                  connection.close();
              } 
              catch (SQLException e) 
              {
                  log.error(e.getLocalizedMessage(),e);
              }
          }
          if (statement!=null)
          {
              try 
              {
                  statement.close();
              } 
              catch (SQLException e) 
              {
                  log.error(e.getLocalizedMessage(),e);
              }
          }
          if (rs!=null)
          {
              try 
              {
                  rs.close();
              } 
              catch (SQLException e) 
              {
                  log.error(e.getLocalizedMessage(),e);
              }
          }
        }
    }
}

4.ThreadLocal内存分布图

在这里插入图片描述

5.ThreadLocal使用拓展:阅读spring-web

  • RequestContextFilter
  • RequestContextHolder
  • RequestContextListener
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值