스레드 로컬과 스레드 멤버 변수 사이에는 여전히 차이가 있습니다. ThreadLocal 클래스는 스레드 로컬 변수를 제공합니다. 이 로컬 변수는 일반 회원 변수와 다릅니다. ThreadLocal 변수가 여러 스레드에서 사용하는 경우 각 스레드는 변수의 사본 만 얻을 수 있습니다. 이것은 Java API의 설명입니다. API 소스 코드를 읽음으로써 사본이 아님을 알았습니다. 사본의 개념은 무엇입니까? 클론? 아니면 다른 것, 너무 모호합니다.
정확하게 말하면, Type ThreadLocal의 변수 내부의 레지스트리 (Map <Thread, t>)가 변경되었지만 Type ThreadLocal 자체의 변수는 실제로 하나이며 이것이 본질입니다!
예는 다음과 같습니다.
1. 표준 예제
MythreadLocal 클래스가 정의되고 해당 객체 TLT가 생성되며 4 개의 스레드에서 사용됩니다. 결과적으로, 4 개의 스레드의 TLT 변수는 공유되지 않습니다. 두 번째는 자체를 사용하는 것이며, 이는 네 개의 스레드가 TLT (클론)의 사본을 사용한다는 것을 보여줍니다.
/*** strooklocal class*/public class mythreadlocal {// int 또는 정수 데이터를 저장하기 위해 threadlocal 변수를 정의하십시오. private strandlocal <integer> tl = new ThreadLocal <integer> () {@override protected initeger initialValue () {return 0; }}; public integer getnextnum () {// tl의 값을 얻고 1을 추가하고 t1 tl.set (tl.get () + 1)의 값을 업데이트합니다. return tl.get (); }} / *** 테스트 스레드*/ public class testthread는 스레드 {private mythreadlocal tlt = new MythreadLocal (); public testthread (mythreadlocal tlt) {this.tlt = tlt; } @override public void run () {for (int i = 0; i <3; i ++) {system.out.println (thread.currentthread (). getName () + "/t" + tlt.getNextNum ()); }}} / *** ThreadLocal test*/ public class test {public static void main (String [] args) {mythreadlocal tlt = new Mythreadlocal (); 스레드 T1 = New TestThread (TLT); 스레드 T2 = New TestThread (TLT); 스레드 T3 = New TestThread (TLT); 스레드 T4 = New TestThread (TLT); t1.start (); t2.start (); t3.start (); t4.start (); }}
세 스레드는 독립적으로 번호가 매겨져 있으며 서로 영향을 미치지 않는다는 것을 알 수 있습니다.
스레드 -0 1 스레드 -1 1 스레드 -0 2 스레드 -0 3 스레드 -1 3 스레드 -3 1 스레드 -3 1 스레드 -3 2 스레드 -3 3 프로세스 종료 코드 0
TLT 객체는 하나이며, 넌센스 TL 객체도 하나입니다. 조합 관계는 일대일이기 때문입니다. 그러나 스레드 수가 증가함에 따라 많은 정수 객체가 생성됩니다. 그것은 단지 정수와 int가 이미 일반적입니다. 그래서 정수의 객체 속성을 느낄 수 없습니다.
2. ThreadLocal을 사용하지 마십시오
ThreadLocal을 사용하지 않으면 MythreadLocal 클래스를 다시 정의하면됩니다.
/ *** ThreadLocal Class*/ Public Class MythreadLocal {Private Integer T1 = 0; 공개 정수 getnextnum () {return t1 = t1+1; } // int 또는 Integer Data를 저장하기 위해 ThreadLocal 변수 정의 // private ThreadLocal <integer> tl = new ThreadLocal <integer> () {// @override // Protected Integer InitialValue () {// return 0; //} //}; // // public integer getnextnum () {// // tl 값을 얻고 1을 추가하고 t1 // tl.set (tl.get () + 1)의 값을 업데이트합니다. // return tl.get (); //}}
그런 다음 테스트를 실행하십시오.
스레드 -2 스레드 -2 2 스레드 -1 4 스레드 -3 3 스레드 -3 9 스레드 -3 10 스레드 -1 8 스레드 -0 7 스레드 -0 11 스레드 -2 5 프로세스 종료 코드 0
여기에서 4 개의 스레드가 TLT 변수를 공유하고 각 스레드는 TLT의 속성을 직접 수정합니다.
3. 스레드 라소를 혼자서 실현하십시오
패키지 com.lavasoft.test2; java.util.collections import; java.util.hashmap import; java.util.map import; /*** ThreadLocal class*/public class mythreadlocal {// int 또는 정수 데이터를 저장하기 위해 threadlocal 변수를 정의하십시오. private com.lavasoft.test2.testlocal <integer> tl = new com.lavasoft.test2.threadlocal <integer> () {@overrride protected Integer Integer () }}; public integer getnextnum () {// tl의 값을 얻고 1을 추가하고 t1 tl.set (tl.get () + 1)의 값을 업데이트합니다. return tl.get (); }} class ThreadLocal <t> {private map <Thread, t> map = collections.synchronizedMap (new Hashmap <Thread, t> ()); public ThreadLocal () {} protected t initialValue () {return null; } public t get () {Thread T = Thread.CurrentThread (); t obj = map.get (t); if (obj == null &&! map.containskey (t)) {obj = initialValue (); map.put (t, obj); } return obj; } public void set (t value) {map.put (Thread.currentThread (), value); } public void remove () {map.remove (Thread.currentThread ()); }}
시험 실행 :
스레드 -0 1 스레드 -0 2 스레드 -0 3 스레드 -2 1 스레드 -3 1 스레드 -2 3 스레드 -3 2 스레드 -3 3 스레드 -1 3 프로세스 종료 코드 0
놀랍게도,이 ThreadLocal 의이 카피 캣 버전도 잘 작동하여 Java API에서 ThreadLocal의 기능을 구현합니다.
4. 현상을 통해 본질을 참조하십시오
실제로, 프로그램 관점에서 볼 때, TLT 변수는 실제로 의심의 여지없이 하나입니다. 그러나 인쇄 번호가 서로 영향을 미치지 않는 이유는 무엇입니까?
정수를 사용하기 때문입니까? -----아니요.
그 이유는 : Protected t initialValue () 및 get (). 각 스레드가 호출되면 get ()가 맵에 존재하지 않으면 생성되기 때문입니다. 그것이 호출되면, 새로운 변수는 Type T로 생성됩니다. 물론 새로 생성 될 때마다, 각각은 서로에게 영향을 미치지 않고 그것들을 사용합니다.
본질을 명확하게 보려면 정수를 대체하고 일부 클래스를 다시 작성하십시오.
패키지 com.lavasoft.test2; java.util.collections import; java.util.hashmap import; java.util.map import; /*** strooklocal class*/public class mythreadlocal {// int 또는 정수 데이터를 저장하기 위해 ThreadLocal 변수를 정의합니다. // private ThreadLocal <bean> tl = new ThreadLocal <ean> () {private com.lavasoft.test2.threadlocal <bean> tl = new com.lavasoft.test2.threadlocal < @bean (threadlocal) () InitialValue () {return new bean (); }}; @override public String toString () {return "MythreadLocal {" + "tl =" + tl + '}'; } public bean getBean () {return tl.get (); }} class ThreadLocal <t> {private map <Thread, t> map = collections.synchronizedMap (new Hashmap <Thread, t> ()); public ThreadLocal () {} protected t initialValue () {return null; } public t get () {Thread T = Thread.CurrentThread (); t obj = map.get (t); if (obj == null &&! map.containskey (t)) {obj = initialValue (); map.put (t, obj); } return obj; } public void set (t value) {map.put (Thread.currentThread (), value); } public void remove () {map.remove (Thread.currentThread ()); }} 패키지 com.lavasoft.test2; / ** * 테스트 Bean */ public class bean {private String id = "0"; 개인 문자열 이름 = "none"; public bean () {} public bean (문자열 id, 문자열 이름) {this.id = id; this.name = 이름; } public String getId () {return id; } public void setId (String id) {this.id = id; } public String getName () {return name; } public void setName (문자열 이름) {this.name = 이름; } public String showInfo () {return "bean {" + "id = '" + id +'/'' + ", name = '" + name +'/'' + '}'; }} 패키지 com.lavasoft.test2; / *** 테스트 스레드*/ public class testthread는 스레드 {private mythreadlocal tlt = new MythreadLocal (); public testthread (mythreadlocal tlt) {this.tlt = tlt; } @override public void run () {System.out.println ( ">>>> :" + tlt); for (int i = 0; i <3; i ++) {system.out.println (thread.currentthread (). getName ()+"/t"+tlt.getBean ()+"/t"+tlt.getBean (). showInfo (); }}}
그런 다음 테스트를 실행하십시오.
>>>>> : mythreadlocal {tl=com.lavasoft.test2.mythreadlocal$1@1de3f2d} >>>>> : mythreadlocal.lavasoft.test2.mythreadlocal$1@1de3f2d} >>>>> : mythreadlocal (tll=com.lavasoft.lavasoft.test2.bean@291aff bean {id = '0', name = 'none'} thread-2 com.lavasoft.test2.bean@fe64b9 bean {id = '0', name = 'none' '} stride-3 com.lavasoft.test2.bean @186db54 bean {rembean@186db54 name = 'none' '} strook-2 com.lavasoft.test2.bean@fe64b9 bean {id ='0 ', name ='none '} thread-2 com.lavasoft.test2.bean@fe64b9 bean {id ='0 ', name ='red com.lavasoft.test2.bean@291aff bean {id = '0', name = 'none'} strook-3 com.lavasoft.test2.bean@186db54 bean {id = '0', name '} strook -3 com.lavasoft.test2.bean@186db54 bean {id ='0 '} 1'} 1 com.lavasoft.test2.bean@291aff bean {id = '0', name = 'none'} strook-0 com.lavasoft.test2.bean@291aff bean {id = '0', name = 'none'} 스레드 -0 com.lavasoft.test2.bean@291aff bean {id = '0' '}}}}}}} com.lavasoft.test2.bean@291aff bean {id = '0', name = 'none'} Exit 코드 0으로 완료되었습니다
인쇄 결과에서 MythreadLocal의 TLT 객체는 실제로 하나이며 TLT 객체의 Thress 객체도 하나라는 것이 분명합니다. 그러나 각 스레드에 T1T가 사용되면 스레드는 Bean 객체를 재현하여 사용하기 위해 ThreadLocal 맵에 추가합니다.
ThreadLocal에 대한 몇 가지 오해 :
1. ThreadLocal은 Java 스레드를 구현 한 것입니다
ThreadLocal은 실제로 Java 스레드와 관련이 있지만 Java 스레드의 구현은 아니며 로컬 변수를 유지하는 데 사용됩니다. 각 스레드의 경우 주로 스레드 충돌을 피하기 위해 자체 변수 버전을 제공하며 각 스레드는 자체 버전을 유지합니다. 서로 독립적 이어지면 수정은 서로 영향을 미치지 않습니다.
2. ThreadLocal은 각 세션과 관련이 있습니다
이름에서 알 수 있듯이 ThreadLocal은 스레드를 목표로합니다. Java 웹 프로그래밍에서 각 사용자는 세션의 시작부터 세션이 끝까지 자체 세션 식별자가 있습니다. 그러나 ThreadLocal은 세션 계층에 없습니다. 실제로 ThreadLocal은 사용자 세션과 무관합니다. 서버 측 동작입니다. 서버가 새 스레드를 생성 할 때마다 자체 ThreadLocal을 유지합니다.
이러한 오해와 관련하여, 나는 개인적으로 그것이 일부 애플리케이션 서버를 기반으로 개발자의 로컬 테스트의 결과라고 생각합니다. 우리 모두 알다시피, 일반 응용 프로그램 서버는 스레드 풀 세트, 즉 각 액세스마다 새 스레드가 반드시 생성되는 것은 아닙니다. 대신 스레드 캐시 풀이 있습니다. 액세스하려면 먼저 캐시 풀에서 기존 스레드를 찾으십시오. 그들이 모든 것을 사용했다면, 새로운 스레드가 생성됩니다.
따라서 개발자는 일반적으로 자신을 테스트하는 유일한 사람이므로 서버 부담은 매우 작아서 액세스가있을 때마다 동일한 스레드를 공유하여 오해가 발생합니다. 각 세션에는 스레드가 있습니다.
3. ThreadLocal은 각 스레드와 관련이 있습니다. 사용자가 액세스 할 때마다 새로운 ThreadLocal이 있습니다.
이론적으로, ThreadLocal은 실제로 각 스레드와 관련이 있으며, 각 스레드는 자체 ThreadLocal을 갖습니다. 그러나 위에서 언급했듯이 일반 응용 프로그램 서버는 스레드 풀 세트를 유지합니다. 따라서 다른 사용자가 동일한 스레드를받을 수 있습니다. 따라서 Headlocal 기반을 수행 할 때는 Threadlocal 변수의 캐시를 피하기 위해주의해야하므로 다른 스레드가 스레드 변수에 액세스 할 수 있습니다.
4. 각 사용자 액세스마다 스레드 로컬을 여러 번 사용할 수 있습니다.
Threadlocal은 양날의 검이며 사용하면 좋은 결과를 얻을 수 있다고 말할 수 있습니다. 그러나 ThreadLocal이 잘 사용되지 않으면 글로벌 변수와 동일합니다. 코드를 재사용 할 수 없으며 독립적으로 테스트 할 수 없습니다. 재사용 할 수있는 일부 클래스는 이제 ThreadLocal 변수에 의존하기 때문입니다. ThreadLocal이 없으면 이러한 클래스를 사용할 수 없습니다. 나는 개인적으로 Threadlocal이 잘 사용되고 언급 할 가치가 있다고 생각합니다.
1. 현재 세션 사용자를 저장하십시오. Quake Want Jert
2. Webwork의 ActionContext와 같은 일부 컨텍스트 변수를 저장하십시오
3. Spring Hibernate ORM 세션과 같은 상점 세션