source

Java ThreadLocal 변수가 정적이어야 하는 이유

gigabyte 2022. 9. 25. 17:35
반응형

Java ThreadLocal 변수가 정적이어야 하는 이유

여기서 스레드 로컬용 JavaDoc을 읽고 있었습니다.

https://docs.oracle.com/javase/1.5.0/docs/api/java/lang/ThreadLocal.html

"ThreadLocal 인스턴스는 일반적으로 스레드(사용자 ID 또는 트랜잭션 ID 등)와 상태를 관련짓는 클래스의 개인 정적 필드입니다."

그런데 왜 (일반적으로) 정적인 상태를 선택했을까요? "스레드 단위" 상태를 갖는 것은 좀 혼란스럽지만 필드는 정적인가요?

인스턴스 수준 필드인 경우 실제로는 "스레드 단위"가 아니라 "스레드 단위 - 인스턴스 단위"가 됩니다.그건 보통 네가 찾는 의미가 아니야.

일반적으로 사용자 컨버세이션, 웹 요청 등으로 범위가 지정되는 개체와 같은 것을 보유합니다.클래스 인스턴스로 하위 범위를 지정하지 않는 것이 좋습니다.
1은 1은 => 1은 1은 1은 1입니다.
개체당 하나의 웹 요청 => 하나의 지속성 세션이 아닙니다.

클래스에서 스태틱하게 하거나 스태틱필드를 사용하지 않도록 합니다.클래스 자체를 싱글톤으로 하고 그 싱글톤을 글로벌하게 사용할 수 있는 한 인스턴스 수준 스레드 로컬을 안전하게 사용할 수 있습니다.

그럴 필요 없을 텐데.중요한 것은 싱글톤이어야 한다는 것이다.

그 이유는 스레드와 연관된 포인터를 통해 변수에 액세스하기 때문입니다.이들은 스레드 범위를 가진 글로벌 변수처럼 작동하므로 정적 변수가 가장 적합합니다.이는 pthreads 등의 스레드 로컬 상태를 취득하는 방법이며, 이는 단순히 역사와 구현의 사고일 수 있습니다.

이것을 참고하면 더 잘 이해할 수 있습니다.

로 말하면, 요,,,ThreadLocal을 사용하다가 " " 를 호출할 때ThreadLocal get/setmethod는 스레드 오브젝트를 맵의 키에 검색/저장하고 맵의 값에 값을 저장합니다.따라서 스레드마다 값 복사(로컬에 저장하려는 값 복사)가 다릅니다. 왜냐하면 스레드는 맵의 엔트리에 존재하기 때문입니다.

그렇기 때문에 모든 값을 유지하기 위해 하나의 지도만 필요합니다.반드시 필요한 것은 아니지만 (스태틱을 선언하지 않고) 여러 개의 맵을 사용하여 각 스레드개체를 유지할 수 있습니다.이는 완전히 장황하기 때문에 스태틱 변수가 선호됩니다.

스레드별 인스턴스별 스레드 로컬의 용도는 객체의 모든 메서드에서 어떤 것이 표시되도록 하고 일반 필드에서와 같이 액세스를 동기화하지 않고 스레드 안전을 유지하는 경우입니다.

static final ThreadLocal변수는 스레드 세이프입니다.

static는 각 스레드에 대해서만 여러 클래스에 걸쳐 ThreadLocal 변수를 사용할 수 있도록 합니다.여러 클래스에 걸쳐 각 스레드 로컬 변수의 글로벌 변수 디카리테이션입니다.

이 스레드의 안전성은 다음 코드 샘플로 확인할 수 있습니다.

  • CurrentUser- 현재 사용자 ID를 ThreadLocal에 저장합니다.
  • TestService- 간단한 방법으로 서비스 -getUser()CurrentUser에서 현재 사용자를 가져옵니다.
  • TestThread- 여러 스레드를 만들고 동시에 사용자 ID를 설정하는 데 사용되는 클래스입니다.

.

public class CurrentUser

public class CurrentUser {
private static final ThreadLocal<String> CURRENT = new ThreadLocal<String>();

public static ThreadLocal<String> getCurrent() {
    return CURRENT;
}

public static void setCurrent(String user) {
    CURRENT.set(user);
}

}

public class TestService {

public String getUser() {
    return CurrentUser.getCurrent().get();
}

}

.

import java.util.ArrayList;
import java.util.List;

public class TestThread {

public static void main(String[] args) {

  List<Integer> integerList = new ArrayList<>();

  //creates a List of 100 integers
  for (int i = 0; i < 100; i++) {

    integerList.add(i);
  }

  //parallel stream to test concurrent thread execution
  integerList.parallelStream().forEach(intValue -> {

    //All concurrent thread will set the user as "intValue"
    CurrentUser.setCurrent("" + intValue);
    //Thread creates a sample instance for TestService class
    TestService testService = new TestService();
    //Print the respective thread name along with "intValue" value and current user. 
    System.out.println("Start-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());

    try {
      //all concurrent thread will wait for 3 seconds
      Thread.sleep(3000l);
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    //Print the respective thread name along with "intValue" value and current user.
    System.out.println("End-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());
  });

}

}

.

Test Thread 메인클래스를 실행합니다.출력 -

Start-main->62->62
Start-ForkJoinPool.commonPool-worker-2->31->31
Start-ForkJoinPool.commonPool-worker-3->81->81
Start-ForkJoinPool.commonPool-worker-1->87->87
End-main->62->62
End-ForkJoinPool.commonPool-worker-1->87->87
End-ForkJoinPool.commonPool-worker-2->31->31
End-ForkJoinPool.commonPool-worker-3->81->81
Start-ForkJoinPool.commonPool-worker-2->32->32
Start-ForkJoinPool.commonPool-worker-3->82->82
Start-ForkJoinPool.commonPool-worker-1->88->88
Start-main->63->63
End-ForkJoinPool.commonPool-worker-1->88->88
End-main->63->63
...

분석 요약

  1. "주요" 실을 시작하고"62"로 현재 사용자 설정한다면 parallely"ForkJoinPool.commonPool-worker-2" 실을 시작하고"31일"로 현재 사용자 설정한다면 parallely"ForkJoinPool.commonPool-worker-3" 실을 시작하고"81"로 현재 사용자 설정한다면 parallely"ForkJoinPool.commonPool-worker-1" 실을 시작하고"87"Start-main-&gt로 현재 사용자 설정;62->, 62Start-Fo.rkJoinPool.common Pool-worker-2->31->31 Start-Fork JoinPool.commonPool-3->81 Start-Fork JoinPool.common-Worker-1->87
  2. 위의 모든 스레드는 3초간 sleep됩니다.
  3. main실행이 종료되고 현재 사용자가 병렬로 "62"로 인쇄됩니다.ForkJoinPool.commonPool-worker-1실행이 종료되고 현재 사용자를 "87"로 동시에 인쇄합니다.ForkJoinPool.commonPool-worker-2실행이 종료되고 현재 사용자가 병렬로 "31"로 인쇄됩니다.ForkJoinPool.commonPool-worker-3실행이 종료되고 현재 사용자가 "81"로 인쇄됩니다.

추론

동시 스레드는 "static final Thread Local"로 선언된 경우에도 올바른 사용자 ID를 가져올 수 있습니다.

언급URL : https://stackoverflow.com/questions/2784009/why-should-java-threadlocal-variables-be-static

반응형