<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Java并發編程:深入剖析ThreadLocal

      Java并發編程:深入剖析ThreadLocal

        想必很多朋友對ThreadLocal并不陌生,今天我們就來一起探討下ThreadLocal的使用方法和實現原理。首先,本文先談一下對ThreadLocal的理解,然后根據ThreadLocal類的源碼分析了其實現原理和使用需要注意的地方,最后給出了兩個應用場景。

        以下是本文目錄大綱:

        一.對ThreadLocal的理解

        二.深入解析ThreadLocal類

        三.ThreadLocal的應用場景

        若有不正之處請多多諒解,并歡迎批評指正。

        請尊重作者勞動成果,轉載請標明原文鏈接:

         http://www.rzrgm.cn/dolphin0520/p/3920407.html

      一.對ThreadLocal的理解

        ThreadLocal,很多地方叫做線程本地變量,也有些地方叫做線程本地存儲,其實意思差不多。可能很多朋友都知道ThreadLocal為變量在每個線程中都創建了一個副本,那么每個線程可以訪問自己內部的副本變量。

        這句話從字面上看起來很容易理解,但是真正理解并不是那么容易。

        我們還是先來看一個例子:

      class ConnectionManager {
      	
      	private static Connection connect = null;
      	
      	public static Connection openConnection() {
      		if(connect == null){
      			connect = DriverManager.getConnection();
      		}
      		return connect;
      	}
      	
      	public static void closeConnection() {
      		if(connect!=null)
      			connect.close();
      	}
      }
      

         假設有這樣一個數據庫鏈接管理類,這段代碼在單線程中使用是沒有任何問題的,但是如果在多線程中使用呢?很顯然,在多線程中使用會存在線程安全問題:第一,這里面的2個方法都沒有進行同步,很可能在openConnection方法中會多次創建connect;第二,由于connect是共享變量,那么必然在調用connect的地方需要使用到同步來保障線程安全,因為很可能一個線程在使用connect進行數據庫操作,而另外一個線程調用closeConnection關閉鏈接。

        所以出于線程安全的考慮,必須將這段代碼的兩個方法進行同步處理,并且在調用connect的地方需要進行同步處理。

        這樣將會大大影響程序執行效率,因為一個線程在使用connect進行數據庫操作的時候,其他線程只有等待。

        那么大家來仔細分析一下這個問題,這地方到底需不需要將connect變量進行共享?事實上,是不需要的。假如每個線程中都有一個connect變量,各個線程之間對connect變量的訪問實際上是沒有依賴關系的,即一個線程不需要關心其他線程是否對這個connect進行了修改的。

        到這里,可能會有朋友想到,既然不需要在線程之間共享這個變量,可以直接這樣處理,在每個需要使用數據庫連接的方法中具體使用時才創建數據庫鏈接,然后在方法調用完畢再釋放這個連接。比如下面這樣:

      class ConnectionManager {
      	
      	private  Connection connect = null;
      	
      	public Connection openConnection() {
      		if(connect == null){
      			connect = DriverManager.getConnection();
      		}
      		return connect;
      	}
      	
      	public void closeConnection() {
      		if(connect!=null)
      			connect.close();
      	}
      }
      
      
      class Dao{
      	public void insert() {
      		ConnectionManager connectionManager = new ConnectionManager();
      		Connection connection = connectionManager.openConnection();
      		
      		//使用connection進行操作
      		
      		connectionManager.closeConnection();
      	}
      }
      

         這樣處理確實也沒有任何問題,由于每次都是在方法內部創建的連接,那么線程之間自然不存在線程安全問題。但是這樣會有一個致命的影響:導致服務器壓力非常大,并且嚴重影響程序執行性能。由于在方法中需要頻繁地開啟和關閉數據庫連接,這樣不盡嚴重影響程序執行效率,還可能導致服務器壓力巨大。

        那么這種情況下使用ThreadLocal是再適合不過的了,因為ThreadLocal在每個線程中對該變量會創建一個副本,即每個線程內部都會有一個該變量,且在線程內部任何地方都可以使用,線程之間互不影響,這樣一來就不存在線程安全問題,也不會嚴重影響程序執行性能。

        但是要注意,雖然ThreadLocal能夠解決上面說的問題,但是由于在每個線程中都創建了副本,所以要考慮它對資源的消耗,比如內存的占用會比不使用ThreadLocal要大。

      二.深入解析ThreadLocal類

        在上面談到了對ThreadLocal的一些理解,那我們下面來看一下具體ThreadLocal是如何實現的。

        先了解一下ThreadLocal類提供的幾個方法:

      public T get() { }
      public void set(T value) { }
      public void remove() { }
      protected T initialValue() { }
      

         get()方法是用來獲取ThreadLocal在當前線程中保存的變量副本,set()用來設置當前線程中變量的副本,remove()用來移除當前線程中變量的副本,initialValue()是一個protected方法,一般是用來在使用時進行重寫的,它是一個延遲加載方法,下面會詳細說明。

        首先我們來看一下ThreadLocal類是如何為每個線程創建一個變量的副本的。

        先看下get方法的實現:

        

         第一句是取得當前線程,然后通過getMap(t)方法獲取到一個map,map的類型為ThreadLocalMap。然后接著下面獲取到<key,value>鍵值對,注意這里獲取鍵值對傳進去的是  this,而不是當前線程t。

        如果獲取成功,則返回value值。

        如果map為空,則調用setInitialValue方法返回value。

        我們上面的每一句來仔細分析:

        首先看一下getMap方法中做了什么:

        

        可能大家沒有想到的是,在getMap中,是調用當期線程t,返回當前線程t中的一個成員變量threadLocals。

        那么我們繼續取Thread類中取看一下成員變量threadLocals是什么:

        

        實際上就是一個ThreadLocalMap,這個類型是ThreadLocal類的一個內部類,我們繼續取看ThreadLocalMap的實現:

        

        可以看到ThreadLocalMap的Entry繼承了WeakReference,并且使用ThreadLocal作為鍵值。

        然后再繼續看setInitialValue方法的具體實現:

        很容易了解,就是如果map不為空,就設置鍵值對,為空,再創建Map,看一下createMap的實現:

        

        至此,可能大部分朋友已經明白了ThreadLocal是如何為每個線程創建變量的副本的:

        首先,在每個線程Thread內部有一個ThreadLocal.ThreadLocalMap類型的成員變量threadLocals,這個threadLocals就是用來存儲實際的變量副本的,鍵值為當前ThreadLocal變量,value為變量副本(即T類型的變量)。

        初始時,在Thread里面,threadLocals為空,當通過ThreadLocal變量調用get()方法或者set()方法,就會對Thread類中的threadLocals進行初始化,并且以當前ThreadLocal變量為鍵值,以ThreadLocal要保存的副本變量為value,存到threadLocals。

        然后在當前線程里面,如果要使用副本變量,就可以通過get方法在threadLocals里面查找。

        下面通過一個例子來證明通過ThreadLocal能達到在每個線程中創建變量副本的效果:

      public class Test {
      	ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
      	ThreadLocal<String> stringLocal = new ThreadLocal<String>();
      
      	
      	public void set() {
      		longLocal.set(Thread.currentThread().getId());
      		stringLocal.set(Thread.currentThread().getName());
      	}
      	
      	public long getLong() {
      		return longLocal.get();
      	}
      	
      	public String getString() {
      		return stringLocal.get();
      	}
      	
      	public static void main(String[] args) throws InterruptedException {
      		final Test test = new Test();
      		
      		
      		test.set();
      		System.out.println(test.getLong());
      		System.out.println(test.getString());
      	
      		
      		Thread thread1 = new Thread(){
      			public void run() {
      				test.set();
      				System.out.println(test.getLong());
      				System.out.println(test.getString());
      			};
      		};
      		thread1.start();
      		thread1.join();
      		
      		System.out.println(test.getLong());
      		System.out.println(test.getString());
      	}
      }
      

         這段代碼的輸出結果為:

        

        從這段代碼的輸出結果可以看出,在main線程中和thread1線程中,longLocal保存的副本值和stringLocal保存的副本值都不一樣。最后一次在main線程再次打印副本值是為了證明在main線程中和thread1線程中的副本值確實是不同的。

        總結一下:

        1)實際的通過ThreadLocal創建的副本是存儲在每個線程自己的threadLocals中的;

        2)為何threadLocals的類型ThreadLocalMap的鍵值為ThreadLocal對象,因為每個線程中可有多個threadLocal變量,就像上面代碼中的longLocal和stringLocal;

        3)在進行get之前,必須先set,否則會報空指針異常;

            如果想在get之前不需要調用set就能正常訪問的話,必須重寫initialValue()方法。

          因為在上面的代碼分析過程中,我們發現如果沒有先set的話,即在map中查找不到對應的存儲,則會通過調用setInitialValue方法返回i,而在setInitialValue方法中,有一個語句是T value = initialValue(), 而默認情況下,initialValue方法返回的是null。

        

        看下面這個例子:

        

      public class Test {
      	ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
      	ThreadLocal<String> stringLocal = new ThreadLocal<String>();
      
      	public void set() {
      		longLocal.set(Thread.currentThread().getId());
      		stringLocal.set(Thread.currentThread().getName());
      	}
      	
      	public long getLong() {
      		return longLocal.get();
      	}
      	
      	public String getString() {
      		return stringLocal.get();
      	}
      	
      	public static void main(String[] args) throws InterruptedException {
      		final Test test = new Test();
      		
      		System.out.println(test.getLong());
      		System.out.println(test.getString());
      
      		Thread thread1 = new Thread(){
      			public void run() {
      				test.set();
      				System.out.println(test.getLong());
      				System.out.println(test.getString());
      			};
      		};
      		thread1.start();
      		thread1.join();
      		
      		System.out.println(test.getLong());
      		System.out.println(test.getString());
      	}
      }
      

         在main線程中,沒有先set,直接get的話,運行時會報空指針異常。

        但是如果改成下面這段代碼,即重寫了initialValue方法:

      public class Test {
      	ThreadLocal<Long> longLocal = new ThreadLocal<Long>(){
      		protected Long initialValue() {
      			return Thread.currentThread().getId();
      		};
      	};
      	ThreadLocal<String> stringLocal = new ThreadLocal<String>(){;
      		protected String initialValue() {
      			return Thread.currentThread().getName();
      		};
      	};
      
      	
      	public void set() {
      		longLocal.set(Thread.currentThread().getId());
      		stringLocal.set(Thread.currentThread().getName());
      	}
      	
      	public long getLong() {
      		return longLocal.get();
      	}
      	
      	public String getString() {
      		return stringLocal.get();
      	}
      	
      	public static void main(String[] args) throws InterruptedException {
      		final Test test = new Test();
      
      		test.set();
      		System.out.println(test.getLong());
      		System.out.println(test.getString());
      	
      		
      		Thread thread1 = new Thread(){
      			public void run() {
      				test.set();
      				System.out.println(test.getLong());
      				System.out.println(test.getString());
      			};
      		};
      		thread1.start();
      		thread1.join();
      		
      		System.out.println(test.getLong());
      		System.out.println(test.getString());
      	}
      }
      

         就可以直接不用先set而直接調用get了。

      三.ThreadLocal的應用場景

        最常見的ThreadLocal使用場景為 用來解決 數據庫連接、Session管理等。

        如:

      private static ThreadLocal<Connection> connectionHolder
      = new ThreadLocal<Connection>() {
      public Connection initialValue() {
      	return DriverManager.getConnection(DB_URL);
      }
      };
      
      public static Connection getConnection() {
      return connectionHolder.get();
      }
      

         下面這段代碼摘自:

        http://www.iteye.com/topic/103804

          private static final ThreadLocal threadSession = new ThreadLocal();
      
          public static Session getSession() throws InfrastructureException {
              Session s = (Session) threadSession.get();
              try {
                  if (s == null) {
                      s = getSessionFactory().openSession();
                      threadSession.set(s);
                  }
              } catch (HibernateException ex) {
                  throw new InfrastructureException(ex);
              }
              return s;
          }
      

       

        參考資料:

        《深入理解Java虛擬機》

        《Java編程思想》

        http://ifeve.com/thread-management-10/

        http://www.ibm.com/developerworks/cn/java/j-threads/index3.html

        http://www.iteye.com/topic/103804

        http://www.iteye.com/topic/777716

        http://www.iteye.com/topic/757478

        http://blog.csdn.net/ghsau/article/details/15732053

        http://ispring.iteye.com/blog/162982

        http://blog.csdn.net/imzoer/article/details/8262101

        http://www.blogjava.net/wumi9527/archive/2010/09/10/331654.html

        http://bbs.csdn.net/topics/380049261

      posted @ 2014-08-24 11:34  Matrix海子  閱讀(396109)  評論(75)    收藏  舉報
      主站蜘蛛池模板: 亚洲熟女精品一区二区| 老司机午夜精品视频资源| 狠狠婷婷色五月中文字幕| 中文字幕精品亚洲无线码二区| 亚洲综合伊人五月天中文| 好吊视频专区一区二区三区| 日本一区二区三区在线播放| 日韩高清免费一码二码三码| 精品国产性色av网站| 国产精品一码二码三码四码| 忘忧草社区在线www| 九色综合久99久久精品| 久青草国产在视频在线观看 | 久久亚洲精品中文字幕馆| 视频一区视频二区视频三 | 亚洲欧美色综合影院| 1精品啪国产在线观看免费牛牛| 中文字幕日韩精品有码| 国产欧美一区二区三区免费视频| 国产一区一一区高清不卡| 日韩精品中文字幕人妻| 高清破外女出血AV毛片| 你懂的在线视频一区二区| 亚洲av成人午夜福利| 色综合欧美亚洲国产| 少妇人妻真实偷人精品| 国产精品中文字幕观看| 四虎国产精品永久地址99| 纯肉高h啪动漫| 国产精品人成视频免费国产| 四虎永久地址www成人| 乌克兰丰满女人a级毛片右手影院| 久久成人国产精品免费软件| 中文字幕成人精品久久不卡| 97精品久久天干天天天按摩 | 婷婷四虎东京热无码群交双飞视频| 国产麻豆9l精品三级站| 成人午夜在线观看日韩| 少妇爽到呻吟的视频| 国产WW久久久久久久久久| 亚洲欧美日韩久久一区二区|