동기화 된 키워드
Java 언어 키워드를 사용하여 메소드 또는 코드 블록을 수정하면 최대 한 스레드가 동시에 코드를 실행하도록 할 수 있습니다.
두 개의 동시 스레드가 동일한 개체 객체 에서이 동기화 된 (이) 동기화 된 코드 블록에 액세스하면 한 번 이내에 하나의 스레드 만 실행할 수 있습니다. 다른 스레드는 코드 블록을 실행하기 전에 현재 스레드 가이 코드 블록을 실행할 때까지 기다려야합니다.
그러나 하나의 스레드가 객체의 하나의 동기화 된 (이) 동기화 코드 블록에 액세스하면 다른 스레드는 해당 객체의 비 동기화 된 (이) 동기화 코드 블록에 액세스 할 수 있습니다.
스레드가 객체의 동기화 된 (이) 동기화 코드 블록에 액세스 할 때 다른 스레드는 객체의 다른 모든 동기화 된 (이) 동기화 코드 블록에 대한 액세스를 차단하는 것이 중요합니다.
세 번째 예제는 다른 동기 코드 블록에도 적용됩니다. 즉, 스레드가 객체의 동기화 된 (이) 동기화 코드 블록에 액세스하면이 객체의 객체 잠금을 얻습니다. 결과적으로, 다른 스레드는 객체 객체의 모든 동기 코드 부분에 대한 액세스가 일시적으로 차단됩니다.
위의 규칙은 다른 객체 잠금에도 적용됩니다.
코드 예제
패키지 test160118; public class testsynchronized {public static void main (String [] args) {sy sy = new sy (0); Sy Sy2 = 새로운 Sy (1); sy.start (); sy2.start (); }} class sy는 스레드 {private int flag; 정적 객체 x1 = new Object (); 정적 객체 x2 = new Object (); public sy (int flag) {this.flag = flag; } @override public void run () {System.out.println (플래그); try {if (flag == 0) {synchronized (x1) {system.out.println (flag+"locked x1"); Thread.sleep (1000); 동기화 (x2) {System.out.println (flag+"locked x2"); } system.out.println (플래그+"릴리스 X1 및 X2"); }} if (flag == 1) {synchronized (x2) {system.out.println (flag+"locked x2"); Thread.sleep (1000); 동기화 (x1) {System.out.println (Flag+"잠금 X1"); } system.out.println (플래그+"릴리스 X1 및 X2"); }}} catch (InterruptedException e) {e.printstacktrace (); }}}
스레드 Local Lock-Free 스레드를 둘러싸는 구현 원리
Threadlocal은 무엇을 할 수 있습니까?
이 문장은 말하기 어렵습니다. 실제 프로젝트에서 발생하는 어려움 중 일부를 살펴 보겠습니다. 프로젝트의 일부 매개 변수에 따라 일부 메소드를 호출 한 다음 메소드가 메소드를 호출 한 다음 객체를 가로 지르는 메소드를 호출하면 이러한 메소드가 유사한 매개 변수 A, B 및 C를 사용할 수 있습니다. 예를 들어 A는 A Calleters B 및 C가 필요합니다. B 호출 C 메소드 A 및 B가 필요합니다. 현재 모든 매개 변수는 B 등으로 전달되어야합니다. 많은 방법이 있다면 매개 변수가 점점 더 많아집니다. 또한 프로그램이 매개 변수를 추가 해야하는 경우 관련 메소드에 매개 변수를 하나씩 추가해야합니다. 예, 매우 번거 롭습니다. 나는 당신이 그것을 만났다고 생각합니다. 이것은 또한 C 언어로 객체 지향을위한 몇 가지 일반적인 처리 방법입니다. 그러나 간단한 처리 방법은 객체로 래핑하여 전달하는 것입니다.이 문제는 객체의 속성을 추가하여 해결할 수 있습니다. 그러나 객체는 일반적으로 의미가 있으므로 때로는 간단한 객체 포장은 클래스 정의를 매우 이상하게 만들기 위해 몇 가지 연장 된 관련이없는 속성을 추가하여 이와 같은 복잡한 프로그램을 구성 할 때 범위와 유사한 스코프를 사용하여 처리합니다. 이름과 용도가 더 일반적입니다. 웹 응용 프로그램과 마찬가지로 컨텍스트, 세션, 요청 및 페이지 레벨에는 스코프가 있습니다. ThreadLocal은 이러한 유형의 문제를 해결할 수 있지만 이러한 유형의 문제를 해결하는 것은 그다지 적합하지 않습니다. 이러한 문제에 직면 할 때는 일반적으로 범위와 객체의 초기 단계에서 전달되지 않으며 매개 변수는 추가되지 않을 것이라고 생각합니다. 매개 변수를 추가 할 때 변경할 장소가 많이 있음을 알았습니다. 코드의 구조를 파괴하지 않으려면 매개 변수가 너무 많아서 메소드 코드의 가독성을 줄였습니다. ThreadLocal이 추가되어 처리됩니다. 예를 들어, 하나의 메소드가 다른 메소드를 호출하면 8 개의 매개 변수가 전달되고 매개 변수 중 하나가 NTH 메소드 레이어를 레이어별로 호출하여 전달됩니다. 현재 마지막 메소드는 하나의 매개 변수를 추가해야합니다. 첫 번째 방법이 9 개의 매개 변수가되는 것은 당연하지만, 현재로서는 관련 방법이 연루되어 코드가 부풀어 오릅니다.
위에서 언급 한 ThreadLocal은 양을 잃는 문제를 수리하는 목적이지만 특히 사용하는 권장 방법은 아닙니다. 또한 사용하는 것과 유사한 방법이 있습니다. 즉, 프레임 워크 수준에는 많은 동적 호출이 있으며, 호출 프로세스 중에는 일부 프로토콜을 충족해야합니다. 우리는 보편적이 되려고 노력하지만 프로토콜을 정의 할 때 많은 확장 된 매개 변수를 고려하기가 쉽지 않으며 버전은 언제든지 업그레이드됩니다. 그러나 프레임 워크가 확장되면 인터페이스도 보편적이고 뒤로 호환됩니다. 편리하고 단순하려면 확장 내용이 필요합니다.
간단히 말해서, ThreadLocal은 일부 복잡한 시스템 확장을 간단한 정의로 바꾸어 관련 매개 변수와 관련된 부분을 매우 쉽게 만듭니다. 다음은 다음과 같습니다.
Spring의 Transaction Manager에서 데이터 소스에서 얻은 연결은 ThreadLocal에 배치됩니다. 프로그램이 실행 된 후, 연결은 ThreadLocal에서 얻은 다음 커밋 및 롤백이 수행됩니다. 사용 중에는 DataSource를 통해 프로그램에서 얻은 연결이 스프링부터 얻어 지도록해야합니다. 왜 그런 작동이 있습니까? 비즈니스 코드는 응용 프로그램에 의해 완전히 결정되고 프레임 워크는 비즈니스 코드를 작성해야 할 수 없기 때문에 프레임 워크는 비즈니스 코드가 연결을 관리 할 수 없다는 이점을 잃게됩니다. 비즈니스 코드가 차단되면 스프링은 비즈니스 코드 영역에 연결되지 않습니다. 장소에 저장되어야합니다. 기본 층이 Ibatis와 Spring을 통과 할 때. JDBC와 같은 프레임 워크가 동일한 데이터 소스의 연결을 얻을 때 봄에 합의 된 규칙에 따라이를 얻기 위해 호출합니다. 실행 프로세스는 동일한 스레드에서 처리되므로 동일한 연결이 얻어지면서 사용되는 연결이 커밋, 롤백 및 비즈니스 운영 중에 동일하도록 동일한 연결을 얻을 수 있습니다. 동일한 커넥톤 만 트랜잭션을 보장 할 수 있으므로 데이터베이스 자체가 지원되지 않기 때문입니다.
실제로, 많은 동시 프로그래밍 응용 프로그램에서 Threadlocal은 매우 중요한 역할을합니다. 로컬 변수와 같은 매번 재 할당 공간이 필요하지 않고 잠금 장치를 추가하지 않고 쉽게 스레드를 원활하게 둘러싸고 있습니다. 많은 공간이 스레드 안전이기 때문에 스레드-프라이버시 버퍼를 반복적으로 활용할 수 있습니다.
ThreadLocal을 사용하는 방법?
시스템의 적절한 위치에서 ThreadLocal 변수를 정의하는데,이 위치는 공개 정적 유형으로 정의 될 수 있습니다 (ThreadLocal 객체는 직접 새). 데이터를 넣으려면 세트 (Object)를 사용하고 get () 작업을 사용하고 요소를 삭제할 때 remove ()를 사용하십시오. 다른 방법은 비공개 방법이며 권장되지 않습니다.
다음은 간단한 예입니다 (코드 스 니펫 1).
공개 클래스 ThreadLocalTest2 {public final static strandlocal <string> test_thread_name_local = new ThreadLocal <string> (); 공개 최종 정적 ThreadLocal <string> test_thread_value_local = new ThreadLocal <string> (); public static void main (string [] args) {for (int i = 0; i <100; i ++) {final String name = "thread- [" + i + "]"; 최종 문자열 값 = string.valueof (i); 새 스레드 () {public void run () {try {test_thread_name_local.set (name); test_thread_value_local.set (value); calla (); } 마침내 {test_thread_name_local.remove (); test_thread_value_local.remove (); } } } }.시작(); }} public static void calla () {callb (); } public static void callb () {new ThreadLocalTest2 (). callc (); } public void callc () {calld (); } public void calld () {system.out.println (test_thread_name_local.get () + "/t =/t" + test_thread_value_local.get ()); }}여기서 우리는 각각 이름과 값에 액세스하기 위해 100 개의 스레드를 시뮬레이션합니다. 이름과 값의 값은 동시성 문제가 있는지 확인하기 위해 중간에 의도적으로 동일하게 설정됩니다. 출력을 통해 스레드 출력이 순서대로 출력되지 않음을 알 수 있습니다. 즉, 이는 병렬로 실행되며 스레드 이름과 값이 해당 할 수 있습니다. 중간에서 여러 방법을 통해 실제 호출에서 매개 변수가 전달되지 않으므로 해당 변수를 얻는 방법. 그러나 실제 시스템에서는 종종 교차 계급입니다. 여기에서는 한 클래스에서만 시뮬레이션됩니다. 실제로, 크로스 클래스는 동일한 결과입니다. 직접 시뮬레이션 할 수 있습니다.
나는 이것을 본 후에, 많은 프로그래머들이 ThreadLocal의 원리에 매우 관심이 있다고 생각합니다. 그것이 어떻게 끝났는지 봅시다. 매개 변수는 전달되지 않지만 로컬 변수처럼 사용할 수 있습니다. 그것은 참으로 마법입니다. 실제로, 당신은 그것이 설정 방법이라고 말할 수 있습니다. 이름이 스레드와 관련되어야한다는 것을 알면 덜 말도 안되는 말을하고 소스 코드를 살펴 보겠습니다. 가장 세트를 사용하기 때문에 Get and Remove를 사용한 다음 세트로 시작합니다.
세트 (t obj) 메소드는 (코드 스 니펫 2) :
public void set (t value) {Thread T = Thread.CurrentThread (); ThreadLocalMap map = getMap (t); if (map! = null) map.set (this, value); else createmap (t, value);}먼저, 현재 스레드가 추측과 동일하게 얻은 다음 현재 스레드에서 전달되는 getMAP 메소드가 있습니다. 먼저이 맵이 스레드와 관련된지도라는 것을 먼저 이해할 수 있습니다. 다음으로 비어 있지 않으면 설정 작업을 수행하십시오. 당신이 그것을 따라 가면, 당신은 이것이 해시 맵의 PUT 작동과 유사하다는 것을 알게 될 것입니다. 즉, 데이터 조각이 맵에 기록됩니다. 비어 있으면 Createmap 메소드가 호출됩니다. 입력 후 살펴보십시오 (Code Snippet 3) :
void createmap (스레드 t, t FirstValue) {t.threadlocals = new ThreadLocalMap (this, firstValue);}Cashback은 ThreadLocalMap을 생성하고 전달 된 매개 변수와 현재 ThreadLocal을 KV 구조 (Code Snippet 4)로 씁니다.
ThreadLocalMap (ThreadLocal FirstKey, Object FirstValue) {table = new entry [initial_capacity]; int i = firdkey.threadlocalhashcode & (이니셜_capacity -1); 표 [i] = 새 항목 (FirstKey, FirstValue); 크기 = 1; setthreshold (initial_capacity);}이것은 여기서 ThreadLocalMap의 구조적 세부 사항을 설명하지 않습니다. 구현이 해시 맵과 유사하다는 것을 알아야합니다. 구현 맵이없는 방법에는 여러 가지가 있습니다. 일부 방법 (예 : 반사)을 통해 맵을 얻기 위해 맵을 얻기를 원하지 않기 때문입니다. Threadlocal, 기본 유형의 정적 내부 클래스이며 Java.lang의 클래스 만 참조 할 수 있으므로 스레드를 참조 할 수 있다고 생각할 수 있습니다.
GetMap 메소드를 다시 살펴 보겠습니다. 획득 한 맵이 스레드와 관련이 있고 코드 스 니펫 3을 통해 t.threadlocalmap = new ThreadLocalMap (이, FirstValue)이 있다는 것을 알고 있기 때문에이 변수가 스레드에서 나오는 것을 이해해야한다고 생각합니다. GetMap 방법에 따라 들어가자 :
ThreadLocalMap getMap (스레드 t) {return t.threadlocals;}예, 스레드에서 나오고이 스레드는 현재 스레드가되므로 정의를 살펴보십시오.
ThreadLocal.threadLocalMap ThreadLocals = NULL;
이 속성은 스레드 클래스에 있습니다. 즉, 각 스레드에는 기본적으로 ThreadLocalMap이 있으며 스레드 레벨 로컬 변수를 저장하는 데 사용됩니다. 일반적으로 그러한 과제는 일반적으로 불안하기 때문에 값을 할당 할 수 없습니다.
조금 지저분한 것 같습니다. 걱정하지 마십시오. 뒤돌아보고 아이디어를 탐구하자.
1. 스레드에는 해시 맵과 비슷한 속성이 있지만 그 이름은 ThreadLocalMap입니다. 이 속성은 기본 유형이므로 동일한 패키지의 모든 클래스를 참조 할 수 있습니다. 스레드의 로컬 변수이기 때문에 각 스레드에는 자체 별도의 맵이있어 서로 충돌하지 않으므로 ThreadLocal이 정적 스레드로 정의 되더라도 충돌이 없습니다.
2. ThreadLocal 및 Thread는 동일한 패키지 아래에 있습니다. 이 클래스를 참조하고 작동 할 수 있습니다. 이 시점에서 각 ThreadLocal은 하나를 정의하고,이를 키로 사용하고, 값으로 전달하는 값을 값으로 사용하며, 이것은 당신이 정의하는 threadLocal입니다. 따라서 다른 ThreadLocal 변수는 세트를 사용하고 키가 다르기 때문에 서로의 데이터가 충돌하지 않습니다. 물론, 같은 ThreadLocal이 두 번의 설정 작업을 수행 한 후에는 마지막 시간이 우세 할 것입니다.
3. 요약하면, 스레드가 평행 할 때, 스레드 로컬은 로컬 변수처럼 사용될 수 있고 스레드-안전이며, 다른 스레드 로컬 변수 간의 데이터는 충돌이 없습니다.
Get 메소드를 계속보고 메소드를 제거하겠습니다. 실제로 간단합니다.
public t get () {Thread T = Thread.CurrentThread (); ThreadLocalMap map = getMap (t); if (map! = null) {ThreadLocalMap.entry e = map.getEntry (this); if (e! = null) return (t) e.Value; } return setInitialValue ();}현재 스레드에 따라 GetMap 메소드를 호출함으로써, 즉 T.threadlocalMap이 호출 된 다음 맵에서 검색되면, 맵은 항목과 함께 찾을 수 있습니다. 이를 통해 Map.getEntry도 얻을 수 있습니다.
동일한 제거 방법은 다음과 같습니다.
public void remove () {ThreadLocalMap m = getMap (thread.currentThread ()); if (m! = null) m.remove (this);}또한 맵은 현재 스레드를 기반으로 얻습니다. 비어 있지 않으면 제거하고 제거하십시오.
또한 (2013-6-29), 글을 잊어 버리는 함정은 무엇입니까? 이 ThreadLocal에는 어떤 함정이 있습니까? 앞의 예에서 ThreadLocal 관련 객체가 맵에 바인딩 되고이 맵은 스레드 스레드의 속성임을 알 수 있습니다. 그런 다음 자신을 제거하지 않거나 자신의 프로그램에서 언제 제거 해야하는지 모르는 경우 스레드가 로그 아웃되지 않고 데이터 세트가 로그 아웃되지 않는다는 문제가 있습니다.
반면에,이 객체를 설정 해야하는 위치와 제거 장소를 명확하게 이해하지 않는 한. 모호한 경우 코드가 제거 위치로 이동하지 않거나 논리적 인 문제를 일으킬 가능성이 높습니다. 또한 제거되지 않으면 스레드가 로그 아웃 될 때까지 기다려야합니다. 많은 응용 프로그램 서버에서 커널 할당 스레드에 오버 헤드가 여전히 있기 때문에 스레드가 재사용되므로 이러한 응용 프로그램에서 스레드를 로그 아웃하기가 어렵습니다. 그런 다음 ThreadLocal에 작성된 데이터는 당연히 로그 아웃하기 쉽지 않습니다. 이들은 우연히 숨겨져서 일부 오픈 소스 프레임 워크를 사용할 때 사용될 수 있으며 문제가 발생할 수 있습니다. 마지막으로, OOM 일 때 데이터는 실제로 ThreadLocalMap에서 나옵니다. 이 데이터가 어디에 설정되어 있는지 알지 못 하므로이 구덩이에주의를 기울여야하며 한 명 이상 이이 구덩이에 빠졌습니다.