Entrevistador Jingdong: Em quais cenários o multithreading de Java em projetos corporativos geralmente é usado?

O principal objetivo do multithreading é:

1. Taxa de transferência: Quando você faz WEB, o contêiner faz multithreading para você, mas só pode fazer o nível de solicitação para você. Simplificando, pode ser um segmento por solicitação. Ou vários pedidos de um tópico. Se for de thread único, ele só pode processar a solicitação de um usuário ao mesmo tempo.

2. Escalabilidade: Em outras palavras, você pode aumentar o desempenho aumentando o número de núcleos da CPU. Se for de thread único, o programa usará um único núcleo até que seja executado.Não há maneira de aumentar o desempenho aumentando o número de núcleos da CPU.

Tendo em vista que você está fazendo a WEB, dificilmente o primeiro ponto pode estar envolvido. Deixe-me falar sobre o segundo ponto aqui.

Dê um exemplo simples:

Supondo que haja uma solicitação, o processamento do servidor de solicitação precisa realizar 3 operações de IO muito lentas (como consulta de banco de dados ou consulta de arquivo), então a sequência normal pode ser (os parênteses representam o tempo de execução):

  1. Leia o arquivo 1 (10ms)
  2. Dados do processo 1 (1ms)
  3. Leia o arquivo 2 (10ms)
  4. Dados do processo 2 (1ms)
  5. Leia o arquivo 3 (10ms)
  6. Processar 3 dados (1ms)
  7. Integre os resultados de dados de 1, 2 e 3 (1ms)

Leva 34 ms no total para um único thread.

Se você dividir ab, cd e ef em 3 threads nesta solicitação, leva apenas 12 ms.

Portanto, multithreading não é que seja inútil, mas que você geralmente tem que ser bom em encontrar alguns pontos que podem ser otimizados. Em seguida, avalie se o programa deve ser usado. Suponha o mesmo problema acima: mas o tempo de execução de cada etapa é diferente.

  1. Leia o arquivo 1 (1ms)
  2. Dados do processo 1 (1ms)
  3. Leia o arquivo 2 (1ms)
  4. Dados do processo 2 (1ms)
  5. Leia o arquivo 3 (28ms)
  6. Processar 3 dados (1ms)
  7. Integre os resultados de dados de 1, 2 e 3 (1ms)

Leva 34 ms no total para um único thread.

Se você ainda seguir o esquema de divisão acima (o esquema acima é o mesmo que o princípio do barril, o consumo de tempo depende da velocidade de execução do encadeamento mais lento), neste exemplo é o terceiro encadeamento, que executa 29ms. Então, a última solicitação leva 30ms. Comparado a não usar thread único, ele economiza 4ms. No entanto, também pode demorar 1,2 ms para alternar o agendamento de thread. Portanto, a vantagem desse esquema não é óbvia e também traz um aumento na complexidade do programa. Não vale a pena.

Portanto, o ponto de otimização agora não é a divisão de tarefas e multi-threading como no primeiro exemplo. Mas para otimizar a velocidade de leitura do arquivo 3. Pode ser para usar o cache e reduzir algumas leituras repetidas.

Em primeiro lugar, suponha que haja uma situação em que todos os usuários solicitem essa solicitação, que na verdade é equivalente a todos os usuários que precisam ler o arquivo 3. Então pense nisso, 100 pessoas fizeram essa solicitação, o que significa que o tempo que você gasta lendo este arquivo é 28 × 100 = 2800ms. Portanto, se você armazenar o arquivo em cache, enquanto a solicitação do primeiro usuário for lida, o segundo usuário não precisará lê-lo.O acesso interno é muito rápido, podendo ser inferior a 1ms.

Código falso:

public class MyServlet extends Servlet{
    private static Map<String, String> fileName2Data = new HashMap<String, String>();
    private void processFile3(String fName){
        String data = fileName2Data.get(fName);
        if(data==null){
            data = readFromFile(fName); //耗时28ms
            fileName2Data.put(fName, data);
        }
        //process with data
    }
}

Parece ser muito bom. Crie um mapeamento entre o nome do arquivo e os dados do arquivo. Se você ler dados que já existem em um mapa, não precisará ler o arquivo.

Mas o problema é que o Servlet é concorrente, e isso causará um problema muito sério, um loop infinito. Porque, quando o HashMap é modificado simultaneamente, pode levar à formação de uma lista ligada circular! ! ! (Para obter detalhes, você mesmo pode ler o código-fonte do HashMap) Se você não tocou em muitos threads, pode descobrir que o servidor não solicitou um cartão enorme até então e você não sabe qual é a situação!

Ok, então use ConcurrentHashMap, assim como seu nome, ele é um HashMap seguro para thread, que pode resolver o problema facilmente.

public class MyServlet extends Servlet{
    private static ConcurrentHashMap<String, String> fileName2Data = new ConcurrentHashMap<String, String>();
    private void processFile3(String fName){
        String data = fileName2Data.get(fName);
        if(data==null){
            data = readFromFile(fName); //耗时28ms
            fileName2Data.put(fName, data);
        }
        //process with data
    }
}

Isso realmente resolve o problema, de modo que mesmo que um usuário tenha acessado o arquivo a, outro usuário que deseja acessar o arquivo a também obterá dados de fileName2Data e, então, não causará um loop infinito.

No entanto, se você acha que isso acabou, então você pensa em multithreading facilmente, Sao Nian! Você descobrirá que, quando 1000 usuários acessam o mesmo arquivo pela primeira vez, eles realmente o leem 1000 vezes (este é o mais extremo, talvez apenas algumas centenas). Que porra é essa !!!

O código está errado, posso viver minha vida assim!

Analise bem. Servlet é multi-threaded, então

public class MyServlet extends Servlet{
    private static ConcurrentHashMap<String, String> fileName2Data = new ConcurrentHashMap<String, String>();
    private void processFile3(String fName){
        String data = fileName2Data.get(fName);
        //“偶然”-- 1000个线程同时到这里,同时发现data为null
        if(data==null){
            data = readFromFile(fName); //耗时28ms
            fileName2Data.put(fName, data);
        }
        //process with data
    }
}

O "acidental" comentado acima é inteiramente possível, então ainda há um problema em fazê-lo.

Portanto, você pode simplesmente encapsular uma tarefa para manipulá-la sozinho.

public class MyServlet extends Servlet{
    private static ConcurrentHashMap<String, FutureTask> fileName2Data = new ConcurrentHashMap<String, FutureTask>();
    private static ExecutorService exec = Executors.newCacheThreadPool();
    private void processFile3(String fName){
        FutureTask data = fileName2Data.get(fName);
        //“偶然”-- 1000个线程同时到这里,同时发现data为null
        if(data==null){
            data = newFutureTask(fName);
            FutureTask old = fileName2Data.putIfAbsent(fName, data);
            if(old==null){
                data = old;
            }else{
                exec.execute(data);
            }
        }
        String d = data.get();
        //process with data
    }
     
    private FutureTask newFutureTask(final String file){
        return  new FutureTask(new Callable<String>(){
            public String call(){
                return readFromFile(file);
            }
 
            private String readFromFile(String file){return "";}
        }
    }
}

Todos os códigos acima são digitados diretamente em bbs e não há garantia de que possam ser executados diretamente.

Cenários com mais multithreading: próprio servidor da web; vários servidores dedicados (como servidores de jogos);
cenários de aplicativos comuns para multithreading:

  • Tarefas em segundo plano, como: enviar emails regularmente para um grande número de usuários (acima de 100w);
  • Processamento assíncrono, como: postagem no Weibo, registro de registros, etc .;
  • Computação distribuída

Benefícios do leitor

Obrigado por ver aqui!
Compilei muitas das últimas perguntas da entrevista sobre Java para 2.021 (incluindo respostas) e notas de estudo sobre Java aqui, conforme mostrado abaixo
Insira a descrição da imagem aqui

As respostas às perguntas da entrevista acima são organizadas em notas de documento. Bem como entrevistas também compilou algumas informações sobre alguns dos fabricantes e entrevista Zhenti última coleção 2021 (ambos documentando uma pequena parte da captura de tela) grátis para que todos possam compartilhar, se necessário pode clicar para inserir o sinal: CSDN! Grátis para compartilhar ~

Se você gostou deste artigo, por favor, encaminhe-o e goste.

Lembre-se de me seguir!
Insira a descrição da imagem aqui

Acho que você gosta

Origin blog.csdn.net/weixin_49527334/article/details/115081266
Recomendado
Clasificación