Java ThreadLocal değişkenleri neden statik olmalı?


102

Burada Threadlocal için JavaDoc okuyordum

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

ve "ThreadLocal örnekleri, durumu bir iş parçacığı (örneğin, bir kullanıcı kimliği veya İşlem Kimliği) ile ilişkilendirmek isteyen sınıflarda genellikle özel statik alanlardır" diyor.

Ama sorum şu: Neden onu statik yapmayı seçtiler (tipik olarak) - "iş parçacığı başına" duruma sahip olmak işleri biraz kafa karıştırıcı hale getiriyor, ancak alanlar statik mi?

Yanıtlar:


132

Çünkü eğer bu bir örnek seviyesinde alan olsaydı, o zaman aslında "İş Parçacığı Başına - Örnek Başına" olurdu, sadece garantili bir "İş Parçacığı Başına" değil. Normalde aradığınız anlamsal bu değildir.

Genellikle, kapsamı bir Kullanıcı Konuşması, Web İsteği, vb. Gibi nesneler gibi bir şeyi tutar. Bunların aynı zamanda sınıfın örneğine göre alt kapsamda olmasını istemezsiniz.
Bir web isteği => bir Persistence oturumu.
Tek bir web isteği değil => nesne başına bir kalıcılık oturumu.


2
Bu açıklamayı beğendim çünkü ThreadLocal'ın nasıl kullanılacağını gösteriyor
kellyfj

4
Örnek başına iş parçacığı yararlı bir anlambilim olabilir, ancak bu örüntü için çoğu kullanım o kadar çok nesneyi içerir ki ThreadLocal, nesneleri iş parçacığı başına örneklerle eşleyen bir karma kümesine başvuruyu tutmak için a kullanmak daha iyi olacaktır .
supercat

@optional Bu, statik olmayan her bir örneğinin ThreadLocalkendi iş parçacığı yerel verilerini tutacağı anlamına gelir, bu örnekler aynı evrede var olsa bile ThreadLocal. Bunu yapmak ille de yanlış değil - sanırım bu ikisinin en az popüler modeli olabilir
geg

17

Ya statik hale getirin ya da sınıfınızdaki herhangi bir statik alandan kaçınmaya çalışıyorsanız - sınıfın kendisini bir tekil yapın ve ardından bu tekil tüm dünyada mevcut olduğu sürece bir örnek düzeyi ThreadLocal'ı güvenle kullanabilirsiniz.



3

Bunun nedeni, değişkenlere iş parçacığı ile ilişkili bir işaretçi aracılığıyla erişilmesidir. İş parçacığı kapsamına sahip global değişkenler gibi davranırlar, bu nedenle statik en yakın uyumdur. Bu, pthreads gibi şeylerde yerel durumu elde etmenin bir yoludur, bu nedenle bu sadece bir tarih ve uygulama kazası olabilir.


1

Evre başına örnek başına bir evre yerel için bir kullanım, bir nesnenin tüm yöntemlerinde bir şeyin görünür olmasını istiyorsanız ve sıradan bir alanda olduğu gibi ona erişimi senkronize etmeden güvenli bir şekilde iş parçacığına sahip olmanızdır.


1

Bakın bu , bu daha iyi anlaşılması verir.

Kısacası, ThreadLocalnesne bir anahtar-değer haritası gibi çalışır. İş parçacığı çağırdığındaThreadLocal get/set yöntemi , eşleme anahtarındaki iş parçacığı nesnesini ve eşlemenin değerindeki değeri alır / depolar. Bu nedenle, farklı iş parçacıklarının farklı değer kopyaları vardır (yerel olarak depolamak istediğiniz), çünkü farklı haritanın girişinde bulunur.

Bu yüzden tüm değerleri korumak için tek bir haritaya ihtiyacınız var. Gerekli olmasa da, her bir iş parçacığı nesnesini de tutmak için birden fazla eşlemeniz olabilir (statik bildirmeden), ki bu tamamen gereksizdir, bu yüzden statik değişken tercih edilir.


-1

static final ThreadLocal değişkenler iş parçacığı açısından güvenlidir.

staticThreadLocal değişkenini yalnızca ilgili iş parçacığı için birden çok sınıfta kullanılabilir hale getirir. bu, birden çok sınıfta ilgili iş parçacığı yerel değişkenlerinin bir tür Global değişken tanımlamasıdır.

Bu iş parçacığı güvenliğini aşağıdaki kod örneği ile kontrol edebiliriz.

  • CurrentUser - mevcut kullanıcı kimliğini ThreadLocal'da saklar
  • TestService- Yöntemle basit hizmet - getUser()mevcut kullanıcıyı CurrentUser'dan almak için.
  • TestThread - bu sınıf, birden çok iş parçacığı oluşturmak ve aynı anda kullanıcı kimliklerini ayarlamak için kullanılır

.

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());
  });

}

}

.

TestThread ana sınıfını çalıştırın. Çıktı -

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
...

Analiz özeti

  1. "ana" iş parçacığı başlar ve geçerli kullanıcıyı "62" olarak ayarlar, paralel olarak "ForkJoinPool.commonPool-işçi-2" iş parçacığı başlar ve mevcut kullanıcıyı "31" olarak ayarlar, paralel olarak "ForkJoinPool.commonPool-işçi-3" iş parçacığı başlar ve akımı ayarlar kullanıcı "81" olarak, paralel olarak "ForkJoinPool.commonPool-işçi-1" iş parçacığı başlar ve mevcut kullanıcıyı "87" Başlatma-ana-> 62-> 62 Start-ForkJoinPool.commonPool-işçi-2-> 31-> 31 olarak ayarlar Start-ForkJoinPool.commonPool-worker-3-> 81-> 81 Start-ForkJoinPool.commonPool-worker-1-> 87-> 87
  2. Yukarıdaki konuların tümü 3 saniye uyuyacak
  3. mainyürütme sona erer ve geçerli kullanıcıyı "62" olarak yazdırır, paralel olarak ForkJoinPool.commonPool-worker-1yürütme sona erer ve mevcut kullanıcıyı "87" olarak yazdırır, paralel olarak ForkJoinPool.commonPool-worker-2yürütme sona erer ve mevcut kullanıcıyı "31" olarak yazdırır, paralel olarak ForkJoinPool.commonPool-worker-3yürütme sona erer ve mevcut kullanıcıyı "81" olarak yazdırır

Çıkarım

Concurrent Threads, "statik final ThreadLocal" olarak bildirilmiş olsa bile doğru kullanıcı kimliklerini alabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.