문제

스레드-로컬 데이터베이스 연결을 사용하는 경우 스레드가 존재할 때 연결을 닫아야 합니다.

호출 스레드의 run() 메서드를 재정의할 수 있는 경우에만 이 작업을 수행할 수 있습니다.심지어 그것은 훌륭한 해결책이 아닙니다. 종료할 때 해당 스레드에 의해 연결이 열린 적이 있는지 알 수 없기 때문입니다.

문제는 실제로 더 일반적입니다.스레드가 종료될 때 스레드 로컬 개체의 일부 종료 메서드를 호출하도록 강제하는 방법입니다.

Java 1.5의 소스를 살펴본 결과 스레드 로컬 맵이 null로 설정되어 결국 가비지 수집이 호출되는 것을 발견했습니다. 마무리(), 하지만 가비지 수집기에 의존하고 싶지 않습니다.

데이터베이스 연결이 닫혀 있는지 확인하려면 다음 재정의가 불가피한 것 같습니다.

@Override 
public void remove() {
    get().release(); 
    super.remove(); 
}

어디 풀어 주다() 데이터베이스 연결이 열려 있으면 닫습니다.그러나 스레드가 이 스레드 로컬을 사용한 적이 있는지는 알 수 없습니다.이 스레드에서 get()을 호출한 적이 없다면 여기에서는 상당한 노력이 낭비됩니다. ThreadLocal.초기값() 이 스레드에 지도가 생성됩니다.

-

Thorbjørn의 의견에 따른 추가 설명 및 예:

java.lang.ThreadLocal 스레드에 바인딩된 개체에 대한 팩토리 유형입니다.이 유형에는 객체에 대한 getter와 팩토리 메소드(일반적으로 사용자가 작성함)가 있습니다.getter가 호출되면 이 스레드에서 이전에 호출한 적이 없는 경우에만 팩토리 메서드를 호출합니다.

사용 스레드로컬 스레드 코드가 제3자가 작성한 경우에도 개발자는 리소스를 스레드에 바인딩할 수 있습니다.

예:다음과 같은 리소스 유형이 있다고 가정해 보겠습니다. 내 취향 그리고 우리는 스레드당 하나만 갖고 싶습니다.

사용 클래스에서 정의합니다.Private STATIC THREADLOCAL RESOCECECECECACTORY = NEW THREADLOCAL () {@OVERRIDE PROTECTED MYTYPE InitialValue () {return New MyType ();}}

이 클래스의 로컬 컨텍스트에서 사용하십시오.public void somemethod () {mytype resource = resourcefactory.get ();리소스.useResource();}

얻다() 부를 수있다 초기 값() 호출 스레드의 수명주기에서 한 번만.그 시점에서 내 취향 인스턴스화되어 이 스레드에 바인딩됩니다.후속 호출 얻다() 이 스레드에서 이 개체를 다시 참조합니다.

고전적인 사용 예는 다음과 같습니다. 내 취향 스레드가 안전하지 않은 텍스트/날짜/xml 포맷터입니다.

그러나 이러한 포맷터는 일반적으로 해제되거나 닫힐 필요가 없으며 데이터베이스 연결은 수행되며 저는 java.lang.ThreadLocal 스레드당 하나의 데이터베이스 연결을 갖습니다.

내가보기 엔, java.lang.ThreadLocal 거의 완벽합니다.호출 스레드가 제3자 애플리케이션에 속하는 경우 리소스 폐쇄를 보장할 방법이 거의 없기 때문입니다.

나는 당신의 두뇌가 필요합니다 종자:연장하여 java.lang.ThreadLocal 수정하거나 재정의할 수 없는 스레드를 포함하여 모든 스레드에 대해 하나의 데이터베이스 연결을 바인딩했습니다. 이는 독점적인 사용을 위한 것입니다.잡히지 않은 예외로 인해 스레드가 종료되는 경우 연결이 닫히도록 관리했습니다.

정상적인 스레드 종료의 경우 가비지 수집기는 연결을 닫습니다(왜냐하면 내 취향 재정의 마무리()).실제로는 매우 빠르게 발생하지만 이는 이상적이지 않습니다.

내 뜻대로 했다면 다른 방법이 있었을 텐데. java.lang.ThreadLocal:

protected void release() throws Throwable {}

이 방법이 존재한다면 java.lang.ThreadLocal, 스레드 종료/죽음 시 JVM에 의해 호출된 다음 이를 재정의하여 연결을 닫을 수 있습니다(그리고 구속자는 Zion에 왔을 것입니다).

그러한 방법이 없으면 종료를 확인하는 다른 방법을 찾고 있습니다.JVM 가비지 수집에 의존하지 않는 방식입니다.

감사해요

도움이 되었습니까?

해결책

당신이 민감한 처분이라면, 지금 멀리보십시오.

나는 이것이 확장 될 것으로 기대하지 않을 것이다. 시스템의 스레드 수를 효과적으로 두 배로 늘립니다. 허용 가능한 일부 사용 사례가있을 수 있습니다.

public class Estragon {
  public static class Vladimir {
    Vladimir() { System.out.println("Open"); }
    public void close() { System.out.println("Close");}
  }

  private static ThreadLocal<Vladimir> HOLDER = new ThreadLocal<Vladimir>() {
    @Override protected Vladimir initialValue() {
      return createResource();
    }
  };

  private static Vladimir createResource() {
    final Vladimir resource = new Vladimir();
    final Thread godot = Thread.currentThread();
    new Thread() {
      @Override public void run() {
        try {
          godot.join();
        } catch (InterruptedException e) {
          // thread dying; ignore
        } finally {
          resource.close();
        }
      }
    }.start();
    return resource;
  }

  public static Vladimir getResource() {
    return HOLDER.get();
  }
}

더 나은 오류 처리 등은 구현자를위한 연습으로 남겨집니다.

또한 스레드/리소스 추적을 볼 수도 있습니다. ConcurrentHashMap 다른 스레드 폴링으로 살아있다. 그러나 그 해결책은 절망적 인 사람들의 마지막 수단입니다. 물체는 아마도 너무 자주 확인되거나 거의 확인되지 않을 것입니다.

나는 계측과 관련이없는 다른 것을 생각할 수 없습니다. AOP가 작동 할 수 있습니다.

연결 풀링은 내가 선호하는 옵션입니다.

다른 팁

Runnable을 새로운 Runnable로 감싸십시오.

try {
  wrappedRunnable.run();
} finally {
  doMandatoryStuff();
}

생성하고 대신 실행되도록 하세요.

예를 들어 다음과 같은 메서드로 만들 수도 있습니다.

  Runnable closingRunnable(Runnable wrappedRunnable) {
    return new Runnable() {
      @Override
      public void run() {
        try {
          wrappedRunnable.run();
        } finally {
          doMandatoryStuff();
        }
      }
    };
  }

찾고 있는 실행 파일을 전달하는 해당 메서드를 호출하세요.

대신 Executor를 사용하는 것을 고려할 수도 있습니다.Runable 및 Callable을 관리하기가 훨씬 쉬워집니다.

ExecutorService를 사용하는 경우 다음과 같이 사용할 수 있습니다. executor.submit(closingRunnable(normalRunnable))

전체 ExecutorService를 종료할 것임을 알고 해당 시점에 연결을 닫으려면 "모든 작업이 완료되고 실행기에서 종료가 호출된 후" 닫기를 수행하는 스레드 팩토리를 설정할 수 있습니다. 예:

  ExecutorService autoClosingThreadPool(int numThreads) {
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(numThreads, numThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); // same as Executors.newFixedThreadPool
    threadPool.setThreadFactory(new ThreadFactory() {
      @Override
      public Thread newThread(Runnable r) {
        return new Thread(closingRunnable(r)); // closes it when executor is shutdown
      }
    });
    return threadPool;
  }

doMandatoryStuff가 연결이 이전에 열렸는지 여부를 알 수 있는지 여부와 관련하여 마음에 드는 한 가지는 연결이 열렸는지 여부만 추적하는 두 번째 ThreadLocal이 있다는 것입니다(예:연결이 열리면 AtomicInteger를 2로 설정하고 정리할 때 여전히 기본값(예: 1)에 있는지 확인하세요.

정상적인 JDBC 연습은 당신이 닫는다는 것입니다 Connection (그리고 Statement 그리고 ResultSet) 획득 한 것과 동일한 방법 블록에서.

코드 :

Connection connection = null;

try {
    connection = getConnectionSomehow();
    // Do stuff.
} finally {
    if (connection != null) {
        try {
            connection.close();
        } catch (SQLException e) {
            ignoreOrLogItButDontThrowIt(e);
        }
    }
}

이것을 염두에두고, 당신의 질문은 당신의 디자인에 뭔가 잘못되었다고 생각하게합니다. 가장 짧은 범위로 이러한 종류의 비싸고 외부 리소스를 획득하면 잠재적 인 자원 누출 및 충돌로부터 응용 프로그램을 절약 할 수 있습니다.

원래 의도가 연결 성능을 향상시키는 것이라면 연결 풀링. 예를 들어 사용할 수 있습니다 C3P0 이것을위한 API. 또는 웹 응용 프로그램 인 경우 AppServer의 내장 연결 풀링 시설을 사용하여 DataSource. 자세한 내용은 AppServer 특정 문서를 참조하십시오.

나는 당신이 왜 전통적인 연결 풀링을 사용하지 않는지 이해하지 못합니다. 그러나 나는 당신이 당신의 이유가 있다고 가정 할 것입니다.

당신은 얼마나 많은 자유가 있습니까? 일부 DI 프레임 워크는 객체 수명주기 및 스레드 스코프 변수 (모두 멋지게 프록시)를 지원합니다. 그들 중 하나를 사용할 수 있습니까? 나는 봄이 상자 밖에서이 모든 일을 할 것이라고 생각하는 반면, Guice는 수명주기와 스레드 범위를 처리하기 위해 제 3 자 Lib가 필요하다고 생각합니다.

다음으로 Threadlocal 변수 생성 또는 스레드 생성에 대해 얼마나 많은 제어가 있습니까? 나는 당신이 ThreadLocal에 대해 완전히 제어 할 수 있지만 스레드 생성에 대해서는 아무것도 제한되지 않는다고 생각합니까?

세척을 포함하여 run () 메소드를 확장하여 새로운 runnable 또는 스레드를 모니터링하기 위해 Aspect-Teended 프로그래밍을 사용하여 정리를 포함시킬 수 있습니까? 또한 스레드를 등록 할 수 있도록 threadLocal을 확장해야합니다.

연결을 한 번 열어야 했으므로 같은 장소에서 폐쇄를 처리해야합니다. 환경에 따라 스레드가 재사용 될 수 있으며 응용 프로그램이 종료되기 전에 스레드가 수집 될 것으로 예상 할 수 없습니다.

일반적으로 클래식 한 것 외에는 이에 대한 좋은 해결책이 없다고 생각합니다. 리소스를 얻는 코드는이를 폐쇄 할 책임이 있습니다.

특정 경우,이 학위로 스레드를 호출하는 경우 스레드 시작시 메소드와 관련하여 사용자 지정 매개 변수를 취하는 메소드 또는 일부 형태의 종속성 주입을 통해 전달할 수 있습니다. 그런 다음 연결을 공급하는 코드가 있으므로 제거하는 코드가 있습니다.

연결을 요구하지 않는 코드가 연결되지 않으므로 닫을 필요가 없지만 설계가 너무 멀리있는 것처럼 들리기 때문에 주석을 기반으로 한 종속성 주입이 여기에서 작동 할 수 있습니다.

서브 클래스에 목록 속성을 설정하도록 threaedlocal의 get () 메소드를 무시하십시오. 이 속성을 쉽게 쿼리하여 특정 스레드에 대해 get () 메소드가 호출되었는지 확인할 수 있습니다. 그런 다음 ThreadLocal에 액세스 하여이 경우 청소할 수 있습니다.

의견에 대한 응답으로 업데이트되었습니다

우리가 한 일은

@Override
public void run() {
  try {
    // ...
  } finally {
    resource.close();
  }
}

기본적으로 항상 (아마도 열린 다음) 실을 통해 모든 경로에 대해 닫습니다. 누군가를 도와 줄 경우 :)

같은 문제를보고 있습니다. 지금까지 최종화 ()를 사용해야하지만 이제는 더 이상 사용되지 않습니다. 작업이 일부 집행 인에게 제출되므로 실행자가 귀하에게 표시하지 않는 한 실이 정확히 종료되는지 알 수 없습니다.

예를 들어, ThreadPooleExecutor를 확장하여 Executor를 구축하면 afterExecution () 및 Terminated () 메소드를 재정의하여이를 달성 할 수 있습니다. 전자는 비정상적으로 빠져 나가는 실을 사용하는 반면 후자는 일반적으로입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top