1. Visão geral do tópico
Threads são a unidade básica de execução para execução do programa. Quando o sistema operacional (excluindo sistemas operacionais de thread único, como o DOS inicial da Microsoft) executa um programa, um processo será estabelecido no sistema e, nesse processo, pelo menos um thread deve ser estabelecido (este thread é chamado de principal thread). thread) como o ponto de entrada para a execução deste programa. Portanto, qualquer programa em execução no sistema operacional possui pelo menos um thread principal.
Processos e threads são dois modelos operacionais essenciais nos sistemas operacionais modernos. Pode haver vários processos no sistema operacional, incluindo processos de sistema (processos criados internamente pelo sistema operacional) e processos de usuário (processos criados por programas de usuário) e um processo pode ter um ou mais threads. Não há memória compartilhada entre processos, o que significa que os processos do sistema são executados em seus próprios espaços de memória independentes. Threads em um processo podem compartilhar o espaço de memória alocado pelo sistema para o processo.
Os threads podem não apenas compartilhar a memória do processo, mas também ter um espaço de memória próprio. Esse espaço de memória também é chamado de pilha de threads. Ele é alocado pelo sistema quando o thread é criado. dados usados dentro do thread, como pilha de threads. Variáveis definidas na função de execução.
Nota: Qualquer thread executará uma função quando for criada. Esta função é chamada de função de execução de thread. Esta função também pode ser considerada o ponto de entrada do thread (semelhante à função principal do programa). Não importa qual linguagem ou tecnologia seja usada para criar um thread, esta função deve ser executada (a expressão desta função pode ser diferente, mas sempre haverá tal função). Por exemplo, o terceiro parâmetro da função API CreateThread usada para criar um thread no Windows é o ponteiro desta função de execução.
Depois que o sistema operacional divide o processo em vários threads, esses threads podem ser executados simultaneamente sob o gerenciamento do sistema operacional, melhorando significativamente a eficiência de execução do programa. Embora a execução de threads pareça a execução de vários threads ao mesmo tempo do ponto de vista macro, na verdade isso é apenas um encobrimento do sistema operacional. Como uma CPU só pode executar uma instrução por vez, é impossível executar duas tarefas ao mesmo tempo em um computador com uma CPU. Para melhorar a eficiência de execução do programa, o sistema operacional removerá um thread quando ele estiver ocioso e permitirá que outros threads o executem. Este método é chamado de agendamento de thread. A razão pela qual vemos vários threads sendo executados ao mesmo tempo na superfície é porque o tempo para alternar entre diferentes threads é muito curto e, em circunstâncias normais, a alternância é muito frequente. Suponha que tenhamos os threads A e B. Em tempo de execução, pode ser que depois de A ser executado por 1 milissegundo, depois de mudar para B, B seja executado por mais 1 milissegundo, depois mude para A e A seja executado por mais 1 milissegundo. Como 1 milissegundo é difícil de ser percebido pelas pessoas comuns, superficialmente parece que A e B são executados ao mesmo tempo, mas na verdade A e B são executados alternadamente.
2. Os benefícios que os fios trazem para nós
O uso adequado de threads pode reduzir os custos de desenvolvimento e manutenção e até melhorar o desempenho de aplicações complexas. Por exemplo, em aplicativos GUI, os eventos podem ser melhor processados por meio da natureza assíncrona dos threads; em programas de servidor de aplicativos, vários threads podem ser estabelecidos para lidar com solicitações de clientes; Threads podem até simplificar a implementação de máquinas virtuais. Por exemplo, o coletor de lixo da Java Virtual Machine (JVM) geralmente é executado em um ou mais threads. Portanto, o uso de threads melhorará nossas aplicações nos cinco aspectos a seguir:
1. Aproveite ao máximo os recursos da CPU
A maioria dos computadores no mundo agora possui apenas uma CPU. Portanto, é particularmente importante aproveitar ao máximo os recursos da CPU. Ao executar um programa de thread único, a CPU pode ficar ociosa enquanto o programa está bloqueado. Isso causará muito desperdício de recursos computacionais. O uso de multithreading em um programa pode executar outros threads quando um thread está inativo ou bloqueado e a CPU está ociosa. Desta forma, é difícil que a CPU fique ociosa. Portanto, os recursos da CPU são totalmente utilizados.
2. Simplifique o modelo de programação
Se o programa concluir apenas uma tarefa, basta escrever um programa de thread único e escrever o código de acordo com as etapas para executar a tarefa. Mas para completar múltiplas tarefas, se você ainda usar um único thread, você terá que determinar no programa se e quando cada tarefa deve ser executada. Por exemplo, exibe as horas, minutos e segundos de um relógio. Se você usar um único thread, deverá avaliar o tempo de rotação e o ângulo desses três ponteiros, um por um, no loop. Se três threads forem usados para lidar com a exibição desses três ponteiros, isso significará executar uma tarefa separada para cada thread. Isso ajuda os desenvolvedores a compreender e manter o programa.
3. Simplifique o processamento de eventos assíncronos
Quando um aplicativo de servidor recebe diferentes conexões de clientes, a maneira mais simples de lidar com isso é criar um thread para cada conexão de cliente. O thread de escuta ainda é responsável por ouvir as solicitações do cliente. Se este tipo de aplicação for processado por um único thread, quando o thread de escuta recebe uma solicitação do cliente, ele passa a ler os dados enviados pelo cliente. Após a leitura dos dados, o método de leitura é bloqueado, ou seja, este thread. will não poderá mais ouvir as solicitações dos clientes. Se quiser lidar com várias solicitações de clientes em um único thread, você deverá usar conexões de soquete sem bloqueio e E/S assíncrona. No entanto, usar E/S assíncrona é mais difícil de controlar e mais sujeito a erros do que usar E/S síncrona. Portanto, eventos assíncronos, como solicitações múltiplas, podem ser tratados mais facilmente usando multithreading e E/S síncrona.
4. Torne a GUI mais eficiente
Ao usar um único encadeamento para processar eventos de GUI, um loop deve ser usado para verificar eventos de GUI que podem ocorrer a qualquer momento. Dentro do loop, além de verificar eventos de GUI, outros códigos de programa devem ser executados. Se o código for muito longo, os eventos da GUI ficarão "congelados" até que o código seja executado.
Em estruturas GUI modernas (como SWING, AWT e SWT), um thread de despacho de eventos (EDT) separado é usado para verificar eventos GUI. Quando pressionamos um botão, a função de evento click do botão será chamada neste thread de envio de evento. Como a tarefa do EDT é apenas verificar eventos da GUI, a resposta aos eventos dessa forma é muito rápida.
5. Economia de custos
Geralmente existem três métodos para melhorar a eficiência da execução do programa:
(1) Aumente o número de CPUs no computador.
(2) Iniciar vários processos para um programa
(3) Use vários processos no programa.
O primeiro método é o mais fácil de fazer, mas também o mais caro. Este método não requer modificação do programa. Em teoria, qualquer programa pode usar este método para melhorar a eficiência de execução. Embora o segundo método não exija a compra de novo hardware, não é fácil compartilhar dados. Se a tarefa a ser concluída por este programa exigir o compartilhamento de dados, esse método não é conveniente e iniciar vários threads consumirá muito sistema. recursos. O terceiro método apenas compensa as deficiências do primeiro método, ao mesmo tempo que herda as suas vantagens. Em outras palavras, não há necessidade de adquirir uma CPU, nem ocupará muitos recursos do sistema ao iniciar muitos threads (por padrão, o espaço de memória ocupado por um thread é muito menor que o espaço de memória ocupado por um processo) . Múltiplo) e multithreading podem simular o modo de execução de várias CPUs, portanto, usar multithreading é a maneira mais barata de melhorar a eficiência de execução do programa.
3. Modelo de thread Java
Como Java é uma linguagem puramente orientada a objetos, o modelo de threading do Java também é orientado a objetos. Java encapsula todas as funções necessárias de threads por meio da classe Thread. Para criar um thread, deve haver uma função de execução de thread. Esta função de execução de thread corresponde ao método run da classe Thread. A classe Thread também possui um método start, que é responsável por estabelecer um thread, o que equivale a chamar a função de criação de thread do Windows CreateThread. Quando o método start é chamado, se o thread for estabelecido com sucesso, o método run da classe Thread é chamado automaticamente. Portanto, qualquer classe Java que herde Thread pode criar um thread por meio do método start da classe Thread. Se quiser executar sua própria função de execução de thread, você deve substituir o método run da classe Thread.
Além da classe Thread no modelo de thread Java, há também uma interface Runnable que identifica se uma classe Java pode ser usada como uma classe de thread. Essa interface possui apenas um método abstrato run, que é a função de execução de thread do Java. modelo de rosca. Portanto, o único critério para uma classe de thread é se a classe implementa o método run da interface Runnable. Em outras palavras, uma classe com uma função de execução de thread é uma classe de thread.
Como pode ser visto acima, existem duas maneiras de criar threads em Java. Uma é herdar a classe Thread e a outra é implementar a interface Runnable e criar threads por meio de Thread e da classe que implementa Runnable. esses dois métodos são essencialmente Diz-se que é um método, ou seja, o thread é criado por meio da classe Thread e o método run é executado. Mas a grande diferença é que os threads são criados herdando a classe Thread. Embora seja mais fácil de implementar, como Java não suporta herança múltipla, se esta classe de thread herdar Thread, ela não pode herdar outras classes. métodos para estabelecer threads implementando a interface Runnable, para que as classes de thread possam herdar classes relacionadas a negócios em vez da classe Thread quando necessário.