Ali p9 ensina você a entender o princípio de multithreading Java em três minutos

Prefácio
Thread pool, daí o nome sugere, é um pool para armazenar threads.Para colocá-lo mais academicamente, é uma coleção de recursos de thread. Por que existe um conceito de pool de threads? Pense nisso antes, quando todos nós precisamos de encadeamentos, criamos um diretamente manualmente, e então não nos importamos com isso depois de executar a tarefa. O encadeamento é uma ferramenta ou portador para realizarmos tarefas assíncronas. Não pagamos muita atenção para o thread em si. O impacto do ciclo de vida no sistema ou no ambiente, e se concentra apenas na saída dos resultados da execução de tarefas multi-threaded, e então o objetivo é alcançado, mas a manutenção e o monitoramento de recursos de thread são realmente ignorados. Com a utilização de um grande número de recursos multithread em sistemas de grande escala, a falta de atenção, manutenção e gerenciamento do multithreading tem ampliado gradativamente seu impacto na ocupação de recursos e reduzido desempenho, o que despertou o pensamento das pessoas.

  1. Item da lista

A criação e destruição de multi-threads ocupa uma grande proporção no ciclo de vida de multi-threads. Esta parte, na verdade, consome recursos e desempenho. Se os threads são usados ​​para realizar tarefas simples, o custo de manutenção dos threads excedeu os benefícios da tarefa Não vale a pena perder, então um pool de threads é criado. Através do uso de conjuntos de encadeamentos, o ciclo de vida dos encadeamentos pode ser controlado e os encadeamentos podem ser facilmente obtidos, reutilizados, e a sobrecarga de desempenho adicional causada pela criação e destruição frequente de encadeamentos pode ser evitada. Este é provavelmente o plano de fundo e a intenção original de a introdução de pools de threads.

1. Método de criação de multi-thread
1.1 Herdar a classe Thread para criar a classe thread
1. Etapas de implementação
Definir uma subclasse que herda a classe Thread e sobrescreve o método run () dessa classe;

Crie uma instância da subclasse Thread, ou seja, crie um objeto thread;

Chame o método start () do objeto thread para iniciar o thread.

2.
代码 `class SomeThead extends Thraad {public void run () {// faça algo aqui
}
}

public static void main (String [] args) {SomeThread oneThread = new SomeThread ();
//
启动线程oneThread.start (); } `

1.2 Implementar a interface Runnable para criar uma classe de thread
1. Etapas de implementação
Definir a classe de implementação da interface Runnable e reescrever o método run () da interface;

Crie uma instância da classe de implementação Runnable e use essa instância como objeto de destino de Thread, ou seja, o objeto Thread é o objeto real de thread.

2. 核心
代码 class SomeRunnable implementa Runnable {public void run () {// faça algo aqui}} Runnable oneRunnable = new SomeRunnable (); Tópico oneThread = novo Tópico (oneRunnable); oneThread.start ();

1.3 Criar um thread através de Callable e Future
1. Etapas de implementação
Crie uma classe de implementação da interface Callable e implemente o método call () .O método modificado será o corpo de execução do thread e terá um valor de retorno.

Crie uma instância da classe de implementação Callable e use a classe FutrueTask para agrupar o objeto Callable. O objeto FutureTask encapsula o valor de retorno do método call () do objeto Callable

Use o objeto FutureTask como o destino do objeto Thread para criar e iniciar um novo thread

Chame o método get () do objeto FutureTask para obter o valor de retorno após o término da execução do thread filho.

2. Código central
`// 1. Crie a classe de implementação da interface Callable e implemente o método call () public class SomeCallable01 implementa Callable {@Override public Integer call () throws Exception {int i = 0; for (; i < 10; i ++) {System.out.println (Thread.currentThread (). GetName () + "" + i);} return i;}

public static void main (String [] args) { // 2. Crie uma instância da classe de implementação Callable SomeCallable01 ctt = new SomeCallable01 ();

//3.使用FutrueTask类进行包装Callable对象,FutureTask对象封装了Callable对象的call()方法的返回值
FutureTask<Integer> ft = new FutureTask<>(ctt);

//开启ft线程
for(int i = 0;i < 21;i++)
{
    System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);
    if(i==20)//i为20的时候创建ft线程
    {
    	//4.使用FutureTask对象作为Thread对象的target创建并启动新线程
        new Thread(ft,"有返回值的线程FutureTask").start();
    }
}

//ft线程结束时,获取返回值
try
{	
	//5.调用FutureTask对象的get()方法获取子线程执行结束后的返回值。
    System.out.println("子线程的返回值:"+ft.get());//get()方法会阻塞,直到子线程执行结束才返回
} catch (InterruptedException e)
{
    e.printStackTrace();
} catch (ExecutionException e)
{
    e.printStackTrace();
}

}
Copiar código
} `

Em segundo lugar, a diferença entre as maneiras de criar threads
1. Use a classe Thread herdada para criar vários threads
1) Vantagens É
simples de escrever. Se você precisa acessar o thread atual, não precisa usar o Thread.currentThread (). Você pode usar isso diretamente para obter o tópico atual.

2) Desvantagens A
classe thread herdou a classe Thread, portanto não pode mais herdar de outras classes pai. (Existem limitações de herança única)

Ao criar vários threads, cada tarefa tem variáveis ​​de membro que não são compartilhadas e as estáticas devem ser adicionadas para conseguir o compartilhamento

2. Use o método de implementação da classe Runnable para criar multithreading
1) Vantagens
Evita as limitações de herança única.Múltiplos threads podem compartilhar um objeto de destino, o que é muito adequado para multithreading para processar o mesmo recurso.

2) As desvantagens são
mais complicadas, o acesso ao segmento deve usar o método Thread.currentThread (), sem valor de retorno.

3. Use o método de implementação da interface Callable para criar vários threads
1) Vantagens Ele
tem um valor de retorno, evita as limitações de herança única e vários threads podem compartilhar um objeto de destino, o que é muito adequado para o processamento multi-threaded do mesmo recurso.

2) As desvantagens são
mais complicadas e você deve usar o método Thread.currentThread () para acessar o thread

4. A diferença entre Runnable e Callable
1) O método Callable stipula (override) é call (), o método Runnable stipula (override) é run ().

2) Tarefas chamáveis ​​podem retornar valores após a execução, mas tarefas executáveis ​​não podem retornar valores.

3) O método de chamada pode lançar exceções, mas o método de execução não.

4) Execute a tarefa Callable para obter um objeto Future, que representa o resultado do cálculo assíncrono. Ele fornece um método para verificar se o cálculo está completo, para aguardar a conclusão do cálculo e para recuperar o resultado do cálculo. Por meio do objeto Future, você pode entender a execução da tarefa, cancelar a execução da tarefa e obter o resultado da execução future.get ().

3.
Escalonamento multi-thread 3.1. Estratégia de escalonamento
Time slice: o escalonamento de thread adota a rotação de time slice. Tipo preemptivo: thread de alta prioridade antecipa a CPU

3.2. Método de agendamento Java
1) Para threads de mesma prioridade formarem uma fila do primeiro a entrar, primeiro a sair (primeiro a chegar, primeiro a ser servido), use a estratégia de intervalo de tempo

2) Para alta prioridade, use a estratégia preemptiva de agendamento de prioridade

3.3, o
nível de prioridade do thread :

MAX_PRIORITY: 10

MIN_PRIORIDADE: 1

NORM_PRIORITY: 5

método:

`getPriority (): retorna a prioridade do thread

setPriority (int newPriority): Altera a prioridade do thread`

Observações:

Threads de alta prioridade devem antecipar os direitos de execução da CPU de threads de baixa prioridade. Mas apenas em termos de probabilidade, os threads de alta prioridade têm maior probabilidade de serem executados. Isso não significa que apenas os threads de alta prioridade são executados antes que os threads de baixa prioridade sejam executados.

4. Gerenciamento de estado multithread
4.1 Thread sleep-sleep
1) Visão geral
Se precisarmos pausar o thread atualmente em execução por um período de tempo e entrar no estado de bloqueio, podemos chamar o método sleep de Thread.

2) Método de suspensão de thread
Coloque o thread em execução para dormir dentro do número especificado de milissegundos:

sleep (long millis) Dorme o encadeamento em execução dentro do número especificado de milissegundos mais o número especificado de nanossegundos:

dormir (long millis , int nanos)

3) O
sono de implementação de código é um método estático, é melhor não chamá-lo com o objeto de instância de Thread, porque ele dorme o encadeamento que está em execução no momento, não o objeto de encadeamento que o chama. É válido apenas para o encadeamento objeto no estado de execução.

`public class SynTest {public static void main (String [] args) {new Thread (new CountDown (),“ 倒计时 ”) .start (); }}

a classe CountDown implementa Runnable {int time = 10; public void run () {while (true) {if (time> = 0) {System.out.println (Thread.currentThread (). getName () + “:” + time–); tente {Thread.sleep (1000); // 睡眠 时间 为 1 秒} catch (InterruptedException e) {e.printStackTrace (); }}}}} `

4) Observações O
agendamento de thread Java é o núcleo do multithreading Java.Somente um bom agendamento pode dar um jogo completo ao desempenho do sistema e melhorar a eficiência de execução do programa. Mas não importa como o programador escreve o cronograma, ele só pode afetar a ordem de execução do thread na maior extensão, mas não pode alcançar um controle preciso. Porque depois de usar o método sleep, o thread entra no estado de bloqueio, e somente quando o tempo de sono terminar, ele entrará novamente no estado pronto e o estado pronto entrará no estado de execução, que é controlado pelo sistema, e nós não pode interferir com precisão com ele., Portanto, se você chamar Thread.sleep (1000) para fazer o thread dormir por 1 segundo, o resultado pode ser maior que 1 segundo.

4.2. Thread concession-yield
1) Visão geral O
método yield () é semelhante ao método sleep (). Também é um método estático fornecido pela classe Thread. Ele também pode pausar o thread atualmente em execução e cpu recursos para outros Fio. Mas, ao contrário do método sleep (), ele não entra no estado de bloqueio, mas entra no estado pronto. O método yield () apenas faz a thread atual pausar, entrar novamente no pool de threads prontas e deixar o agendador de threads do sistema reagendar novamente. É inteiramente possível que esta situação: quando uma thread chama o método yield (), o agendador de threads agenda-o novamente para entrar no estado de execução para execução.

Na verdade, quando um thread chama o método yield () para suspender, a prioridade é a mesma do thread atual, ou um thread com uma prioridade mais alta do que o estado pronto do thread atual tem mais chance de ter uma chance de execução. Claro, isso é simplesmente possível, porque é impossível para nós interferir precisamente no encadeamento de escalonamento da CPU.

2) Implementação do código
public class Test1 { public static void main (String [] args) throws InterruptedException { new MyThread ("nível baixo", 1) .start (); new MyThread ("intermediário", 5) .start (); new MyThread ("Advanced", 10) .start (); } }





class MyThread extends Thread { public MyThread (String name, int pro) { super (name); // Define o nome do thread this.setPriority (pro); // Define a prioridade }



@Override  
public void run() {  
    for (int i = 0; i < 30; i++) {  
        System.out.println(this.getName() + "线程第" + i + "次执行!");  
        if (i % 5 == 0)  
            Thread.yield();  
    }  
}  

}
Copiar o código
3) A diferença entre sleep e yield
①Depois que o método sleep suspender o thread atual, ele entrará no estado de bloqueio. Somente quando o tempo de espera terminar, ele entrará no estado pronto. Depois que o método de rendimento é chamado, ele entra diretamente no estado pronto, portanto, pode apenas entrar no estado pronto e ser programado para o estado de execução.

②A instrução do método sleep lança InterruptedException, portanto, ao chamar o método sleep, a exceção deve ser detectada ou a instrução display lança a exceção. O método de rendimento não declara que uma exceção de tarefa é lançada.

③O método sleep tem melhor portabilidade do que o método yield e geralmente não depende do método yield para controlar a execução de threads simultâneos.

4.3. Encadeamento - junção
1) Visão geral
O significado da fusão de encadeamentos é fundir os encadeamentos de vários encadeamentos paralelos em um único encadeamento para a execução. A classe thread fornece O método de junção é usado para completar esta função.Note que não é um método estático.

resumidamente:

Quando o encadeamento B executa o método .join () do encadeamento A, o encadeamento B esperará e o encadeamento B será executado quando o encadeamento A terminar. join pode ser usado para juntar threads temporariamente para execução.

2) Método de mesclagem de thread
Tem três métodos sobrecarregados:

O encadeamento atual espera que ele se junte ao encadeamento e espera que o encadeamento seja encerrado.

void join ()

O tempo mais longo que o segmento atual espera para que o segmento termine é milissegundos.

Se o encadeamento não for executado dentro de milissegundos, o encadeamento atual entra no estado pronto e aguarda o agendamento da CPU novamente

junção vazia (milis longos)

O tempo de espera mais longo para este encadeamento terminar é milissegundos + nanos

Nanossegundos. Se o encadeamento não for executado dentro do tempo de milissegundos, o encadeamento atual entra no estado pronto e aguarda o agendamento da CPU novamente

junção vazia (milis longos, nanos int)

3) Implementação do código
public static void main (String [] args) lança InterruptedException { yieldDemo ms = new yieldDemo (); Thread t1 = new Thread (ms, "Zhang San fica depois de comer"); Thread t2 = novo Thread (ms , "Li Si saiu depois de comer"); Tópico t3 = novo Tópico (ms, "Wang Wu saiu depois de comer"); t1.start (); t1.join ();





    t2.start();
    t3.start();
    System.out.println( "主线程");
}`

Thread t = new Thread (() -> { try { Thread.sleep (1000); } catch (InterruptedException e) { e.printStackTrace (); } r = 10; });






t.start ();
// Deixar o bloco do thread principal e esperar que o thread t seja executado antes de continuar
// Remova a linha, o resultado da execução é 0, mais o resultado da execução da linha é 10
t.join ();
log.info ("r: {}”, r);

// Run result
13: 09: 13.892 [main] INFO thread.TestJoin-r: 10
Copiar o código
4.4, definir a prioridade do thread
1) Visão geral
Cada thread tem um atributo de prioridade quando é executado, o thread com alta prioridade pode obter mais execução oportunidades, enquanto threads com baixa prioridade obtêm menos oportunidades de execução. Semelhante ao thread em espera, a prioridade do thread ainda não pode garantir a ordem de execução dos threads. No entanto, os encadeamentos com alta prioridade têm uma probabilidade maior de adquirir recursos da CPU, e os encadeamentos com baixa prioridade têm a oportunidade de execução.

A prioridade padrão de cada encadeamento tem a mesma prioridade do encadeamento pai que o criou.Por padrão, o encadeamento principal tem prioridade normal.

2) Envolvendo métodos de prioridade A
classe Thread fornece os métodos setPriority (int newPriority) e getPriority () para definir e retornar a prioridade de um thread especificado. O parâmetro do método setPriority é um inteiro, variando de 1 a · 0. Você pode usar as três constantes estáticas fornecidas pela classe Thread:

MAX_PRIORITY = 10 MIN_PRIORITY = 1 NORM_PRIORITY = 5

3) Implementação do código
public class Test1 { public static void main (String [] args) throws InterruptedException { new MyThread ("high level", 10) .start (); new MyThread ("low level", 1) .start () ; } }




class MyThread extends Thread {  
    public MyThread(String name,int pro) {  
        super(name);//设置线程的名称  
        setPriority(pro);//设置线程的优先级  
    }  
    @Override  
    public void run() {  
        for (int i = 0; i < 100; i++) {  
            System.out.println(this.getName() + "线程第" + i + "次执行!");  
        }  
    }  
}

Copiar o código
4) Comentários
Embora o Java forneça 10 níveis de prioridade, esses níveis de prioridade requerem o suporte do sistema operacional. A prioridade dos diferentes sistemas operacionais não é a mesma e não corresponde bem aos 10 níveis de prioridade do Java. Portanto, devemos usar as três constantes estáticas MAX_PRIORITY, MIN_PRIORITY e NORM_PRIORITY para definir a prioridade, de modo a garantir a melhor portabilidade do programa.

4.5 Threads de plano de fundo (daemon)
1) Visão geral O
uso de threads daemon é raro, mas eles não são inúteis.Por exemplo, threads como coleta de lixo e gerenciamento de memória de JVM são todos threads daemon. Há também o pool de conexão de banco de dados usado ao fazer aplicativos de banco de dados.O próprio pool de conexão também contém muitos threads de segundo plano para monitorar o número de conexões, período de tempo limite, status e assim por diante.

Por padrão, o processo java precisa esperar que todos os encadeamentos terminem antes de terminar. Há um encadeamento especial chamado encadeamento daemon. Quando todos os encadeamentos não-daemon forem concluídos, mesmo que não seja concluído, será forçado a encerrar .

2) Envolvendo métodos
Chame o método setDaemon (true) do objeto thread para defini-lo como um thread daemon.

Marque o encadeamento como um encadeamento daemon ou um encadeamento do usuário. Quando os encadeamentos em execução são todos encadeamentos daemon, a máquina virtual Java sai. Este método deve ser chamado antes de iniciar o thread. Este método primeiro chama o método checkAccess do thread sem nenhum parâmetro. Isso pode lançar uma SecurityException (no segmento atual).

public final void setDaemon (boolean on)
Parâmetros: on-If true, marca a thread como uma thread daemon.
Lança:
IllegalThreadStateException-se o segmento estiver ativo.
SecurityException - se o segmento atual não puder modificar o segmento.

3) Objetivo do
daemon thread O daemon thread é geralmente usado para realizar algumas tarefas em segundo plano, como tocar música de fundo enquanto seu aplicativo está em execução e fazer a verificação gramatical automática e salvamento automático em um editor de texto.

A coleta de lixo do Java também é um encadeamento daemon. A vantagem da linha de guarda é que você não precisa se preocupar com seu final. Por exemplo, você deseja reproduzir música de fundo quando seu aplicativo está em execução. Se você definir o encadeamento que reproduz música de fundo como um encadeamento não daemon, quando o usuário solicitar para sair, não apenas deverá sair do encadeamento principal, mas também notificar a música de fundo a ser reproduzida O encadeamento sai; se for definido como um encadeamento daemon, não é necessário.

4.6. Parar o thread
1) Visão geral
Thread.stop (), Thread.suspend, Thread.resume, Runtime.runFinalizersOnExit Esses métodos de encerrar a execução do thread foram abandonados e é extremamente inseguro usá-los.

A maneira correta de interromper a discussão:

Primeiro: o método run é executado normalmente e, em seguida, termina.

Segundo: controle a condição do loop e o identificador da condição de julgamento para encerrar o encadeamento.

2) 实现 代码 示例
class MyThread extends Thread {int i = 0; boolean next = true; @Override public void run () {while (next) {if (i == 10) next = false; i ++; System.out.println (i); }}}

4.7. Interrupção de thread - interrupção
1) O que é interrupção (interrupção)? A
interrupção é apenas um mecanismo cooperativo. Java não adiciona nenhuma sintaxe para interromper. O processo de interrupção precisa ser implementado completamente pelos próprios programadores;

Cada objeto de thread possui um sinalizador, que é usado para indicar se o segmento está interrompido; o bit do sinalizador é verdadeiro para indicar interrupção e falso para indicar ininterrupto;

Defina o sinalizador do encadeamento como verdadeiro chamando o método de interrupção do objeto do encadeamento; ele pode ser chamado em outros encadeamentos ou em seu próprio encadeamento.

Sinalizador de interrupção: se o thread está interrompido, verdadeiro significa que está interrompido, falso significa que não

2) Envolvendo o método
isInterrupted ():

Obtenha a marca de interrupção do thread (verifique qual objeto de thread é chamado), a marca de interrupção do thread não será modificada após a chamada

método interrupt ():

Interrompa este encadeamento (qual objeto de encadeamento é chamado para interromper quem). Se o encadeamento que precisa ser interrompido estiver em um estado bloqueado (suspensão, espera, junção), seu estado interrompido será limpo e uma exceção (InterruptedException) será lançada. Esta interrupção não está realmente parando o encadeamento, mas configurando seu estado de interrupção para o estado "interrompido", o encadeamento continuará a ser executado, para saber como parar o encadeamento, ainda temos que pará-lo por nós mesmos, este método é apenas para parar o thread O estado de é definido como "parar", o que é verdade.

Interromper um encadeamento normal, o encadeamento não será interrompido, mas a interrupção do encadeamento é marcada como verdadeira.

método interrompido ():

Verifique se o thread atual está interrompido e use-o em conjunto com o método interrupt () acima. O status de interrupção do thread será apagado por este método, ou seja: se este método for chamado com sucesso duas vezes em sucessão, a segunda vez

A chamada retornará falso (a menos que o thread atual seja interrompido novamente após a primeira chamada e antes da segunda chamada).

Em outras palavras: limpe o sinalizador de interrupção após a chamada, ou seja, se for verdadeiro, o sinalizador de interrupção após a chamada é falso (não comumente usado)

4.8. Bloqueio de threads O bloqueio de
threads pode ser dividido em muitos tipos. A definição de bloqueio no nível do sistema operacional e no nível do Java pode ser diferente, mas em um sentido amplo, existem várias maneiras de bloquear threads:

1) Bloqueio de BIO, ou seja, um fluxo de IO bloqueador é usado

2) hibernar (muito tempo) deixa o thread dormir em um estado de bloqueio

3) a.join () O encadeamento que chama este método entra no bloqueio, esperando que um encadeamento termine de executar e retome a execução

4) sychronized ou ReentrantLock faz com que o thread entre no estado bloqueado sem adquirir o bloqueio

5) Chamar o método wait () após obter o bloqueio também fará com que a thread entre no estado de bloqueio

6) LockSupport.park () permite que o thread entre no estado de bloqueio

V. Resumo dos métodos principais de thread
5.1. Correspondência entre os seis estados e métodos de thread

Insira a descrição da imagem aqui

5.2. Resumo dos métodos de núcleo de thread
1) Métodos de núcleo na classe Thread
2) Métodos relacionados a thread em Objeto
Insira a descrição da imagem aqui

Resumindo
Insira a descrição da imagem aqui

Muitas pessoas têm entrevistado recentemente. Compilei várias cópias aqui: materiais Java multithreading, intervalo da família Spring (1187 páginas de documentos), materiais Java sistematizados: (incluindo os pontos de conhecimento mais recentes do Java em 2021, tópicos de entrevista em java e questões reais da Internet , e-books, etc. resumidos em 21 anos), amigos necessitados podem se juntar ao grupo QQ "748945508" para obtê-los gratuitamente.

Acho que você gosta

Origin blog.csdn.net/dcj19980805/article/details/114693244
Recomendado
Clasificación