Multihilo 2 Comprensión y uso de multihilo

Hay dos tipos de bloqueos sincronizados

  1. objeto de clase
  2. instancia de clase

El primer tipo: bloquear objetos, hay dos formas, de la siguiente manera:

// 方法一:synchronized 修饰static方法
public static synchronized void test(){
    
    
    System.out.println(Thread.currentThread().getName()+" start ");
    try {
    
    
        TimeUnit.SECONDS.sleep(2);
    } catch (Exception e) {
    
    
        e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName()+" end ");
}
// 方法二:synchronized锁class对象
public void test2(){
    
    
    synchronized (getClass()) {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(2);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" run ");
    }
}

public static void main(String[] args) {
    
    
    TestThread10 t = new TestThread10();
    new Thread(()->TestThread10.test(),"线程 1 ").start();
    new Thread(()->t.test2(),"线程 2 ").start();
}

El resultado es el siguiente:

resultado 1

En el subproceso 1, es decir, inicie el subproceso 1 primero y espere a que finalice el subproceso 1 antes de ejecutar el subproceso 2;

La clase de bloqueo puede entenderse como la adición de un bloqueo al objeto de la clase, y todos los métodos bloqueados deben esperar a que se libere el bloqueo anterior antes de la ejecución.

El segundo tipo: una instancia de una clase.

La sincronización de subprocesos solo se puede lograr cuando el mismo objeto de instancia está bloqueado, de la siguiente manera:

public void test2(){
    
    
    synchronized (this) {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(2);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" run ");
    }
}

captura de excepción sincronizada

Cuando se usa sincronizado, se debe manejar una excepción, de lo contrario liberará automáticamente el bloqueo.Su
mecanismo es bloquear manualmente y liberar automáticamente el bloqueo. Veamos un ejemplo, donde se debe manejar la excepción; de lo contrario, se liberará el subproceso 1 en el código a continuación.

private Integer c = 0;

@Override
public void run() {
    
    
  count();
}

private synchronized void count(){
    
    
    System.out.println(Thread.currentThread().getName()+" start。。。");
    while (true) {
    
    
        System.out.println(Thread.currentThread().getName()+" count="+c++);
        try {
    
    
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        if (c == 5) {
    
    
            int i = 1/0;
        }
    }

}

public static void main(String[] args) {
    
    
    TestThread3 t = new TestThread3();
    Thread t1 = new Thread(t, "线程 1");
    Thread t2 = new Thread(t, "线程 2");
    t1.start();
    t2.start();
}


gráfico 2

hilo principal y hilo secundario

Los subprocesos se dividen en subprocesos de usuario y subprocesos de demonio ;

El método principal es en realidad un subproceso principal.Después de que el sistema operativo inicia java.exe, inicia un proceso, y luego el proceso inicia el subproceso principal, y el subproceso principal inicia otros subprocesos.

  • Subproceso del demonio: termina junto con el subproceso principal (el subproceso principal termina y el subproceso del demonio también termina)
  • Subprocesos de usuario (subprocesos no daemon): pero todos los subprocesos de usuario finalizan y el subproceso principal finaliza
t2.setDaemon(true);
t2.start();

El papel de los volátiles

Puede ver el blog de este blogger: Explicación detallada de la palabra clave volátil en Java - Blog de Zheng Bin - Blog Garden (cnblogs.com)

Tiene dos funciones:

  1. Visibilidad entre hilos
  2. evitar el reordenamiento de instrucciones

Aviso:

La visibilidad no significa atomicidad, solo permite que otros subprocesos vean el valor más reciente en tiempo real, mientras que otras operaciones no las garantiza.

Reordenación de instrucciones, esto es un poco esotérico, puede ignorarlo temporalmente

notificar es uno de los subprocesos de espera iniciados aleatoriamente

notificar es uno de los subprocesos en espera que se inician aleatoriamente, y no tiene nada que ver con la prioridad del subproceso, y los métodos de espera y notificación deben usarse en el mismo bloqueo; otro punto es que lock.wait cederá el bloqueo a aquellos que lo necesita después de bloquear El subproceso, sin embargo, llama a lock.notify() después de que otros subprocesos hayan terminado de ejecutarse para activar el subproceso en espera, pero el código en el bloqueo actual no ha terminado de ejecutarse y el bloqueo no se liberará.

Simulación de escena simple:

Un contenedor síncrono de capacidad fija con métodos put y get y un método getCount que puede admitir el bloqueo de llamadas de dos subprocesos productores y 10 subprocesos consumidores.

public class TestThread8 {
    
    
private final LinkedList list = new LinkedList();

private final int MAX = 10;

private int count = 0;

public synchronized void put(Object o) {
    
    
    while (list.size() == MAX) {
    
    
        try {
    
    
            // 在这里等待;的那个调用notify时会从这里继续执行
            this.wait();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
    list.add(o);
    count++;
    // 启动所有线程,包括生产者,随机的
    this.notifyAll();
}

public synchronized void get() {
    
    
    while (list.size() == 0) {
    
    
        try {
    
    
            this.wait();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
    list.removeLast();
    count--;
    this.notifyAll();
}

public int getCount() {
    
    
    return list.size();
}

public static void main(String[] args) {
    
    
    TestThread8 t = new TestThread8();

    for (int i = 0; i < 2; i++) {
    
    
        new Thread(() -> {
    
    
            int j = 0;
            while (true) {
    
    
                t.put(Thread.currentThread().getName() + " put " + t.getCount());
                System.out.println(Thread.currentThread().getName() + " put " + t.getCount());
            }
        }).start();
    }

    for (int i = 0; i < 10; i++) {
    
    
        new Thread(() -> {
    
    
            while (true) {
    
    
                t.get();
                System.out.println(Thread.currentThread().getName() + " get " + t.getCount());
            }
        }).start();
    }
}
}

Supongo que te gusta

Origin blog.csdn.net/qq_28911061/article/details/129348273
Recomendado
Clasificación