JAVA後端《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使用拓展:閱讀spring-web

  • RequestContextFilter
  • RequestContextHolder
  • RequestContextListener
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章