ThreadLocal Java Fundamentos -

A .ThreadLocal definições de variáveis:
ThreadLocal Java, é um tipo especial de variável. Cada segmento tem um ThreadLocal que cada segmento tem sua própria variável independente, as condições de concorrência foi completamente eliminada. Se você fornecer uma cópia de suas próprias variáveis únicas para cada segmento, vai melhorar significativamente a eficiência. Primeiro, por multiplexação reduz o número de criação caro de objetos. Em segundo lugar, você não consegue usar um caso thread-safe de síncrono ou invariância caro.

Os dois .API explicou:
Essa classe fornece variáveis de segmentos locais (segmento local). Estas variáveis diferem de suas contrapartes normais, porque o acesso a uma variável (por onde começar ou método set) Cada segmento tem sua própria variável local, que é independente da cópia de inicialização da variável. exemplo ThreadLocal campos estáticos privados são tipicamente de classe, e eles desejam estado do segmento um (por exemplo, ID de usuário ou ID da transação) está associada.
Por exemplo, a seguinte classe gerar um identificador único local para cada segmento. O ID thread é atribuído quando a primeira chamada UniqueThreadIdGenerator.getCurrentThreadId (), não muda em chamadas subseqüentes.

  import java.util.concurrent.atomic.AtomicInteger;

 public class UniqueThreadIdGenerator {

     private static final AtomicInteger uniqueId = new AtomicInteger(0);

     private static final ThreadLocal < Integer > uniqueNum = 
         new ThreadLocal < Integer > () {
             @Override protected Integer initialValue() {
                 return uniqueId.getAndIncrement();
         }
     };
 
     public static int getCurrentThreadId() {
         return uniqueId.get();
     }
 } // UniqueThreadIdGenerator

Cada segmento tem mantido uma referência implícita à sua cópia de um variáveis de segmentos locais, desde que o fio está vivo e a instância ThreadLocal é acessível; depois rosca desaparecer, todas as cópias será a instância local de sua coleção de thread de lixo (a menos que a existência destes outras referências para copiar).
Aqui Insert Picture Descrição
III. Vamos olhar um exemplo através do uso de ThreadLocal

public class MyThreadLocalStringDemo {
    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    private String getString() {
        return threadLocal.get();
    }

    private void setString(String string) {
        threadLocal.set(string);
    }

    public static void main(String[] args) {
        int threads = 9;
        MyThreadLocalStringDemo demo = new MyThreadLocalStringDemo();
        CountDownLatch countDownLatch = new CountDownLatch(threads);
        for (int i = 0; i < threads; i++) {
            Thread thread = new Thread(() -> {
                demo.setString(Thread.currentThread().getName());
                System.out.println(demo.getString());
                countDownLatch.countDown();
            }, "thread - " + i);
            thread.start();
        }
    }
}

Os resultados são os seguintes:
Aqui Insert Picture Descrição
Pode haver algumas pessoas sentem em caso que pudermos para conseguir isso através do bloqueio. Sim, sim, de fato bloqueio pode resolver este problema, mas aqui enfatizamos que dados de segmento isolar o problema, não o problema de dados multi-threaded compartilhados. Se nós aqui, além de getString () do que existem muitos outros métodos também usaram este String, desta vez não há nenhum processo de transferência de dados explícita entre os vários métodos, e podem ser adquiridos diretamente na variável ThreadLocal, este é o ThreadLocal os dados de segmento núcleo que compartilham os mesmos dados de um isolamento segmento diferente. Desde genéricos de apoio ThreadLocal, aqui é armazenar uma String para demonstrar, de fato, pode armazenar qualquer tipo, o efeito é o mesmo.

Quatro .ThreadLocal análise de código fonte
antes de código-fonte análise entendemos uma coisa que é os mapeamentos objeto de instância e variáveis ThreadLocal por linha linhas para manter , em outras palavras, a variável ThreadLocal objeto de instância mapeamentos e é armazenado dentro de um Mapa ( mapear o Mapa não é um mapa resumo em java.util), e este mapa é um campo da classe thread ! Mas a verdadeira armazenamento mapeamentos Mapa é ThreadLocalMap . Aqui olhamos para a fonte específica é conseguido através de vários métodos de vista.

//set 方法	
public void set(T value) {	
    Thread t = Thread.currentThread();	
    ThreadLocalMap map = getMap(t);	
    if (map != null)	
        map.set(this, value);	
    else	
        createMap(t, value);	
}	
	
//获取线程中的ThreadLocalMap 字段!!	
ThreadLocalMap getMap(Thread t) {	
    return t.threadLocals;	
}	
	
//创建线程的变量	
void createMap(Thread t, T firstValue) {	
     t.threadLocals = new ThreadLocalMap(this, firstValue);	
}	

Primeiro, pegue método set no segmento atual, em seguida, passar o thread atual ThreadLocalMap getMap a uma variável do tipo threadLocals, se houver a atribuição direta, se o segmento não existe para criar ThreadLocalMap variável e atribuição. Quando a atribuição desta variável aqui é chamar a instância do objeto em si.


public T get() {	
    Thread t = Thread.currentThread();	
    ThreadLocalMap map = getMap(t);	
    if (map != null) {	
        ThreadLocalMap.Entry e = map.getEntry(this);	
        if (e != null) {	
            @SuppressWarnings("unchecked")	
            T result = (T)e.value;	
            return result;	
        }	
    }	
    return setInitialValue();	
}	
	
	
private T setInitialValue() {	
    T value = initialValue();	
    Thread t = Thread.currentThread();	
    ThreadLocalMap map = getMap(t);	
    if (map != null)	
        map.set(this, value);	
    else	
        createMap(t, value);	
    return value;	
}	

obter método é relativamente simples, também é o primeiro a obter ThreadLocalMap thread atual variável, se houver o valor de retorno, não existe, criar e retorna o valor inicial.

ThreadLocalMap utilizando a matriz de entrada [] para armazenar o relacionamento entre as instâncias de objectos com variáveis e objectos exemplo, como chave , variável como o valor para alcançar uma relação de correspondência. E a chave aqui é usado em uma referência fraca para a instância do objeto, (estamos aqui é porque a instância chave de objeto, cada instância objeto tem seu próprio ciclo de vida, usando referências fracas pode estar aqui sem afetar a instância do objeto no ciclo de vida referência a eles). Esta parte do código fonte não é lançado.

Cinco uso prático (a entrevista pode responder)
Cena: alta concorrência, SimpleDateFormat não é thread-safe, usamos ThreadLocal ao fio de segurança endereço.
Solução:
4, usando ThreadLocal: Cada segmento tem seu próprio objeto SimpleDateFormat. (Recomendado)

public class DateUtils {
    /**
     * 锁对象
     */
    private static final Object lockObj = new Object();
    /**
     * 存放不同的日期模板格式的sdf的Map
     */
    private static Map<String, ThreadLocal<SimpleDateFormat>> sdfMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();
    /**
     * 返回一个ThreadLocal的sdf,每个线程只会new一次sdf
     *
     * @param pattern
     * @return
     */
    private static SimpleDateFormat getSdf(final String pattern) {
        ThreadLocal<SimpleDateFormat> tl = sdfMap.get(pattern);

        // 此处的双重判断和同步是为了防止sdfMap这个单例被多次put重复的sdf
        if (tl == null) {
            synchronized (lockObj) {
                tl = sdfMap.get(pattern);
                if (tl == null) {
                    // 只有Map中还没有这个pattern的sdf才会生成新的sdf并放入map
                    System.out.println("put new sdf of pattern " + pattern + " to map");
                    // 这里是关键,使用ThreadLocal<SimpleDateFormat>替代原来直接new SimpleDateFormat
                    tl = new ThreadLocal<SimpleDateFormat>() {
                        @Override
                        protected SimpleDateFormat initialValue() {
                            System.out.println("thread: " + Thread.currentThread() + " init pattern: " + pattern);
                            return new SimpleDateFormat(pattern);
                        }
                    };
                    sdfMap.put(pattern, tl);
                }
            }
        }
        return tl.get();
    }
    /**
     * 使用ThreadLocal<SimpleDateFormat>来获取SimpleDateFormat,这样每个线程只会有一个SimpleDateFormat
     * 如果新的线程中没有SimpleDateFormat,才会new一个
     * @param date
     * @param pattern
     * @return
     */
    public static String format(Date date, String pattern) {
        return getSdf(pattern).format(date);
    }
    public static Date parse(String dateStr, String pattern) throws Exception {
        return getSdf(pattern).parse(dateStr);
    }
}

código de teste:

public class DateUtilTest {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                DateUtils.format(new Date(), "yyyy-MM-dd");
            };
        };
        Thread t2 = new Thread() {
            @Override
            public void run() {
                DateUtils.format(new Date(), "yyyy-MM-dd");
            };
        };
        Thread t3 = new Thread() {
            @Override
            public void run() {
                DateUtils.format(new Date(), "yyyy-MM-dd");
            };
        };
        Thread t4 = new Thread() {
            @Override
            public void run() {
                try {
                    DateUtils.parse("2017-06-10 12:00:01", "yyyy-MM-dd HH:mm:ss");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        };
        Thread t5 = new Thread() {
            @Override
            public void run() {
                try {
                    DateUtils.parse("2017-06-10 12:00:01", "yyyy-MM-dd HH:mm:ss");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        };
        System.out.println("单线程执行:");
        ExecutorService exec1 = Executors.newFixedThreadPool(1);
        exec1.execute(t1);
        exec1.execute(t2);
        exec1.execute(t3);
        exec1.execute(t4);
        exec1.execute(t5);
        exec1.shutdown();

        Thread.sleep(1000);

        System.out.println("双线程执行:");
        ExecutorService exec2 = Executors.newFixedThreadPool(2);
        exec2.execute(t1);
        exec2.execute(t2);
        exec2.execute(t3);
        exec2.execute(t4);
        exec2.execute(t5);
        exec2.shutdown();
    }
}

Os resultados são os seguintes:
Aqui Insert Picture Descrição
1) uma thread executa cinco tarefas, dois novos SimpleDateFormat objetos
2) dois tópicos para realizar cinco tarefas, novas quatro objetos SimpleDateFormat, cada segmento tem dois formato "aaaa-MM- dd "" aAAA-mM- dD HH: mm: ss " objeto, SimpleDateFormat não compartilhada objetos entre segmentos, para cada segmento, são atuadores lineares, o fenômeno não parece partilhar o calendário. É a solução perfeita para o segmento seguro problema SimpleDateFormat.
Se você usar o primeiro método, realizar cinco tarefas, certamente precisa de um novo cinco SimpleDateFormat, o conceito de um único segmento não reutilizar. Mas usando ThreadLocal, por um único segmento, o mesmo formato que pode ser reutilizado objeto SimpleDateFormat, assim desta forma, tanto para garantir a thread-safe, eles não podem consumir muitos recursos do sistema, de fato, esta idéia e solicitação da web, objeto de resposta é o mesmo que , é isolado por um fio, cada thread para manter uma cópia de seus objetos para garantir a segurança do thread.

Publicado 99 artigos originais · ganhou elogios 2 · Vistas 2613

Acho que você gosta

Origin blog.csdn.net/weixin_41588751/article/details/105231421
Recomendado
Clasificación