Una introducción detallada y fácil de entender a multithreading 3

Este artículo ha participado en el evento "Ceremonia de creación de recién llegados" para comenzar juntos el camino de la creación de oro.

Introducción a esta sección

  Esta sección presenta principalmente el fenómeno de Bloqueo y interbloqueo. Los estudiantes que necesitan comprender la diferencia entre procesos y subprocesos, las razones de los problemas de concurrencia y la palabra clave sincronizada pueden leer los dos primeros artículos de esta columna.

1.Bloqueo

Después de   jdk5.0 , se proporciona un bloqueo más potente y fácil de controlar, es decir, la interfaz de bloqueo. Puede bloquear y liberar bloqueos manualmente usando Bloquear, lo que hace que el proceso de bloqueo sea más controlable. A través de Lock, también puede saber si el subproceso ha adquirido correctamente el bloqueo, y también puede permitir que varios subprocesos completen la operación de lectura en una clase al mismo tiempo, en lugar de bloquear completamente una clase en una habitación pequeña como sincronizada, y no permitir que otros subprocesos realicen ninguna operación. .
  En primer lugar, debe saber que sincronizado es una palabra clave en Java, pero Lock es una interfaz. Antes de usarlo, primero debe crear un objeto Lock. La clase de implementación de la interfaz de bloqueo más utilizada es ReentrantLock (bloqueos reentrantes, la introducción detallada de bloqueos reentrantes, AQS, bloqueos justos, etc. se mostrarán en los capítulos siguientes), este artículo solo presenta esto.
  Eche un vistazo a la demostración a continuación, definí un ReentrantLock y usé los métodos de bloqueo y desbloqueo más básicos para completar el bloqueo y el desbloqueo.

public class LearnThread implements Runnable {
    private static int count=0;
    private ReentrantLock lock=new ReentrantLock();
    @Override
    public  void run() {
            lock.lock();
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + " 阻塞的 " + (count++));
            }
            lock.unlock();//你可以尝试在其他时机解锁,比如i==5时,来观察发生的情况。
    }

    public static void main(String[] args) {
        LearnThread t1 = new LearnThread();
        new Thread(t1, "线程1").start();
        new Thread(t1, "线程2").start();
    }
}
复制代码

  La forma de usar Lock aquí no es estándar, debemos asegurarnos de que Lock siempre se liberará, generalmente el código que se bloqueará se incluye en el bloque de código de prueba junto con la línea lock.lock(), y el código para estar bloqueado se incluye en el bloque de prueba La declaración lock.unlock() está escrita en la sección finalmente.
  También puede usar el método tryLock para intentar adquirir el bloqueo y manejarlo de manera diferente para el éxito o el fracaso.

public class LearnThread implements Runnable {
    private static int count=0;
    private ReentrantLock lock=new ReentrantLock();
    @Override
    public  void run() {
            if(lock.tryLock()) {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + " 阻塞的 " + (count++));
                }
                lock.unlock();
            }
            else{
                System.out.println(Thread.currentThread().getName() + " 获取锁失败! ");
            }
    }

    public static void main(String[] args) {
        LearnThread t1 = new LearnThread();
        new Thread(t1, "线程1").start();
        new Thread(t1, "线程2").start();
    }
}
复制代码

  Esto da una salida como esta en mi computadora

线程1 阻塞的 0
线程1 阻塞的 1
线程1 阻塞的 2
线程2 获取锁失败! 
线程1 阻塞的 3
线程1 阻塞的 4
复制代码

  Puede ver claramente que el subproceso 2 intentó adquirir el bloqueo en el medio, pero falló, ejecutó la política de falla (imprimiendo una oración "Error al adquirir el bloqueo") y luego se destruyó.

2. Fenómeno de punto muerto

  想象一个这样的场景,你和你的哥哥在家里,都想给自己做一杯美味的香蕉奶昔。可是家里只剩下一根香蕉和一杯牛奶了。你率先拿到了香蕉,而你哥哥占有了牛奶。现在你们两僵持不下,彼此都不想让步,情况一直这样持续下去,你们都没法喝到美味的香蕉奶昔。
  线程之间也会出现这样的情况,看看下面这个demo

class A{
    public static int n=10;
}
class B{
    public static int n=5;
}
public class LearnThread implements Runnable {
    private final static A a=new A();
    private final static B b=new B();
    @Override
    public  void run() {
        if(Thread.currentThread().getName().equals("线程1")) {
            synchronized (a) {
                System.out.println(Thread.currentThread().getName() + " 拿到了a ");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b) {
                    System.out.println(Thread.currentThread().getName() + " 拿到了b ");
                }
                System.out.println(a.n + b.n);
            }

        }else{
            synchronized (b) {
                System.out.println(Thread.currentThread().getName() + " 拿到了b ");
                synchronized (a) {
                    System.out.println(Thread.currentThread().getName() + " 拿到了a ");
                }
                System.out.println(a.n + b.n);
            }


        }
    }

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

}
复制代码

  我们的类t1里只有一个A和一个B,我们首先让线程1拿到了a,再用Threa.sleep函数强制让它等待0.1秒,这个间隙里,线程2拿到了b。现在它们都想拿到对方手上的资源,很可惜,我们没有设置让步策略,它们会这样持续下去,直到你自己中断程序。
  输出结果如下:

线程1 拿到了a 
线程2 拿到了b 
复制代码

  死锁的其他知识点将在面经篇中展示,这里只对概念做一个基本介绍。

Supongo que te gusta

Origin juejin.im/post/7083857630606655518
Recomendado
Clasificación