Esto es suficiente para las preguntas de la entrevista de subprocesos múltiples de Java 2020, desde la creación de subprocesos hasta el análisis del grupo de subprocesos (con respuestas)

Prefacio

Un proceso se refiere a un programa de aplicación que se ejecuta en la memoria.Cada proceso tiene su propio espacio de memoria independiente, es decir, espacio de proceso o (espacio virtual). Los procesos no dependen de subprocesos, sino que existen de forma independiente y se pueden iniciar varios subprocesos en un proceso.
Subproceso se refiere a un flujo de ejecución en un proceso y se pueden ejecutar varios subprocesos en un proceso. Un subproceso siempre pertenece a un proceso determinado. El subproceso no tiene su propio espacio de direcciones virtuales. Comparte todos los recursos asignados al proceso con otros subprocesos en el proceso. Los subprocesos en el proceso unificado comparten una memoria de pila, y cada subproceso tiene su propia memoria de pila. La ejecución "simultánea" es una percepción humana, y la ejecución en realidad se rota entre hilos.

Sincrónico y asincrónico

Sincronización: ejecución en cola, ineficiente pero segura.
Asincrónico: ejecución sincrónica, alta eficiencia pero datos inseguros.

Simultaneidad y paralelismo

Simultaneidad: se refiere a dos o más eventos que ocurren en el mismo período de tiempo.
Paralelo: dos o más eventos ocurren al mismo tiempo (simultáneamente).

3 formas de creación de hilos

1. Método de herencia

public  class MyThread extends Thread{
    @Override
    public void run(){
    }
}

        MyThread m = new MyThread();
        m.start();

2. Método de interfaz

        //实现runnable
        //1 创建一个任务对象
        MyRunnable r = new MyRunnable();
        //创建一个线程并给他一个任务
        Thread t = new Thread(r);
        //启动线程
        t.start();

Ventajas de la interfaz:

En comparación con la herencia de subprocesos, la implementación de Runnable tiene las siguientes ventajas:
1. El subproceso múltiple se realiza creando tareas y luego asignando tareas a subprocesos, lo cual es más adecuado para la situación en la que varios subprocesos ejecutan tareas al mismo tiempo.
2. Puede evitar las limitaciones que trae la herencia única
3., Tareas y subprocesos están separados, lo que mejora la robustez del programa
4. La tecnología de grupo de subprocesos aprendida más tarde, acepta tareas de tipo Runnable pero no acepta subprocesos de tipo Thread.

    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("12345"+i);
                }
            }
        }.start();

Pero hay una forma muy simple de heredar Thread. Al reescribir run () por una clase interna anónima, el multithreading simplemente se realiza sin crear una nueva clase. Cada hilo tiene su propio espacio de pila y comparte una memoria de pila.
Un método llamado por un hilo y el método se ejecuta en este hilo.

3. Callable se da cuenta del retorno del estado del hilo (implementa la interfaz Callalble)

La interfaz Callalble admite la devolución del resultado de la ejecución. Debe llamar a FutureTask.get () para obtenerlo. Este método impedirá que el proceso principal continúe ejecutándose
. Si no se llama, no se bloqueará.

	Callable<Integer> callable= new MyCallable();
	FutureTask<Integer> future = new FutureTask<>(callable); 
	new Thread(future).start();
	Integer j=task.get();
	System.out.println("return"+j);

  1. Escriba una clase para implementar la interfaz Callable e implementar el método de llamada
class Mycallable implements Callable<T> { 
	@Override 
	public <T> call() throws Exception { 
	return T; 
	} 
} 

  1. Cree un objeto FutureTask y pase el objeto de clase invocable FutureTask escrito en el primer paso future = new FutureTask <> (invocable);
  2. A través de Thread, inicie el hilo new Thread (futuro) .start ();

Si se llama al método get, el subproceso principal espera a que se complete su ejecución antes de ejecutarse. Si no se llama, no tiene ningún efecto en el subproceso principal y se puede paralelizar.

Método FutureTask ()

Su método padre Future ()

Hilo de daemon e hilo de usuario

Los subprocesos se dividen en subprocesos de demonio y subprocesos de usuario. Subprocesos de
usuario: cuando un proceso no contiene ningún subproceso de usuario superviviente, finaliza.
Subprocesos de demonio: protegiendo los subprocesos de usuario. Cuando finaliza el último subproceso de usuario, todos los subprocesos de demonio mueren automáticamente.
El hilo predeterminado de Java es el hilo del usuario, y el hilo del demonio se establece llamando a setDaemon (verdadero) en el hilo.

        Thread t1 = new Thread(new MyRunnable());
        //设置守护线程
        t1.setDaemon(true);
        t1.start();

Tres métodos de sincronización de subprocesos

1. Sincronice el bloque de código, especifique el bloqueo como el mismo objeto

Formato: sincronizado (objeto de bloqueo) {}

public class Demo8 {
    public static void main(String[] args) {
        Object o = new Object();
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        private Object o = new Object();
        @Override
        public void run() {
                while (true) {
                    synchronized (o) {
                        if (count > 0) {
                         //卖票
                            System.out.println("正在准备卖票");
                            try {
                            Thread.sleep(1000);
                            } catch (InterruptedException e) {
                            e.printStackTrace();
                            }
                            count--;
                            System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                        }else {
                            break;
                        }

                }
            }
        }
    }
}

2. Método de sincronización

El método síncrono se llama en run (). Si es un método normal, use este (este objeto) dentro del método como bloqueo, si es un método estático, es el nombre de la clase.class (objeto de archivo de código byte)

        public synchronized boolean test(){
                System.out.println("测试");
            }

3. Bloqueo de pantalla

Tanto los bloques de código de sincronización como los métodos de sincronización son bloqueos implícitos.
Los bloqueos explícitos son más intuitivos, reflejan bloqueos orientados a objetos, autogenerados y autobloqueo y desbloqueo.

    static class Test implements Runnable{
        //总票数
        //参数为true表示公平锁    默认是false 不是公平锁
        private Lock l = new ReentrantLock(true);
        @Override
        public void run() {
            l.lock();
            System.out.println("测试");
            l.unlock();
        }
    }

Cerradura justa y cerradura injusta

Bloqueo justo: primero en llegar, primero en llegar, hacer cola juntos.
Bloqueo injusto: todos toman
Java de forma predeterminada. Es un bloqueo injusto de forma predeterminada. El bloqueo justo se puede realizar mediante el método de construcción en bloqueo explícito.

//设置公平锁
private Lock l= new ReentrantLock(true);

No es justo si no se pasa ningún parámetro, y falso de forma predeterminada, pero justo si se pasa en

Evitación de interbloqueo

En cualquier método que pueda causar bloqueos, no llame a otro método que pueda causar que los bloqueos generen otro bloqueo.
El siguiente es un ejemplo de interbloqueo:

public class Demo11 {
    public static void main(String[] args) {
        //线程死锁
        Culprit c = new Culprit();
        Police p = new Police();
        new MyThread(c,p).start();
        c.say(p);
    }

    static class MyThread extends Thread{
        private Culprit c;
        private Police p;
        MyThread(Culprit c,Police p){
            this.c = c;
            this.p = p;
        }

        @Override
        public void run() {
            p.say(c);
        }
    }
    static class Culprit{
        public synchronized void say(Police p){//两方法公用this(此对象)锁,第一个方法不执行完无法执行第二个方法
            System.out.println("罪犯:你放了我,我放了人质");
            p.fun();
        }
        public synchronized void fun(){
            System.out.println("罪犯被放了,罪犯也放了人质");
        }
    }
    static class Police{
        public synchronized void say(Culprit c){
            System.out.println("警察:你放了人质,我放了你");
            c.fun();
        }
        public synchronized void fun(){
            System.out.println("警察救了人质,但是罪犯跑了");
        }
    }
}

Punto muerto

Si el primer subproceso ejecuta (asumido) p.fun (), el segundo subproceso p no ha ejecutado el método p.say () y no se alcanza el bloqueo en este momento Ambos métodos se pueden ejecutar, pero se produce un error.

Productor y consumidor

1. Una clase se puede modificar y leer. Si no se utiliza la sincronización de subprocesos, se produce una operación de lectura de otro subproceso durante la modificación y se leerán los datos sucios.
2.Al agregar el método de sincronización de subprocesos sincronizados, el problema de leer datos sucios se puede resolver al no poder leer durante la modificación, pero no se puede lograr la sincronización entre los dos subprocesos y el fenómeno de apropiarse del segmento de tiempo de un subproceso no se puede sincronizar.
3. Si desea leer y modificar, puede esperar la sincronización entre sí, también debe agregar operaciones de espera y notificación. Una descansará después de ejecutar notificar a todos y establecer la bandera de identificación, y la otra leerá la bandera de identificación y la ejecutará. después de despertarse, y luego descansar, despertando al otro. Agregue la bandera de la bandera para determinar el orden de ejecución del hilo.

package com.java.demo;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo4  {

    /**
     * 多线程通信问题, 生产者与消费者问题
     * @param args
     */
    public static void main(String[] args) {
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }

    //厨师
    static class Cook extends Thread{
        private Food f;
        public Cook(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for(int i=0;i<100;i++){
                if(i%2==0){
                    f.setNameAndSaste("老干妈小米粥","香辣味");
                }else{
                    f.setNameAndSaste("煎饼果子","甜辣味");
                }
            }
        }
    }
    //服务生
    static class Waiter extends Thread{
        private Food f;
        public Waiter(Food f) {
            this.f = f;
        }
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }
    //食物
    static class Food{
        private String name;
        private String taste;

        //true 表示可以生产
        private boolean flag = true;

        public synchronized void setNameAndSaste(String name,String taste){
            if(flag) {
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
                flag = false;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public synchronized void get(){
            if(!flag) {
                System.out.println("服务员端走的菜的名称是:" + name + ",味道:" + taste);
                flag = true;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Estado del hilo

Estado del hilo. Un subproceso puede estar en uno de los siguientes estados: Un subproceso que no ha sido iniciado por
NEW se encuentra
en este estado.
Los
subprocesos RUNNABLE ejecutados en la máquina virtual Java están en este estado.
BLOCKED
está bloqueado esperando el subproceso de bloqueo del monitor en este estado.
WAITING
esperando indefinidamente a que otro hilo realice una operación específica hilo en este estado.
TIMED_WAITING Un
subproceso que está esperando a que otro subproceso realice operaciones hasta el tiempo de espera especificado se encuentra en este estado.
Los
subprocesos salidos TERMINADOS están en este estado.
Un hilo solo puede estar en un estado en un momento dado. Estos estados son estados de máquinas virtuales y no reflejan ningún estado de subproceso del sistema operativo.

Grupo de hilos

1. Grupo de subprocesos de caché

Longitud ilimitada
El flujo de ejecución después de que se agrega la tarea:
1. Determine si hay un subproceso inactivo en el grupo de subprocesos
2. Si existe, utilícelo
3. Si no existe, cree un subproceso y utilícelo


        ExecutorService service = Executors.newCachedThreadPool();
        //指挥线程池执行新的任务
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"测试");
            }

        });

2. Grupo de hilos de longitud fija

La longitud es el grupo de subprocesos especificado
. El flujo de ejecución después de unirse a la tarea:
1 Determine si hay un subproceso inactivo en el grupo de subprocesos
2 Si existe, utilice
3 Si no hay subproceso inactivo y el grupo de subprocesos no está lleno, cree un subproceso y ponerlo en el grupo de subprocesos y usar
4 Si no hay subproceso inactivo y el grupo de subprocesos está lleno, espere el subproceso inactivo del grupo de subprocesos

		//设置定长线程池
        ExecutorService service = Executors.newFixedThreadPool(2);
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"测试");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });

3. Grupo de hilos de un solo hilo

El efecto es el mismo que un grupo de hilos de longitud fija con una longitud de 1.
Proceso de ejecución
1 Determine si el hilo en el grupo de hilos está inactivo
2 Si está inactivo, utilícelo
3 Si no está inactivo, espere a que esté inactivo antes de usarlo

        ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"测试");
            }
        });

4. Grupo de subprocesos de longitud fija de período

执行流程
    1 判断线程池是否存在空闲线程
    2 存在则使用
    3 不存在空闲线程  且线程池未满的情况下  则创建线程  并放入线程池中  然后使用
    4 不存在空闲线程  且线程池已满的情况下  则等待线程池的空闲线程

Ejecución periódica de tareas:
ejecuta una determinada tarea automáticamente cuando se activa una determinada tarea

Ejecutar una vez:

        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
        //定时执行一次
        //参数1:定时执行的任务
        //参数2:时长数字
        //参数3:2的时间单位    Timeunit的常量指定
       	scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"测试");
            }
        },5, TimeUnit.SECONDS);      //5秒钟后执行*/

Ejecución periódica

        周期性执行任务
            参数1:任务
            参数2:延迟时长数字(第一次在执行上面时间以后)
            参数3:周期时长数字(没隔多久执行一次)
            参数4:时长数字的单位
        * **/
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"测试");
            }
        },5,1,TimeUnit.SECONDS);

Expresión lambda

Al usar clases internas anónimas como parámetros, puede usar la notación lambda para simplificar enormemente el código.

        //冗余的Runnable编写方式
       Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("测试");
            }
        });
        t.start();

Mantenga los parámetros pasados, mantenga el cuerpo del método para reescribirlo, use -> conectar en el medio

        Thread t = new Thread(() -> System.out.println("测试"));

para resumir

Finalmente, preparé los materiales de aprendizaje de arquitectura Java para todos. El contenido de tecnología de aprendizaje incluye: Spring, Dubbo, MyBatis, RPC, análisis de código fuente, alta concurrencia, alto rendimiento, distribuido, optimización del rendimiento, desarrollo de arquitectura avanzada de microservicios, etc. ¡Amigos necesitados, haga clic aquí para comentar csdn y descargarlo usted mismo ! Otro recordatorio, la revisión completa es la clave para eliminar su estado mental nervioso, pero si revisa lo suficiente, naturalmente tendrá más confianza en el proceso de la entrevista.

Supongo que te gusta

Origin blog.csdn.net/jiagouwgm/article/details/111676667
Recomendado
Clasificación