Notas de estudio de JAVA: subprocesos múltiples

1. Conceptos básicos

  • Tarea
  • proceso
    • El programa que se ejecuta en el sistema operativo es un proceso. Un proceso es un proceso de ejecución del programa. Es una unidad conceptual dinámica de asignación de recursos del sistema.
    • Por lo general, un proceso puede contener varios subprocesos. Por supuesto, hay al menos un subproceso en un proceso; de lo contrario, no tiene sentido que exista. Los subprocesos son la unidad de programación y ejecución de la CPU.
  • hilo
    • Los subprocesos son rutas de ejecución independientes.
    • Cuando el programa se está ejecutando, incluso si no crea un hilo usted mismo, habrá varios hilos en segundo plano, como el hilo principal y el hilo GC.
    • main() se denomina hilo principal, que es el punto de entrada del sistema y se utiliza para ejecutar todo el programa.
    • En un proceso, si se abren varios subprocesos, la ejecución de los subprocesos la programa el programador, que está estrechamente relacionado con el sistema operativo y los humanos no pueden interferir en el orden.
    • Cuando se opera con el mismo recurso, habrá un problema de acaparamiento de recursos y es necesario agregar control de concurrencia.
    • Los subprocesos generarán una sobrecarga adicional, como el tiempo de programación de la CPU y la sobrecarga del control de concurrencia.
    • Cada hilo interactúa en su propia memoria de trabajo y un control inadecuado de la memoria provocará inconsistencia en los datos.

2. Creación de hilos

2.1 Heredar la clase Thread (puntos clave)

  1. Clase de hilo personalizada, heredar la clase de hilo
  2. Reescribe el método run() y escribe el cuerpo de ejecución del hilo.
  3. Cree un objeto de hilo en la función principal y llame al método start() para iniciar el hilo.

Nota: Es posible que el subproceso no se ejecute inmediatamente cuando se inicia, sino que está programado para ser ejecutado por la CPU.

//创建线程方式一:继承Thread类,重写run方法,调用start开启线程
public class TestThread extends Thread {
    
    
    @Override
    public void run() {
    
    
        //run方法线程方法体
        for (int i = 0; i < 20; i++) {
    
    
            System.out.println("我在看代码----" + i);
        }
    }

    public static void main(String[] args) {
    
    
        //创建一个线程对象
        TestThread testThread = new TestThread();
        //调用start()方法开启线程
        testThread.start();
        //main线程,主线程
        for (int i = 0; i < 200; i++) {
    
    
            System.out.println("我在学习多线程----" + i);
        }
    }
}

Caso: Descarga de imagen

//练习Thread,实现多线程同步下载图片
public class TestThread2 extends Thread{
    
    
    private String url; //网络图片地址
    private String name; //保存的文件名
    public TestThread2(String url,String name){
    
    
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {
    
    
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了文件名为:"+name);
    }

    public static void main(String[] args) {
    
    
        TestThread2 t1 = new TestThread2("https://www.baidu.com/img/bdlogo.png","baidu1.jpg");
        TestThread2 t2 = new TestThread2("https://www.baidu.com/img/bdlogo.png","baidu2.jpg");
        TestThread2 t3 = new TestThread2("https://www.baidu.com/img/bdlogo.png","baidu3.jpg");

        t1.start();
        t2.start();
        t3.start();
    }
}
class WebDownloader{
    
    
    //下载方法
    public void downloader(String url,String name){
    
    
        try {
    
    
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
    
    
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}

2.2 Implementar la interfaz Runnable (puntos clave)

  1. Clase de hilo personalizada, implementar interfaz Runnable
  2. Reescribe el método run() y escribe el cuerpo de ejecución del hilo.
  3. El hilo de ejecución debe lanzarse a la clase de implementación de la interfaz ejecutable y llamar al método start().
//创建线程方式2:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
public class TestThread3 implements Runnable{
    
    
    @Override
    public void run() {
    
    
        //run方法线程方法体
        for (int i = 0; i < 20; i++) {
    
    
            System.out.println("我在看代码----" + i);
        }
    }

    public static void main(String[] args) {
    
    
        //创建一个runnable接口的实现类对象
        TestThread3 testThread3 = new TestThread3();
        //创建线程对象,通过线程对象来开启我们的线程,代理
        //Thread thread = new Thread(testThread3);
        //thread.start();
        //调用start()方法开启线程
        new Thread(testThread3).start();
        //main线程,主线程
        for (int i = 0; i < 200; i++) {
    
    
            System.out.println("我在学习多线程----" + i);
        }
    }
}

Comparación de los dos métodos anteriores:

Heredar la clase Thread

  • La subclase hereda la clase Thread y tiene capacidades de subprocesos múltiples.
  • Hilo de inicio: subclase object.start()
  • En desuso: evitar las limitaciones de la herencia única de programación orientada a objetos
  • Implementar la interfaz ejecutable

Implementar interfaz ejecutable

  • Tiene capacidad de subprocesos múltiples
  • Inicie el hilo: pase el objeto de destino + Thread object.start()
    • nuevo hilo(objeto).start();
  • Uso recomendado: evitar las limitaciones de la herencia única y facilitar el uso del mismo objeto por múltiples subprocesos.

Problemas de concurrencia

//多个线程同时操作同一个对象
//买火车票的例子
//发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
public class TestThread4 implements Runnable{
    
    
    //票数
    private int ticketNums = 10;
    @Override
    public void run() {
    
    
        while (true){
    
    
            if(ticketNums<=0){
    
    
                break;
            }
            //模拟延时
            /*try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
        }
    }

    public static void main(String[] args) {
    
    
        TestThread4 testThread4 = new TestThread4();
        new Thread(testThread4,"张三").start();
        new Thread(testThread4,"李四").start();
        new Thread(testThread4,"王五").start();
    }
}

Caso: Tortuga y Liebre

  1. Primero, establezcamos la distancia de la pista y luego acerquémonos cada vez más a la línea de meta.
  2. Determinar si el juego ha terminado.
  3. Imprime el ganador
  4. Comienza la tortuga y la liebre.
  5. En la historia, la tortuga gana y el conejo necesita dormir, así que simulamos que el conejo duerme.
  6. Finalmente, la tortuga ganó la carrera.
//模拟龟兔赛跑
public class Race implements Runnable {
    
    

    //胜利者
    private static String winner;

    @Override
    public void run() {
    
    
        for (int i = 0; i <= 100; i++) {
    
    
            //模拟兔子休息
            if(Thread.currentThread().getName().equals("兔子")&&i%10==0){
    
    
                try {
    
    
                    Thread.sleep(1);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
            //判断比赛是否结束
            boolean flag = gameOver(i);
            //如果比赛结束,就停止程序
            if (flag) {
    
    
                break;
            }
            System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "步");
        }
    }

    //判断是否完成比赛
    private boolean gameOver(int steps) {
    
    
        //判断是否有胜利者
        if (winner != null) {
    
    //已经存在胜利者了
            return true;
        }
        {
    
    
            if (steps >= 100) {
    
    
                winner = Thread.currentThread().getName();
                System.out.println("胜利者是" + winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
    
    
        Race race = new Race();
        new Thread(race, "兔子").start();
        new Thread(race, "乌龟").start();
    }
}

2.3 Implementar la interfaz invocable (comprender)

Implemente la interfaz invocable y anule el método de llamada.

  1. La implementación de la interfaz invocable requiere un tipo de valor de retorno
  2. Anula el método de llamada y necesita lanzar una excepción
  3. Crear objeto de destino
  4. Cree un servicio de ejecución: ExecutorService = Executor.newFixedThreadPool(1);
  5. Enviar ejecución: Resultado futuro1 = ser.submit(1);
  6. Obtenga el resultado: booleano r1 = result.get()
  7. Cerrar el servicio: ser.shutdownNow():

ventaja:

  1. El valor de retorno se puede definir.
  2. Puede lanzar excepciones
//线程创建方式3,实现callable接口
public class TestCallable implements Callable<Boolean> {
    
    
    private String url; //网络图片地址
    private String name; //保存的文件名

    public TestCallable(String url, String name) {
    
    
        this.url = url;
        this.name = name;
    }

    @Override
    public Boolean call() {
    
    
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url, name);
        System.out.println("下载了文件名为:" + name);
        return true;
    }

    public static void main(String[] args) throws Exception{
    
    
        TestCallable t1 = new TestCallable("https://www.baidu.com/img/bdlogo.png", "baidu1.jpg");
        TestCallable t2 = new TestCallable("https://www.baidu.com/img/bdlogo.png", "baidu2.jpg");
        TestCallable t3 = new TestCallable("https://www.baidu.com/img/bdlogo.png", "baidu3.jpg");

        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> r1 = ser.submit(t1);
        Future<Boolean> r2 = ser.submit(t2);
        Future<Boolean> r3 = ser.submit(t3);

        //获取结果
        boolean rs1 = r1.get();
        boolean rs2 = r2.get();
        boolean rs3 = r3.get();
        System.out.println(rs1);
        System.out.println(rs2);
        System.out.println(rs3);

        //关闭服务
        ser.shutdown();
    }
}

//下载器
class WebDownloader {
    
    
    //下载方法
    public void downloader(String url, String name) {
    
    
        try {
    
    
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
    
    
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}

2.4 expresión lambda

  • λ La undécima letra del alfabeto griego, el nombre en inglés es Lamda.
  • Evite demasiadas definiciones de clases internas anónimas
  • Su esencia pertenece al concepto de programación funcional.
  • Se eliminó un montón de código sin sentido, dejando solo la lógica central.
(paraems) -> expressionp[表达式]
(params) -> statement[语句]
(params) -> {
    
    statements}
a->System.out.println("i like lamda-->"+a);
new Thread(()->System.out.println("多线程学习...")).start();

Comprender la interfaz funcional es la clave para aprender expresiones Lambda de Java 8

Definición de interfaz funcional:

  • Cualquier interfaz que contenga solo un método abstracto es una interfaz funcional.
public interface Runnable{
    
    
    public abstract void run();
}
  • Para interfaces funcionales, los objetos de la interfaz se pueden crear mediante expresiones Lambda.
/*
 * 推导lambda表达式
 */
public class TestLambda1 {
    
    

    //3. 静态内部类
    static class Like2 implements ILike {
    
    
        @Override
        public void lambda() {
    
    
            System.out.println("i like lambda2");
        }
    }
    public static void main(String[] args) {
    
    
        ILike like = new Like();
        like.lambda();

        like = new Like2();
        like.lambda();
        //4. 局部内部类
        class Like3 implements ILike {
    
    
            @Override
            public void lambda() {
    
    
                System.out.println("i like lambda3");
            }
        }

        like = new Like3();
        like.lambda();

        //5. 匿名内部类 没有类的名称,必须借助接口或者父类
        like = new ILike() {
    
    
            @Override
            public void lambda() {
    
    
                System.out.println("i like lambda4");
            }
        };
        like.lambda();

        //6. lambda简化
        like = ()->{
    
    
            System.out.println("i like lambda5");
        };
        like.lambda();
    }
}

//1. 定义一个函数式接口
interface ILike {
    
    
    void lambda();
}

//2. 实现类
class Like implements ILike {
    
    
    @Override
    public void lambda() {
    
    
        System.out.println("i like lambda");
    }
}

2.5 Proxy estático

Caso: Implementación de proxy estático versus subproceso

Resumir:

  • Tanto el objeto real como el objeto proxy deben implementar la misma interfaz.
  • El objeto proxy debe representar el rol real.

ventaja:

  1. Los objetos proxy pueden hacer muchas cosas que los objetos reales no pueden hacer
  2. Los objetos reales se centran en hacer lo suyo.
public class StaticProxy {
    
    
    public static void main(String[] args) {
    
    
      	//You you = new You();//你要结婚
   
        WeddingCompany weddingCompany = new WeddingCompany(new You());
        weddingCompany.HappyMarry();
    }
}
interface Marry{
    
    

    void HappyMarry();
}
//真实角色,你去结婚
class You implements Marry{
    
    
    @Override
    public void HappyMarry() {
    
    
        System.out.println("结婚了,超开心!");
    }
}
//代理角色,帮助你结婚
class WeddingCompany implements Marry{
    
    
  	//代理谁->真实目标角色
    private Marry target;

    public WeddingCompany(Marry target) {
    
    
        this.target = target;
    }

    @Override
    public void HappyMarry() {
    
    
        before();
        this.target.HappyMarry();//这就是真实对象
        after();
    }

    private void after() {
    
    
        System.out.println("结婚之后,收尾款");
    }

    private void before() {
    
    
        System.out.println("结婚之前,布置现场");
    }
}

Caso con parámetros

Resumen :

  • Una expresión lambda solo se puede simplificar a una línea si tiene una línea de código. Si hay varias líneas, se puede encapsular con un bloque de código.
  • La premisa es que la interfaz es una interfaz funcional.
  • También puede eliminar tipos de parámetros para múltiples parámetros. Si desea eliminarlos, debe agregar paréntesis.
public class TestLambda2 {
    
    
    //3. 静态内部类
    static class Love implements ILove {
    
    
        @Override
        public void love(int a) {
    
    
            System.out.println("i love you -->" + a);
        }
    }

    public static void main(String[] args) {
    
    
        //4. 局部内部类
        class Love implements ILove {
    
    
            @Override
            public void love(int a) {
    
    
                System.out.println("i love you -->" + a);
            }
        }
        ILove love = new Love();
        love.love(2);
        //5. 匿名内部类 没有类的名称,必须借助接口或者父类
        love = new ILove() {
    
    
            @Override
            public void love(int a) {
    
    
                System.out.println("i love you -->" + a);
            }
        };
        //6. lambda简化
        love = (int a) -> {
    
    
            System.out.println("i love you -->" + a);
        };
        love.love(10);
        //6.1 简化1 去掉参数类型
        love = (a) -> {
    
    
            System.out.println("i love you -->" + a);
        };
        love.love(12);

        //6.2 简化2 简化括号
        love = a -> {
    
    
            System.out.println("i love you -->" + a);
        };
        love.love(520);

        //6.3 简化3 去化花括号
        love = a -> System.out.println("i love you -->" + a);
        love.love(521);
        
    }
}

//1. 定义一个函数式接口
interface ILove {
    
    
    void love(int a);
}

//2. 实现类
class Love implements ILove {
    
    
    @Override
    public void love(int a) {
    
    
        System.out.println("i love you -->" + a);
    }
}

Estado del hilo

Cinco estados principales:

  • Crear estado
  • estado listo
  • estado de bloqueo
  • Estado de funcionamiento
  • estado de muerte

3.1 Algunos métodos comunes de subprocesos

método ilustrar
setPriority(int nueva prioridad) Cambiar la prioridad de un hilo
sueño vacío estático (millis largos) Suspender el cuerpo del subproceso que se está ejecutando actualmente durante el número especificado de milisegundos
unión nula() Espere a que termine el hilo.
rendimiento vacío estático() Pausar el objeto de subproceso que se está ejecutando actualmente y ejecutar otros subprocesos
interrupción nula() Interrumpa hilos, no use este método
booleano está vivo() Probar si un hilo está activo

3.2. Cómo detener hilos

  • No se recomienda utilizar los métodos stop () y destroy () proporcionados por el JDK. [Obsoleto]
  • Se recomienda que el hilo se detenga solo.
  • Se recomienda utilizar un bit de bandera para terminar la variable. Cuando la bandera == false, el hilo finalizará.
//测试Stop
//1. 建议线程正常停止->利用次数,不建议死循环
//2. 建议使用标志位->设置一个标志位
//3. 不要使用stop或者destory等过时或者JDK不建议使用的方法
public class TestStop implements Runnable {
    
    
    //1. 设置一个标志位
    private boolean flag = true;

    @Override
    public void run() {
    
    
        int i = 0;
        while(flag){
    
    
            System.out.println("run...Thread"+i++);
        }
    }
    //2. 设置一个公开的方法停止线程,转换标志位
    public void stop(){
    
    
        this.flag = false;
    }

    public static void main(String[] args) {
    
    
        TestStop testStop = new TestStop();
        new Thread(testStop).start();
        for(int i=0;i<1000;i++){
    
    
            System.out.println("main"+i);
            if(i==900){
    
    
                //调用stop方法,切换标志位,让线程停止
                testStop.stop();
                System.out.println("线程停止了");
            }
        }
    }
}

3.3 Hilo dormir-dormir()

  • dormir (tiempo) especifica el número de milisegundos durante los cuales el hilo actual está bloqueado
  • Hay una excepción InterruptedException en el sueño.
  • Una vez alcanzado el tiempo de suspensión, el hilo entra en el estado listo.
  • El modo de suspensión puede simular retrasos en la red, cuentas regresivas, etc.
  • Cada objeto dormido tiene un candado, el sueño no desbloqueará el candado.
//模拟网络延时:放大问题的发生性
public class TestSleep implements Runnable {
    
    
    //票数
    private int ticketNums = 10;

    @Override
    public void run() {
    
    
        while (true) {
    
    
            if (ticketNums <= 0) {
    
    
                break;
            }
            //模拟延时
            try {
    
    
                Thread.sleep(200);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticketNums-- + "张票");
        }
    }

    public static void main(String[] args) {
    
    
        TestSleep ticket = new TestSleep();
        new Thread(ticket, "张三").start();
        new Thread(ticket, "李四").start();
        new Thread(ticket, "王五").start();
    }
}
//模拟倒计时
public class TestSleep2 {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            tenDown();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        //打印当前系统时间
        Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间
        while(true){
    
    
            try {
    
    
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                startTime = new Date(System.currentTimeMillis());//更新当前时间
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }

        }
    }
    public static void tenDown() throws InterruptedException {
    
    
        int num = 10;
        while(true){
    
    
            Thread.sleep(1000);
            System.out.println(num--);
            if(num<=0){
    
    
                break;
            }
        }
    }
}

3.3 Hilo de cortesía: rendimiento()

  • Hilo educado, que permite que el hilo que se está ejecutando actualmente se detenga pero no se bloquee
  • Mover un hilo del estado en ejecución al estado listo
  • Deje que la CPU reprograme y aún será posible programar el hilo cortés.
//线程礼让不一定成功,看cpu心情
public class TestYield {
    
    
    public static void main(String[] args) {
    
    
        MyYield myYield = new MyYield();
        new Thread(myYield,"A").start();
        new Thread(myYield,"B").start();
    }
}
class MyYield implements Runnable{
    
    

    @Override
    public void run() {
    
    
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName()+"线程结束执行");
    }
}

3.4 Fusionar hilos——Unir()

Unirse a hilos de fusión. Una vez completada la ejecución de este hilo, se ejecutarán otros hilos y se bloquearán otros hilos. Piense en ello como si estuviera haciendo cola.

//测试join方法,想象为插队
public class TestJoin implements Runnable {
    
    

    @Override
    public void run() {
    
    
        for (int i = 0; i < 1000; i++) {
    
    
            System.out.println("线程vip来了"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
    
    
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

        //主线程
        for(int i=0;i<500;i++){
    
    
            if(i==200){
    
    
               thread.join();
            }
            System.out.println("main"+i);
        }
    }
}

3.6 Observación del estado del hilo

  • Hilo.Estado

Estado del hilo: un hilo puede estar en uno de los siguientes estados:

  • NUEVO: Los hilos que aún no se han iniciado se encuentran en este estado.
  • RUNNABLE: los subprocesos que se ejecutan en la máquina virtual Java están en este estado.
  • BLOQUEADO: un subproceso que está bloqueado esperando un bloqueo del monitor se encuentra en este estado.
  • ESPERANDO: Un subproceso que está esperando que otro subproceso realice una acción específica se encuentra en este estado.
  • TERMINADO: El subproceso salido se encuentra en este estado.

Un hilo puede estar en un estado en un momento dado. Estos estados son estados de máquinas virtuales que no reflejan ningún estado de subproceso del sistema operativo.

//观测测试线程状态
public class TestState {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Thread thread = new Thread(()->{
    
    
            for(int i=0;i<5;i++){
    
    
                try {
    
    
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
            System.out.println("--------");
        });

        //观察状态
        Thread.State state = thread.getState();
        System.out.println(state); //New

        //观察启动后
        thread.start();//启动线程
        state = thread.getState();
        System.out.println(state); //Run

        while(state!=Thread.State.TERMINATED){
    
    //只要线程不终止,就一直输出状态
            Thread.sleep(100);
            state = thread.getState();//更新线程状态
            System.out.println(state);//输出状态
        }
    }
}

3.7 Prioridad del hilo

Java proporciona un programador de subprocesos para monitorear todos los subprocesos en el programa que ingresan al estado listo después de iniciarse. El programador de subprocesos determina qué subproceso debe programarse para su ejecución según la prioridad.

  • La prioridad de un hilo está representada por un número, que va del 1 al 10.
    • Hilo.MIN_PRIORITY=1
    • Hilo.MAX_PRIORITY=10
    • Hilo.NORM_PRIORITY=5
  • Utilice los siguientes métodos para cambiar u obtener la prioridad
    • getPriority().setPriority(int xxx)

Se recomienda establecer la prioridad antes de la programación start().

//测试线程的优先级
public class TestPriority extends Thread{
    
    
    public static void main(String[] args) {
    
    
        //主线程默认优先级
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());

        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);
        Thread t5 = new Thread(myPriority);
        Thread t6 = new Thread(myPriority);

        //先设置优先级,再启动
        t1.start();

        t2.setPriority(1);
        t2.start();

        t3.setPriority(4);
        t3.start();

        t4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY = 10
        t4.start();

        t5.setPriority(8);
        t5.start();

        t6.setPriority(7);
        t6.start();
    }
}
class MyPriority implements Runnable{
    
    

    @Override
    public void run() {
    
    
        try {
    
    
            Thread.sleep(1000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());

    }
}

3.8. Hilo de demonio

  • Los hilos se dividen en hilos de usuario y hilos de demonio.
  • La máquina virtual debe garantizar que el hilo del usuario haya completado la ejecución.
  • La máquina virtual no necesita esperar a que el hilo del demonio complete la ejecución.
  • Como registrar registros de operaciones en segundo plano y monitorear las esperas de recolección de basura de la memoria.
//测试守护线程
//上帝守护你
public class TestDaemon {
    
    
    public static void main(String[] args) {
    
    
        God god = new God();
        You you = new You();
        Thread thread = new Thread(god);
        thread.setDaemon(true); //默认是false表示是用户线程,正常的线程都是用户线程

        thread.start();//上帝守护线程启动
        new Thread(you).start(); //你 用户线程启动
    }
}

//上帝
class God implements Runnable{
    
    
    @Override
    public void run() {
    
    
        while(true){
    
    
            System.out.println("上帝保佑你");
        }
    }
}

//你
class You implements Runnable{
    
    
    @Override
    public void run() {
    
    
        for(int i=0;i<36500;i++){
    
    
            System.out.println("你一生都很开心的活着");
        }
        System.out.println("--------死了---------------------");
    }
}

3.9 Concurrencia

El mismo objeto es operado por múltiples subprocesos simultáneamente.

En la vida real nos encontraremos con el problema de "varias personas quieren usar el mismo recurso", por ejemplo, si hay cola en el comedor para conseguir comida, todos quieren comer, la solución más natural es hacer cola una. por uno.

Cuando se trata de problemas de subprocesos múltiples, varios subprocesos acceden al mismo objeto y algunos subprocesos también quieren modificar el objeto. En este momento, necesitamos sincronización de subprocesos. La sincronización de subprocesos es en realidad un mecanismo de espera, ¡es necesario acceder a varios al mismo tiempo! Los subprocesos de este objeto ingresan al grupo de espera de este objeto para formar una cola y esperan a que el subproceso anterior termine de usarlo antes de que lo use el siguiente subproceso.

Condiciones para formar seguridad de hilos: colas y bloqueos.

4. Sincronización de hilos

  • Concurrencia: el mismo objeto es operado por varios subprocesos al mismo tiempo (obtención de tickets)
  • La sincronización de subprocesos es un mecanismo de espera: varios subprocesos que necesitan acceder a un objeto secundario al mismo tiempo ingresan al grupo de espera de este objeto para formar una cola y esperan a que el subproceso anterior termine de usarlo antes de que el siguiente subproceso pueda usarlo.

Condiciones de formación: cola + bloqueo

Dado que varios subprocesos del mismo proceso comparten el mismo espacio de almacenamiento, no solo brinda comodidad, sino que también genera problemas de conflicto de acceso. Para garantizar la exactitud de los datos cuando se accede a ellos en el método, se agrega un mecanismo de bloqueo sincronizado durante el acceso . Un hilo obtiene un bloqueo exclusivo sobre un objeto y ocupa recursos exclusivos. Otros hilos deben esperar y liberar el bloqueo después de su uso. Existen los siguientes problemas:

  • Sostener un candado por un hilo hace que todos los demás hilos que necesitan el candado se cuelguen;
  • En la competencia de subprocesos múltiples, bloquear y liberar bloqueos provocará más cambios de contexto y retrasos en la programación, lo que provocará problemas de rendimiento;
  • Si un subproceso de alta prioridad espera a que un subproceso de baja prioridad libere el bloqueo, provocará una inversión de prioridad y provocará problemas de rendimiento.

4.1. Ejemplos de subprocesos inseguros

1. Emisión de billetes insegura

//不安全的买票
public class UnsafeBuyTicket {
    
    
    public static void main(String[] args) {
    
    
        BuyTicket station = new BuyTicket();
        new Thread(station,"我").start();
        new Thread(station,"美女").start();
        new Thread(station,"黄牛党").start();
    }
}

class BuyTicket implements Runnable{
    
    
    //票数
    private int ticketNums = 10;
    boolean flag = true;
    @Override
    public void run() {
    
    
        //买票
        while(flag){
    
    
            try {
    
    
                buy();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
    }
    private void buy() throws InterruptedException {
    
    
        //判断是否有票
        if(ticketNums<=0){
    
    
            flag = false;
            return;
        }
        //模拟延迟
        Thread.sleep(100);
        //买票
        System.out.println(Thread.currentThread().getName()+"买到了第"+ticketNums--+"张票");
    }
}

2. No es seguro retirar dinero

//取钱线程不安全
//两个人去银行取钱,账户
public class UnsafeBank {
    
    
    public static void main(String[] args) {
    
    
        Account account = new Account(100,"结婚基金");
        Drawing you = new Drawing(account,50,"你");
        Drawing girlfrend = new Drawing(account,100,"女朋友");

        you.start();
        girlfrend.start();
    }
}

//账户
class Account {
    
    
    int money;//余额
    String name;//账户名

    public Account(int money, String name) {
    
    
        this.money = money;
        this.name = name;
    }
}

//银行:模拟存款
class Drawing extends Thread {
    
    
    Account account;//账户
    int drawingMoney;//取了多少钱
    int nowMoney;//剩余多少钱

    public Drawing(Account account, int drawingMoney, String name) {
    
    
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    //取钱
    @Override
    public void run() {
    
    
        //判断有没有钱
        if (account.money - drawingMoney < 0) {
    
    
            System.out.println(Thread.currentThread().getName() + "余额不足");
            return;
        }
        //sleep可以放大问题的发生性
        try {
    
    
            Thread.sleep(1000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        // 卡内余额 = 余额 – 你取的钱
        account.money = account.money - drawingMoney;
        //你手里的钱
        nowMoney = nowMoney + drawingMoney;

        System.out.println("账户余额:" + account.money);
        //Thread.currentThread().getName() == this.getName()
        System.out.println(this.getName() + "手里的钱" + nowMoney);
    }
}

3. Colecciones inseguras

//线程不安全的集合
public class UnsafeList {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
    
    
            new Thread(()->{
    
    
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(3000);
        System.out.println(list.size());
    }
}

4.2 Métodos de sincronización y bloques de sincronización

  • Dado que podemos usar la palabra clave privada para garantizar que solo se pueda acceder al objeto de datos mediante métodos, solo necesitamos proponer un mecanismo para el método, que es la palabra clave sincronizada.

  • Incluye dos métodos: método sincronizado y bloque sincronizado.

método sincronizado:

public synchronized void method(int args){
    
    }
  • El método sincronizado controla el acceso a los "objetos". Cada objeto corresponde a un bloqueo. Cada método sincronizado debe obtener el bloqueo del objeto que llama al método antes de que pueda ejecutarse. De lo contrario, el hilo se bloqueará. Una vez que se ejecuta el método, ocupará exclusivamente el bloqueo hasta que se ejecute el método, se libera solo después de que el método regresa y el hilo que se bloquea más tarde puede obtener el bloqueo y continuar con la ejecución.

  • Desventaja: si un método grande se declara sincronizado, la eficiencia se verá afectada.

bloque sincronizado:

Bloque sincronizado: sincronizado (Obj) {}

Obj se llama monitor de sincronización

  • Obj puede ser cualquier objeto, pero se recomienda utilizar recursos compartidos como monitores de sincronización.
  • No es necesario especificar un monitor de sincronización en el método de sincronización, porque el monitor de sincronización del método de sincronización es este, que es el objeto en sí o la clase.
  • Sincronizar la ejecución del monitor
  1. El primer hilo accede, bloquea el monitor de sincronización y ejecuta el código que contiene.
  2. El segundo hilo accede y descubre que el monitor de sincronización está bloqueado y no se puede acceder a él.
  3. El primer hilo completa el acceso y desbloquea el monitor de sincronización.
  4. El segundo hilo accede, descubre que el monitor de sincronización no tiene bloqueo, luego bloquea y accede

comprar boletos

public class UnsafeBuyTicket {
    
    
    public static void main(String[] args) {
    
    
        BuyTicket station = new BuyTicket();
        new Thread(station,"我").start();
        new Thread(station,"美女").start();
        new Thread(station,"黄牛党").start();
    }
}

class BuyTicket implements Runnable{
    
    
    //票数
    private int ticketNums = 10;
    boolean flag = true;
    @Override
    public void run() {
    
    
        //买票
        while(flag){
    
    
            try {
    
    
                buy();
                //模拟延迟
                Thread.sleep(100);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
    }
    //synchronized 同步方法 ,锁的是this
    private synchronized void buy() throws InterruptedException {
    
    
        //判断是否有票
        if(ticketNums<=0){
    
    
            flag = false;
            return;
        }

        //买票
        System.out.println(Thread.currentThread().getName()+"买到了第"+ticketNums--+"张票");
    }
}

Retirar dinero

//取钱线程不安全
//两个人去银行取钱,账户
public class UnsafeBank {
    
    
    public static void main(String[] args) {
    
    
        Account account = new Account(500,"结婚基金");
        Drawing you = new Drawing(account,50,"你");
        Drawing girlfrend = new Drawing(account,100,"女朋友");

        you.start();
        girlfrend.start();
    }
}

//账户
class Account {
    
    
    int money;//余额
    String name;//账户名

    public Account(int money, String name) {
    
    
        this.money = money;
        this.name = name;
    }
}

//银行:模拟存款
class Drawing extends Thread {
    
    
    Account account;//账户
    int drawingMoney;//取了多少钱
    int nowMoney;//剩余多少钱

    public Drawing(Account account, int drawingMoney, String name) {
    
    
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    //取钱
    // synchronized
    @Override
    public void run() {
    
    
        synchronized (account){
    
    
            //判断有没有钱
            if (account.money - drawingMoney < 0) {
    
    
                System.out.println(Thread.currentThread().getName() + "余额不足");
                return;
            }
            //sleep可以放大问题的发生性
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            // 卡内余额 = 余额 – 你取的钱
            account.money = account.money - drawingMoney;
            //你手里的钱
            nowMoney = nowMoney + drawingMoney;

            System.out.println("账户余额:" + account.money);
            //Thread.currentThread().getName() == this.getName()
            System.out.println(this.getName() + "手里的钱" + nowMoney);
        }
    }
}

recolectar

public class UnsafeList {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
    
    
            new Thread(()->{
    
    
                synchronized (list){
    
    
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        Thread.sleep(3000);
        System.out.println(list.size());
    }
}

Colección de tipos de seguridad JUC

**CopyOnWriteArrayList**

//测试JUC安全类型的集合
public class TestJUC {
    
    
    public static void main(String[] args) {
    
    
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList();
        for (int i = 0; i < 10000; i++) {
    
    
            new Thread(()->{
    
    
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
    
    
            Thread.sleep(3000);
        } catch (InterruptedException e) {
    
    
            throw new RuntimeException(e);
        }
        System.out.println(list.size());
    }
}

4.3 Punto muerto y bloqueo

Punto muerto:

Varios subprocesos ocupan cada uno algunos recursos compartidos y esperan a que otros subprocesos ocupen los recursos de cada uno antes de poder ejecutarse, lo que da como resultado una situación en la que dos o más subprocesos esperan entre sí para liberar recursos y ambos detienen la ejecución. una sincronización

Cuando un bloque mantiene "bloqueos en más de dos objetos" al mismo tiempo, puede ocurrir un problema de "bloqueo mutuo".

Cuatro condiciones necesarias para que se produzca un punto muerto:

  1. Condición mutuamente excluyente: un recurso solo puede ser utilizado por un proceso a la vez.
  2. Condiciones de solicitud y retención: Cuando un proceso se bloquea por solicitar recursos, conserva los recursos obtenidos.
  3. Condición de no privación: Los recursos que se han obtenido mediante el proceso no pueden ser privados por la fuerza antes de que se agoten.
  4. Condiciones de espera circular: varios procesos forman una relación de espera circular de cabeza a cola para los recursos.

Siempre que se viole una o más de las cuatro condiciones anteriores, se puede evitar el punto muerto.

package com.yuan.Demo04;

//多个线程互相抱着对方需要的资源,然后形成僵持
public class DeadLock {
    
    
    public static void main(String[] args) {
    
    
        Makeup g1 = new Makeup(0, "灰姑娘");
        Makeup g2 = new Makeup(1, "白雪公主");
        g1.start();
        g2.start();
    }
}

//口红
class Lipstick {
    
    

}

//镜子
class Mirror {
    
    

}

class Makeup extends Thread {
    
    
    //需要的资源只有一份,用static来保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;//选择
    String girlName;//使用化妆品的人

    public Makeup(int choice, String girlName) {
    
    
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
    
    
        //化妆
        try {
    
    
            makeup();
        } catch (InterruptedException e) {
    
    
            throw new RuntimeException(e);
        }
    }

    //化妆,互相持有对方的锁,就是需要拿到对方的资源
    private void makeup() throws InterruptedException {
    
    
        if (choice == 0) {
    
    
            synchronized (lipstick) {
    
    //获得口红的锁
                System.out.println(this.girlName + "获得口红的锁");
                Thread.sleep(1000);
            }
            synchronized (mirror) {
    
    //1s钟后 获得镜子的锁
                System.out.println(this.girlName + "获得镜子的锁");
            }
        } else {
    
    
            synchronized (mirror) {
    
    //获得口红的锁
                System.out.println(this.girlName + "获得镜子的锁");
                Thread.sleep(2000);
            }
            synchronized (lipstick) {
    
    //1s钟后 获得镜子的锁
                System.out.println(this.girlName + "获得口红的锁");
            }
        }
    }
}

Cerrar con llave:

  • A partir de JDK 5.0, Java proporciona un mecanismo de sincronización de subprocesos más potente: la sincronización se logra definiendo explícitamente objetos de bloqueo de sincronización. El bloqueo de sincronización utiliza el objeto Lock como

  • La interfaz java.util.concurrent.locks.Lock es una herramienta para controlar el acceso de múltiples subprocesos a recursos compartidos. Los bloqueos proporcionan acceso exclusivo a recursos compartidos. Sólo un subproceso puede bloquear el objeto Lock a la vez. El subproceso debe obtener el objeto Lock antes de comenzar a acceder a los recursos compartidos.

  • La clase ReentrantLock implementa Lock, que tiene la misma concurrencia y semántica de memoria que sincronizada. Al implementar el control seguro para subprocesos, ReentrantLock se usa más comúnmente y puede mostrar bloqueos y liberar bloqueos.

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    
    
    public static void main(String[] args) {
    
    
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }
}

class TestLock2 implements Runnable {
    
    
    int ticketNums = 10;
    //定义lock锁
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
    
    
        while (true) {
    
    
            try {
    
    
                lock.lock();//加锁
                if (ticketNums > 0) {
    
    
                    try {
    
    
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
    
    
                        throw new RuntimeException(e);
                    }
                    System.out.println(ticketNums--);
                } else {
    
    
                    break;
                }
            } finally {
    
    
                //解锁
                lock.unlock();
            }
        }
    }
}

Comparación entre sincronizado y Lock:

  • El bloqueo es un bloqueo explícito (activado y desactivado manualmente), sincronizado es un bloqueo implícito y se libera automáticamente cuando está fuera del alcance.
  • Lock solo tiene bloqueos de código, sincronizado tiene bloqueos de bloque de código y bloqueos de método.
  • Al utilizar Lock, la JVM dedicará menos tiempo a programar subprocesos y funcionará mejor. y tiene mejor escalabilidad (proporcionando más subclases)
  • Bloquear > Bloque de código sincronizado (ha ingresado al cuerpo del método y ha asignado los recursos correspondientes) > Método sincronizado (fuera del cuerpo del método)

5. Comunicación de hilos

Escenario de aplicación: problemas de productores y consumidores.

  • Supongamos que solo se puede almacenar un producto en el almacén, el productor coloca el producto producido en el almacén y el consumidor retira el producto del almacén para su consumo.
  • Si no hay producto en el almacén, el productor colocará el producto en el almacén; de lo contrario, detendrá la producción y esperará hasta que el consumidor recoja el producto en el almacén.
  • Si hay un producto en el almacén, el consumidor puede retirarlo para su consumo, de lo contrario el consumo se detendrá hasta que el producto vuelva a colocarse en el almacén.

Este es un problema de sincronización de subprocesos: los productores y consumidores comparten el mismo recurso, y los productores y consumidores son interdependientes y están condicionados entre sí.

  • Para los productores, antes de producir productos, deben notificar a los consumidores que esperen. Después de producir productos, deben notificar a los consumidores inmediatamente para que consuman.
  • Para los consumidores, después del consumo, se debe notificar a los productores que el consumo ha finalizado y que es necesario producir nuevos productos para el consumo.
  • En el problema productor-consumidor, la sincronización por sí sola no es suficiente

**sincronizado: **Puede evitar actualizaciones simultáneas del mismo recurso compartido, lograr la sincronización y no se puede utilizar para implementar el paso de mensajes (comunicación) entre diferentes subprocesos.

5.1 Varios métodos para resolver la comunicación de subprocesos.

Nota: Todos son métodos de la clase Object y solo se pueden usar en métodos sincronizados o bloques de código sincronizados; de lo contrario, se generará ilegalMonitorStateException.

nombre del método efecto
esperar() Indica que el hilo espera hasta que otros hilos lo notifiquen y, a diferencia del modo de suspensión, el bloqueo se liberará.
espera (tiempo de espera largo) Especifique el número de milisegundos a esperar
notificar() Despierta un hilo en espera
notificar a todos() Despierte todos los subprocesos que llaman al método wait() en el mismo objeto. Los subprocesos con mayor prioridad se programan primero.

5.2 Resolver el problema de comunicación entre hilos.

5.2.1 Método de canalización

Modelo de escritura concurrente "modelo productor/consumidor" -> Método de procesamiento

  • Productor: módulo responsable de producir datos (pueden ser métodos, objetos, hilos, procesos)
  • Consumidor: módulo responsable de procesar datos (pueden ser métodos, objetos, hilos, procesos)
  • Búfer: los consumidores no pueden utilizar directamente los datos del productor, hay un búfer entre ellos

El productor coloca los datos producidos en el búfer y el consumidor extrae los datos del búfer.

//测试:生产者消费者模型->利用缓冲区解决:管程法
//生产者、消费者、产品、缓冲区
public class TestPC {
    
    
    public static void main(String[] args) {
    
    
        SynContainer container = new SynContainer();
        new Productor(container).start();
        new Consumer(container).start();
    }
}

//生产者
class Productor extends Thread {
    
    
    SynContainer container;

    public Productor(SynContainer container) {
    
    
        this.container = container;
    }
    //生产

    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            container.push(new Chicken(i));
            System.out.println("生产了" + i + "只鸡");
        }
    }
}

//消费者
class Consumer extends Thread {
    
    
    SynContainer container;

    public Consumer(SynContainer container) {
    
    
        this.container = container;
    }
    //消费

    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            System.out.println("消费了->" + container.pop().id + "只鸡");
        }
    }
}

//产品
class Chicken {
    
    
    int id;//产品编号

    public Chicken(int id) {
    
    
        this.id = id;
    }
}

//缓冲区
class SynContainer {
    
    
    //需要一个容器大小
    Chicken[] chickens = new Chicken[10];
    //容器计数器
    int count = 0;

    //生产者放入产品
    public synchronized void push(Chicken chicken) {
    
    
        //如果容器满了,就需要等待消费者消费产品
        while (count == chickens.length) {
    
    
            //通知消费者消费,生产等待
            try {
    
    
                this.wait();
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }
        //如果没有满,我们需要丢入产品
        chickens[count] = chicken;
        count++;

        //可以通知消费者消费
        this.notifyAll();
    }

    //消费者消费产品
    public synchronized Chicken pop() {
    
    
        //判断能否消费
        while (count == 0) {
    
    
            //等待生产者生产,消费者等待
            try {
    
    
                this.wait();
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }
        //如果可以消费
        count--;
        Chicken chicken = chickens[count];

        //吃完了,通知生产者生产
        this.notifyAll();
        return chicken;
    }
}

5.2.2 Método de la luz de señal

//测试:生产者消费者模型->利用缓冲区解决:信号灯法
public class TestPC2 {
    
    
    public static void main(String[] args) {
    
    
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}

//生产者-->演员
class Player extends Thread {
    
    
    TV tv;

    public Player(TV tv) {
    
    
        this.tv = tv;
    }

    @Override
    public void run() {
    
    
        for (int i = 0; i < 20; i++) {
    
    
            if (i % 2 == 0) {
    
    
                this.tv.play("快乐大本营播放中");
            } else {
    
    
                this.tv.play("抖音,记录美好生活");
            }
        }
    }
}

//消费者-->观众
class Watcher extends Thread {
    
    
    TV tv;

    public Watcher(TV tv) {
    
    
        this.tv = tv;
    }

    @Override
    public void run() {
    
    
        for (int i = 0; i < 20; i++) {
    
    
            tv.watch();
        }
    }
}

//产品-->节目
class TV {
    
    
    //演员表演,观众等待 T
    //观众观看,演员等待 F
    String voice;//表演的节目
    boolean flag = true;

    //表演
    public synchronized void play(String voice) {
    
    
        if (!flag) {
    
    
            try {
    
    
                this.wait();
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }
        System.out.println("演员表演了:" + voice);
        //通知观众观看
        this.notifyAll();//通知唤醒
        this.voice = voice;
        this.flag = !this.flag;

    }

    //观看
    public synchronized void watch() {
    
    
        if (flag) {
    
    
            try {
    
    
                this.wait();
            } catch (InterruptedException e) {
    
    
                throw new RuntimeException(e);
            }
        }
        System.out.println("观看了:" + voice);
        //通知演员表演
        this.notifyAll();
        this.flag = !this.flag;
    }
}

5.2.3 Usar grupo de subprocesos

Antecedentes: los recursos que se crean y destruyen con frecuencia y se utilizan de manera especialmente intensa, como los subprocesos en situaciones concurrentes, tienen un gran impacto en el rendimiento.

Idea: cree varios subprocesos con anticipación, colóquelos en el grupo de subprocesos, consígalos directamente cuando se usen y vuelva a colocarlos en el grupo después de su uso. Puede evitar la creación y destrucción frecuentes y realizar la reutilización. Similar al transporte público en la vida.

ventaja:

  • Capacidad de respuesta mejorada (tiempo reducido para crear nuevos hilos)

  • Reducir el consumo de recursos (reutilizar subprocesos en el grupo de subprocesos, no es necesario crearlos cada vez)

  • Facilita la gestión de hilos...

    1. **corePoolSize:**El tamaño del grupo central
    2. **maximumPoolSize:** Número máximo de subprocesos
    3. **keepAliveTime:** El período máximo de tiempo que un hilo puede mantenerse antes de terminar cuando no tiene tareas.
  • JDK 5.0 proporciona API relacionadas con el grupo de subprocesos: ExecutorService y Executors

  • ExecutorServic: la interfaz real del grupo de subprocesos. Subclases comunes ThreadPoolExecutor

    • ejecución nula (comando ejecutable): ejecuta tarea / comando, sin valor de retorno, generalmente se usa para ejecutar Runnable
    • Envío futuro (tarea invocable): ejecuta la tarea, tiene un valor de retorno y generalmente se usa para ejecutar invocable
    • Apagado vacío(): cierra el grupo de conexiones.
  • Ejecutores: clase de herramienta, clase de fábrica del grupo de subprocesos, utilizada para crear y devolver diferentes tipos de grupos de subprocesos.

6. Resumen del hilo

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

//回顾总结县城的创建
public class ThreadNew {
    
    
    public static void main(String[] args) {
    
    
        new MyThread1().start();
        new Thread(new MyThread2()).start();
        FutureTask<Integer> futureTask = new FutureTask(new MyThread3());
        new Thread(futureTask).start();

        try {
    
    
            Integer integer = futureTask.get();
            System.out.println(integer);
        } catch (InterruptedException e) {
    
    
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

//1. 继承Thread类
class MyThread1 extends Thread {
    
    
    @Override
    public void run() {
    
    
        System.out.println("MyThread1");
    }
}

//2. 实现Runnable 接口
class MyThread2 implements Runnable{
    
    
    @Override
    public void run() {
    
    
        System.out.println("MyThread2");
    }
}

//3. 实现Callable接口
class MyThread3 implements Callable<Integer>{
    
    

    @Override
    public Integer call() throws Exception {
    
    
        System.out.println("MyThread3");
        return 100;
    }
}

Supongo que te gusta

Origin blog.csdn.net/weixin_42823298/article/details/128749759
Recomendado
Clasificación