Programação simultânea Java - só há uma maneira de implementar threads

A operação multithread sempre foi a principal prioridade da tecnologia de back-end. Para um desenvolvedor Java, a familiaridade com a simultaneidade multithread é uma operação básica. No ambiente de produção, geralmente há atividades de pico e a competição multiencadeada é essencial.

Quando tento, costumo perguntar sobre multi-threading. No combate real, muitas vezes há multi-threads competindo por recursos ... Recentemente, o pico no Moutai é muito popular. A essência é que vários threads agarram um Moutai, mas alguns as pessoas usam a captura manual. Algumas pessoas usam o método de captura de script. Claro, só tenho uma garrafa de Moutai na mão. Naturalmente, não posso vender mais do que uma dúzia de garrafas de Moutai. Isso está relacionado à questão da segurança multi-threading.

A seguir, vamos dar uma olhada nas várias maneiras de implementar threads e as diferenças entre elas. Para dar uma conclusão primeiro, só há realmente uma maneira de implementar threads.
(Existem super benefícios no artigo)

Implementar a interface executável

class MyThread implements Runnable {   // 定义线程主体类
    private String name;       // 定义类中的属性
    public MyThread(String name) {    // 定义构造方法
      this.name = name;
    }
    @Override
    public void run() {        // 覆写run()方法
        for (int x = 0; x < 200; x++) {
          System.out.println(this.name + " --> " + x);
      }
    }
}

Implemente primeiro a interface Runnable por meio da classe MyThread e, em seguida, reescreva o método run (). Depois disso, você só precisa passar a instância MyThread que implementa o método run () para a classe Thread para obter multithreading.

Como executar o tópico Runnable:

MyThread a = new MyThread();
new Thread(a).start();

Herdar a classe Thread

class MyThread extends Thread {   // 这就是一个多线程的操作类
    private String name ;     // 定义类中的属性
    public MyThread(String name) {  // 定义构造方法
      this.name = name ;
   }
   @Override
   public void run() {      // 覆写run()方法,作为线程的主操作方法
      for (int x = 0 ; x < 200 ; x ++) {
         System.out.println(this.name + " --> " + x);
      }
   }
}

A diferença do primeiro método é que ele não implementa a interface, mas herda a classe Thread e reescreve o método run (). Eu acredito que você deve estar bem familiarizado com os dois métodos acima, e freqüentemente os usa em seu trabalho.

A partir da definição da classe Thread, podemos ver que a classe Thread também é uma subclasse da interface Runnable:

public class Thread extends implements Runnable

Portanto, existem duas maneiras de iniciar um thread de discussão:

new MyThread().start();
MyThread a = new MyThread();
new Thread(a).start();

Se precisar de mais materiais para entrevistas de grandes empresas, você também pode clicar para entrar diretamente e obtê-los gratuitamente! Senha: CSDN

Chamável com valor de retorno cria thread

Deixe-me falar sobre java.lang.Runnable primeiro. É uma interface e apenas um método run () é declarado nela:

public interface Runnable {
    public abstract void run();
}

Nem Thread nem Runnable podem retornar um valor, que é sua deficiência comum. Callable foi proposto após JDK1.5.

  1. A interface Callable é mais como uma versão aprimorada da interface Runnable. Em comparação com a interface Runable, o método Call () adiciona a capacidade de capturar e lançar exceções; o método Call () pode retornar valores
  2. A interface Future fornece uma classe de implementação FutureTask. A classe FutureTaks é usada para armazenar o valor de retorno do método Call () e serve como o destino da classe Thread.
  3. Chame o método get () de FutureTask para obter o valor de retorno
class CallableTask implements Callable<Integer> {

    @Override

    public Integer call() throws Exception {

        return new Random().nextInt();

    }

}

No entanto, não há como aceitar objetos de instância Callable na classe Thread. Após implementar Callable, você precisa usar a classe FutureTask. Após JDK1.5, Java fornece java.util.concurrent.FutureTask.

Vamos dar uma olhada nos dois métodos de construção de FutureTask:

public FutureTask(Callable<V> callable)
public FutureTask(Runnable runnable,V result)

A estrutura básica de herança da classe é mostrada na figura. Pode-se descobrir que FutureTask implementa a interface RunnableFuture e RunnableFuture implementa as interfaces Future e Runnable.
Insira a descrição da imagem aqui
Portanto, seja Callable ou FutureTask, elas são antes de tudo uma tarefa como Runnable e precisam para ser executado., Não que eles próprios sejam threads. Eles podem ser colocados no pool de threads para execução, não importa qual método seja usado, eles são, em última análise, executados por threads, e a criação de threads filhas ainda é inseparável dos dois métodos básicos mencionados no início, ou seja, para implementar o Interface executável e herdam a classe Thread.

//创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//提交任务,并用 Future提交返回结果
Future<Integer> future = service.submit(new CallableTask());

Thread de criação de pool de threads

O pool de threads implementa multithreading. Por exemplo, se definirmos o número de threads no pool de threads como 10, haverá 10 threads filho para trabalhar para nós. Em seguida, analisaremos o código-fonte no pool de threads em profundidade para ver se o pool de threads é Como implementar threads?

static class DefaultThreadFactory implements ThreadFactory {

    DefaultThreadFactory() {

        SecurityManager s = System.getSecurityManager();

        group = (s != null) ? s.getThreadGroup() :

            Thread.currentThread().getThreadGroup();

        namePrefix = "pool-" +

            poolNumber.getAndIncrement() +

            "-thread-";
    }


    public Thread newThread(Runnable r) {

        Thread t = new Thread(group, r,

                    namePrefix + threadNumber.getAndIncrement(),

0);

        if (t.isDaemon())

            t.setDaemon(false);

        if (t.getPriority() != Thread.NORM_PRIORITY)

            t.setPriority(Thread.NORM_PRIORITY);

        return t;

    }

}

Para o conjunto de encadeamentos, o encadeamento é criado basicamente através da fábrica de encadeamentos. DefaultThreadFactory é usado por padrão. Ele definirá alguns valores padrão para os encadeamentos criados pelo conjunto de encadeamentos, como o nome do encadeamento, um daemon thread e a prioridade do thread, etc. Mas não importa como você define essas propriedades, ele eventualmente cria um thread por meio de new Thread (), mas o construtor aqui leva mais parâmetros. Pode-se ver que a criação de um thread por meio do pool de threads não se desvia do original. Os dois os métodos básicos de criação são essencialmente implementados por meio de new Thread ().

Portanto, quando respondermos à questão da implementação do thread, depois de descrever os dois primeiros métodos, podemos estender ainda mais que "Eu também sei que o pool de threads e Callable também podem criar threads, mas eles são essencialmente criação de threads por meio dos dois primeiros métodos básicos "Essa resposta se tornará um item de bônus na entrevista.

Se precisar de mais materiais para entrevistas de grandes empresas, você também pode clicar para entrar diretamente e obtê-los gratuitamente! Senha: CSDN

Resumo 1: há apenas uma maneira de implementar threads

Em relação a este problema, não vamos nos concentrar em por que há apenas uma maneira de criar um thread. Primeiro, pensamos que existem duas maneiras de criar um thread. Outros métodos de criação, como pools de thread ou temporizadores, estão fora do novo Thread (). Fez uma camada de encapsulamento. Se chamarmos isso de uma nova forma, a forma de criar threads será sempre mutável e infinita. Por exemplo, quando o JDK é atualizado, ele pode ter mais algumas classes, que serão chamar new Thread () O reencapsulamento, na superfície, será uma nova maneira de implementar threads. Olhando para a essência através do fenômeno, após abrir o encapsulamento, você descobrirá que eles são finalmente implementados com base na interface Runnable ou herdada a classe Thread.

Resumo 2: Implementar a interface Runnable é melhor do que herdar a classe Thread para implementar threads

Vamos comparar as duas maneiras de implementar o conteúdo do thread que acabamos de mencionar, por isso é melhor implementar a interface Runnable do que herdar a classe Thread para implementar o thread? Qual é a coisa boa?

  1. Realize o desacoplamento das classes Runnable e Thread. Existe apenas um método run () em Runnable, que define o conteúdo que deve ser executado.Neste caso, a classe Thread é responsável pela inicialização do thread e configuração de propriedade, etc., com direitos e responsabilidades claros.
  2. Melhorar o desempenho. Usando o método de herança da classe Thread, você precisa criar um novo thread independente cada vez que realizar uma tarefa. Se quiser realizar esta tarefa, você deve criar uma nova classe que herde a classe Thread. Todo o thread é criado a partir de o início ao fim da execução é destruído.Esta série de operações é muito mais cara do que o método run () para imprimir o próprio texto, que equivale a pegar sementes de gergelim e perder melancia, o que não vale a pena perder. Se implementarmos a interface Runnable, podemos transferir tarefas diretamente para o pool de threads e usar alguns threads fixos para concluir as tarefas.Não há necessidade de criar e destruir threads todas as vezes, o que reduz muito a sobrecarga de desempenho.
  3. A linguagem Java não suporta herança dupla. Uma vez que nossa classe herda a classe Thread, ela não será capaz de herdar outras classes no futuro. Desta forma, se esta classe precisar herdar outras classes para alcançar alguma expansão funcional no futuro , vai Não há como fazer isso, o que equivale a limitar a escalabilidade futura do código.

Em resumo, devemos primeiro escolher criar threads implementando a interface Runnable.

Resumo 3: Por que a inicialização multi-thread não está chamando run (), mas start ()

public synchronized void start() {
    /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
     */
     // 没有初始化,抛出异常
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
 
    /* Notify the group that this thread is about to be started
     * so that it can be added to the group's list of threads
     * and the group's unstarted count can be decremented. */
    group.add(this);
 // 是否启动的标识符
    boolean started = false;
    try {
     // start0() 是启动多线程的关键
     // 这里会创建一个新的线程,是一个 native 方法
     // 执行完成之后,新的线程已经在运行了
        start0();
        // 主线程执行
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

O código fonte do método start não tem poucas linhas de código, e os comentários são mais detalhados, o mais importante é o método start0 (), que será explicado mais tarde. Vamos dar uma olhada no código-fonte do método run ():

 @Override
    public void run() {
     // 简单的运行,不会新起线程,target 是 Runnable
        if (target != null) {
            target.run();
        }
    }

O código-fonte do método run () é relativamente simples, apenas uma chamada para um método comum, o que também confirma nossa conclusão acima.

A seguir, vamos falar sobre o método start0 (), que é a chave para multithreading real. O código para start0 () é o seguinte:

private native void start0();

start0 é marcado como nativo, ou seja, um método local, que não requer implementação ou compreensão.

Depois que o método start () chama o método start0 (), o encadeamento não é necessariamente executado imediatamente, mas o transforma em um estado executável. O tempo de execução específico depende da CPU, e a CPU é programada de maneira uniforme.

Também sabemos que Java é multiplataforma e pode ser executado em sistemas diferentes. O algoritmo de escalonamento da CPU de cada sistema é diferente, portanto, diferentes processamentos precisam ser feitos. Essa questão só pode ser alcançada pela JVM, start0 () O método é naturalmente marcado como nativo.

Realizar o verdadeiro multithreading em Java é o método start0 () no início, e o método run () é apenas um método comum.

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 as 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) 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/114078576
Recomendado
Clasificación