1. Processos e tópicos
1. Qual é o processo?
Definição estreita: um processo é uma instância de um programa de computador que está sendo executado.
Definição geral: um processo é uma atividade em execução de um programa com certas funções independentes sobre um determinado conjunto de dados. É a unidade básica de execução dinâmica do sistema operacional. Nos sistemas operacionais tradicionais, os processos são unidades básicas de alocação e unidades básicas de execução.
2. O que é um tópico?
Os threads, às vezes chamados de processos leves (LWP), são as menores unidades do fluxo de execução do programa. Um encadeamento padrão consiste em um ID do thread, ponteiro de instrução atual (PC), um conjunto de registros e uma pilha. Além disso, um thread é uma entidade no processo e é a unidade básica que é agendada e despachada independentemente pelo sistema. O tópico em si não possui os recursos do sistema, mas possui apenas alguns recursos essenciais durante a operação, mas pode compartilhar todos os recursos de propriedade do processo com outros threads pertencentes ao mesmo processo.
3. Qual é a diferença entre um processo e um thread?
A principal diferença entre processos e threads é que eles são métodos diferentes de gerenciamento de recursos do sistema operacional.
Um processo tem um espaço de endereço independente. Após um processo falhado, ele não afetará outros processos no modo protegido e um encadeamento é apenas um caminho de execução diferente em um processo.
Os threads têm sua própria pilha e variáveis locais, mas não há espaço de endereço separado entre os threads. Se um thread morre, significa que todo o processo morre. Portanto, os programas de vários processos são mais robustos que os programas de thread, mas ao trocar os processos, eles consomem mais recursos e são menos eficientes. No entanto, para algumas operações simultâneas que requerem operações simultâneas que exigem compartilhamento de determinadas variáveis, elas só podem usar threads, não processos.
Em suma, a diferença entre um thread e um processo é:
(1) um programa possui pelo menos um processo e um processo possui pelo menos um thread;
(2) A escala de divisão dos threads é menor que a do processo, aumentando a simultaneidade dos programas com vários threads.
(3) O processo possui unidades de memória independentes durante a execução e vários threads compartilham memória, o que melhora bastante a eficiência da operação do programa.
(4) Há uma diferença entre threads e processos durante a execução. Cada encadeamento independente tem uma entrada para a execução do programa, uma sequência de execução e uma saída para o programa. No entanto, os threads não podem ser executados de forma independente e devem existir no aplicativo, e vários controles de execução de threads são fornecidos pelo aplicativo.
(5) De um ponto de vista lógico, o significado de threading multi-threads nisso em um aplicativo, várias peças de execução podem ser executadas ao mesmo tempo. No entanto, o sistema operacional não considera vários threads como vários aplicativos independentes para realizar a programação de processos, gerenciamento e alocação de recursos.
Essa é a diferença importante entre um processo e um thread.
2. O ciclo de vida de um tópico e os cinco estados básicos
Os threads java têm cinco estados básicos:
(1) Novo estado (novo): Quando o par de objetos do thread é criado, ele entra no novo estado, como: thread t = new mythread ();
(2) Estado pronto (executável): Quando o método start () do objeto Thread (t.start ();), o thread entra no estado pronto. Um segmento no estado pronto significa apenas que o thread está pronto e está esperando a CPU agendar a execução a qualquer momento, não que o thread seja executado imediatamente após a execução de t.start ();
(3) Estado em execução: quando a CPU começa a agendar threads no estado pronto, o thread pode ser realmente executado, ou seja, entra no estado em execução. Nota: O estado pronto é a única entrada para o estado em execução, ou seja, se um thread deseja entrar no estado em execução para executar, ele deve estar primeiro no estado pronto;
(4) Estado bloqueado: por algum motivo, um tópico no estado em execução desiste temporariamente do uso da CPU e interrompe a execução. Neste momento, entra no estado de bloqueio. Não terá a chance de ser chamado pela CPU novamente para entrar no estado em execução. De acordo com os motivos do bloqueio, os estados de bloqueio podem ser divididos em três tipos:
① Esperando pelo bloqueio: o tópico no estado em execução executa o método wait () para fazer o thread entrar no estado de bloqueio;
② Bloqueio sincronizado: o encadeamento falha ao adquirir a trava de sincronização sincronizada (porque a trava é ocupada por outros threads) e entrará no estado de bloqueio sincronizado;
Blocking OUTRO Bloqueio: Ao ligar para o Sleep () ou Join () ou enviar uma solicitação de E/S, o thread entrará em um estado de bloqueio. Quando o Sleep () State se estendeu, a junção () esperou que o thread termine ou o tempo de tempo ou o processamento de E/S tenha sido concluído, o thread reinseriu para o estado pronto.
(5) Estado morto: o thread terminou de executar ou sair do método run () devido a uma exceção, e o thread termina seu ciclo de vida.
3. Implementação do Java Multithreading
Em Java, se você deseja implementar um programa multi-thread, deve confiar na classe principal de um thread (como o conceito de uma classe principal, que representa a classe principal de um thread), mas a classe principal deste thread precisa ter alguns requisitos especiais ao defini-lo. Esta classe pode herdar a classe Thread ou implementar a interface executável para concluir a definição.
1. Herite a classe de threads para implementar a multi-threading
java.lang.thread é uma classe responsável pelas operações de threads. Qualquer classe pode se tornar a classe principal de um thread se herdar a classe Thread. Como é a classe principal, deve ter seus métodos de uso e o método principal iniciado pelo thread precisa substituir o método run () na classe Thread.
Defina a classe corporal de um tópico:
classe mythread estende o thread {// a classe principal do título do thread private string title; public mythread (título da string) {this.title = title; } @Override public void run () {// O método principal do thread para (int x = 0; x <10; x ++) {System.out.println (this.title + "run, x =" + x); }}}Agora que existe uma aula de threads e há métodos de operação correspondentes, o objeto deve ser gerado e os métodos internos devem ser chamados, para que o seguinte programa seja escrito:
classe pública testdemo {public static void main (string [] args) {mythread mt1 = new mythread ("thread a"); Mythread mt2 = novo mythread ("thread b"); Mythread mt3 = novo mythread ("thread c"); mt1.run (); mt2.run (); mt3.run (); }Resultados em execução:
Thread A Runs, x = 0
Thread A Runs, x = 1
Thread A Runs, x = 2
Thread A Runs, x = 3
Thread A Runs, x = 4
Thread A Runs, x = 5
Thread A Runs, x = 6
Thread A Runs, x = 7
Thread A Runs, x = 8
Thread A Runs, x = 9
O thread B é executado, x = 0
O thread B é executado, x = 1
O thread B é executado, x = 2
O tópico B é executado, x = 3
O tópico B é executado, x = 4
O thread B é executado, x = 5
O thread B é executado, x = 6
O thread B é executado, x = 7
O thread B é executado, x = 8
O thread B é executado, x = 9
Thread C é executado, x = 0
Thread C é executado, x = 1
Thread C é executado, x = 2
Thread C é executado, x = 3
Thread C é executado, x = 4
Thread C é executado, x = 5
Thread C é executado, x = 6
Thread C é executado, x = 7
Thread C é executado, x = 8
Thread C é executado, x = 9
Descobrimos que as operações acima não iniciam o multi-threading, porque a execução de vários threads deve ser executada alternadamente e, neste momento, é executada sequencialmente, e o código de cada objeto continuará sendo executado para baixo após o código de cada objeto ser executado.
Se você deseja realmente iniciar o multi-threading em um programa, deve confiar em um método da classe Thread: public void start (), o que significa que você realmente inicia o multi-threading. Depois de chamar esse método, o método run () será indiretamente chamado:
classe pública testdemo {public static void main (string [] args) {mythread mt1 = new mythread ("thread a"); Mythread mt2 = novo mythread ("thread b"); Mythread mt3 = novo mythread ("thread c"); mt1.start (); mt2.start (); mt3.start (); }}Resultados em execução:
Thread C é executado, x = 0
Thread A Runs, x = 0
O thread B é executado, x = 0
Thread A Runs, x = 1
Thread C é executado, x = 1
Thread A Runs, x = 2
O thread B é executado, x = 1
Thread A Runs, x = 3
Thread A Runs, x = 4
Thread A Runs, x = 5
Thread C é executado, x = 2
Thread C é executado, x = 3
Thread C é executado, x = 4
Thread C é executado, x = 5
Thread C é executado, x = 6
Thread C é executado, x = 7
Thread C é executado, x = 8
Thread C é executado, x = 9
Thread A Runs, x = 6
Thread A Runs, x = 7
Thread A Runs, x = 8
Thread A Runs, x = 9
O thread B é executado, x = 2
O tópico B é executado, x = 3
O tópico B é executado, x = 4
O thread B é executado, x = 5
O thread B é executado, x = 6
O thread B é executado, x = 7
O thread B é executado, x = 8
O thread B é executado, x = 9
No momento, você pode descobrir que vários threads são executados alternadamente entre si, mas os resultados de cada execução são diferentes. Através do código acima, podemos tirar uma conclusão: se você deseja iniciar um thread, deve confiar no método START () da classe Thread para executar. Após o início do thread, o método run () será chamado por padrão.
Depois de chamar o método START (), uma série de coisas complicadas aconteceu:
(1) iniciar um novo thread de execução (com uma nova pilha de chamadas);
(2) o encadeamento é transferido do novo estado para o estado executável;
(3) Quando o thread tiver a oportunidade de executar, seu método Target Run () será executado.
Nota: Para Java, o método Run () não tem nada de especial. Como o método main (), significa apenas que o novo thread conhece o nome do método (e a assinatura) da chamada. Portanto, é legal chamar o método de execução no Runnable ou Thread, mas não inicia um novo thread.
Explicação: Por que você precisa ligar para o start () em vez de chamar diretamente o run () quando um thread iniciar?
Descobrimos que, depois de ligar para o start (), ele realmente executa o método RUN () substituído (), então por que não chamar o método run () diretamente? Para explicar esse problema, abra o código fonte da classe Thread e observe a definição do método START ():
public sincronizado void start () {if (threadStatus! = 0) lança novo ilegalthreadStateException (); group.add (isto); boolean iniciado = false; tente {start0 (); iniciado = true; } finalmente {try {if (! iniciado) {group.ThreadStartFailed (this); }} Catch (throwable ignore) {}}} private nativo void start0 ();Abra o código -fonte desse método e você primeiro descobrirá que o método lançará uma exceção "ilegalthreadStateException". De um modo geral, se um método usa o Throw para lançar um objeto de exceção, essa exceção deve ser capturada usando tentativa ... captura ou jogado usando lances na declaração do método, mas não há nada nessa área. Por que? Porque esta classe de exceção pertence a uma subclasse da exceção de tempo de execução (RunTimeException):
java.lang.Object
|- java.lang.THOWLE
|- java.lang.Exception
|- java.lang.runtimeException
|- java.lang.illegalargumentException
|- java.lang.illegalthreadStateException
Essa exceção será lançada quando um objeto de thread for iniciado repetidamente, ou seja, um objeto Thread só pode ser iniciado uma vez.
Uma das partes mais críticas do método START () é o método start0 (), e esse método usa uma definição de palavra -chave nativa.
A palavra -chave nativa refere -se à interface nativa Java, ou seja, o Java é usado para chamar as funções de função do sistema operacional nativo para concluir algumas operações especiais. Esse desenvolvimento de código é quase raramente visto em Java porque a maior característica do Java é a portabilidade. Se um programa só puder ser usado em um sistema operacional fixo, a portabilidade será completamente perdida, para que essa operação geralmente não seja usada.
A implementação do multithreading deve exigir o suporte do sistema operacional. Então, o método start0 () acima é realmente muito semelhante ao método abstrato sem um corpo de método. Esse corpo de método é entregue à JVM para implementar, ou seja, a JVM no Windows pode usar o método A para implementar o start0 (), enquanto a JVM no Linux pode usar o método B para implementar o start0 (), mas ao ligar, não se importará com o método específico de implementar o método START0 (), mas apenas os cuidados com a operação final e a mão e a mão para o método JV para o JV para o JV para o método JV (), mas apenas os cuidados com a operação e a mão.
Portanto, em operações multithread, usando o método START () para iniciar operações com vários threads requer chamadas de função do sistema operacional.
2. Implemente a interface executável para implementar a multi-threading
O uso da classe Thread pode de fato facilitar a implementação de múltiplos threading, mas a maior desvantagem desse método é o problema da herança única. Para esse fim, a interface executável também pode ser usada no Java para implementar o multi-threading. A definição dessa interface é a seguinte:
interface pública runnable {public void run ();}Implementar multi-threading através da interface executável:
classe Mythread implementa Runnable {// O título da String Private da classe principal do Thread; public mythread (título da string) {this.title = title; } @Override public void run () {// O método principal do thread para (int x = 0; x <10; x ++) {System.out.println (this.title + "run, x =" + x); }}}Isso não é muito diferente da maneira anterior de herdar classes de threads, mas uma vantagem é que evita a limitação da herança única.
Mas o problema está aqui. Como mencionado anteriormente, se você deseja iniciar o multi-threading, precisará confiar no método START () da classe Thread. Ao herdar a classe Thread, pode herdar diretamente esse método e usá -lo. Mas agora você está implementando a interface executável. Sem esse método, você pode herdá -lo. O que você deve fazer?
Para resolver esse problema, você ainda precisa confiar na classe Thread para concluí -lo. Um construtor é definido na classe Thread para receber o objeto de interface executável:
Tópico público (Target Runnable);
Inicie o Multithreading usando a classe Thread:
classe pública testdemo {public static void main (string [] args) lança exceção {mythread mt1 = new mythread ("thread a"); Mythread mt2 = novo mythread ("thread b"); Mythread mt3 = novo mythread ("thread c"); novo thread (mt1) .start (); novo thread (mt2) .start (); novo thread (mt3) .start (); }}Resultados em execução:
Thread A Runs, x = 0
O thread B é executado, x = 0
O thread B é executado, x = 1
Thread C é executado, x = 0
O thread B é executado, x = 2
Thread A Runs, x = 1
O tópico B é executado, x = 3
Thread C é executado, x = 1
Thread C é executado, x = 2
O tópico B é executado, x = 4
O thread B é executado, x = 5
Thread A Runs, x = 2
Thread A Runs, x = 3
Thread A Runs, x = 4
Thread A Runs, x = 5
Thread A Runs, x = 6
Thread A Runs, x = 7
Thread A Runs, x = 8
Thread A Runs, x = 9
O thread B é executado, x = 6
O thread B é executado, x = 7
O thread B é executado, x = 8
O thread B é executado, x = 9
Thread C é executado, x = 3
Thread C é executado, x = 4
Thread C é executado, x = 5
Thread C é executado, x = 6
Thread C é executado, x = 7
Thread C é executado, x = 8
Thread C é executado, x = 9
Neste momento, não apenas a startup com vários threades é alcançada, mas também nenhuma limitações de herança única.
3. O terceiro método para implementar o multi-thread: use a interface chamada para implementar
O multithreading usando a interface executável pode evitar a limitação da herança única, mas há um problema que o método run () na interface executável não pode retornar o resultado da operação. Para resolver esse problema, é fornecida uma nova interface: a interface chamada (java.util.concurrent.callable).
interface pública Callable <V> {public v Call () lança exceção;}Depois de executar o método Call () na interface chamada, um resultado será retornado. O tipo de resultado retornado é determinado pelos genéricos na interface chamada.
A operação específica da implementação da interface chamada para implementar a multi-threading é:
Crie uma classe de implementação da interface chamada e implemente o método clall (); Em seguida, use a classe FutureTask para envolver o objeto da classe de implementação chamada e use este objeto FutureTask como o alvo do objeto Thread para criar um encadeamento.
Defina uma aula de corpo de thread:
importar java.util.concurrent.callable; classe mythread implementa Callable <String> {private int ticket = 10; @Override public String Call () lança Exceção {for (int i = 0; i <20; i ++) {if (this.ticket> 0) {System.out.println ("Vender ingressos, o número restante de votos é"+this.Ticket -); }} retornar "Os ingressos foram esgotados"; }}A classe Thread não suporta diretamente a interface chamada. Após o JDK1.5, é fornecida uma aula:
java.util.concurrent.futureTask <V>
Esta classe é principalmente responsável pela operação do objeto de interface chamável. Sua estrutura de definição é a seguinte:
Classe pública FutureTask <V>
estende o objeto
implementa RunnableFurture <V>
A interface RunnableFurture tem a seguinte definição:
Interface pública RunnableFurture <V>
estende Runnable, Future <V>
O seguinte construtor é definido na classe FutureTask:
public FutureTask (Callable <V> Callable)
Agora, você pode finalmente receber o objeto de interface chamável através da classe FutureTask. O objetivo do recebimento é obter o resultado de retorno do método Call ().
A partir da análise acima, podemos encontrar:
A classe FutureTask pode receber objetos de interface chamáveis, e a classe FutureTask implementa a interface RunnableFurture, que herda a interface executável.
Então, podemos começar a multithreading assim:
classe pública testdemo {public static void main (string [] args) lança exceção {mythread mt1 = new mythread (); Mythread mt2 = new mythread (); FutureTask <String> tarefa1 = new FutureTask <String> (MT1); // Método Get Call () para retornar o resultado FutureTask <String> Task2 = new FutureTask <String> (mt2); // Obtenha o método () para retornar o resultado // FutureTask é uma subclasse da interface executável. Você pode usar a construção da classe Thread para receber objetos de tarefas novos thread (tarefa1) .start (); novo thread (Task2) .start (); // Depois que a execução multithread é concluída, você pode usar o método get () na interface pai da FutureTask Future para obter o resultado do resultado da execução.out.println ("Resultado de retorno do thread 1:"+task1.get ()); System.out.println ("Resultado de retorno do thread 2:"+task2.get ()); }}Resultados em execução:
Vender ingressos, o número restante de votos é 10
Vender ingressos, o número restante de votos é 10
Vender ingressos, o número restante de votos é 9
Vender ingressos, o número restante de votos é 8
Vender ingressos, o número restante de votos é 7
Vender ingressos, o número restante de votos é 9
Vender ingressos, o número restante de votos é 6
Vender ingressos, o número restante de votos é 8
Vender ingressos, o número restante de votos é 5
Vender ingressos, o número restante de votos é 7
Vender ingressos, o número restante de votos é 4
Vender ingressos, o número restante de votos é 6
Vender ingressos, o número restante de votos é 3
Vender ingressos, o número restante de votos é 5
Vender ingressos, o número restante de votos é 2
Vender ingressos, o número restante de votos é 4
Vender ingressos, o número restante de votos é 1
Vender ingressos, o número restante de votos é 3
Vender ingressos, o número restante de votos é 2
Vender ingressos, o número restante de votos é 1
O resultado de retorno do thread 1: o bilhete foi esgotado. O resultado de retorno do thread 2: o bilhete foi esgotado.
resumo:
O acima explica três maneiras de implementar multithreading. Para startup de threads, todos eles são chamados de método start () do objeto Thread. É importante observar que o método start () não pode ser chamado duas vezes no mesmo objeto de encadeamento.