Notes d'étude JAVA—multi-threading

1. Notions de base

  • Tâche
  • processus
    • Le programme exécuté dans le système d'exploitation est un processus. Un processus est un processus d'exécution du programme. Il s'agit d'une unité conceptuelle dynamique d'allocation de ressources système.
    • Habituellement, un processus peut contenir plusieurs threads. Bien sûr, il y a au moins un thread dans un processus, sinon son existence n'a aucun sens. Les threads sont l'unité de planification et d'exécution du processeur.
  • fil
    • Les threads sont des chemins d'exécution indépendants
    • Lorsque le programme est en cours d'exécution, même si vous ne créez pas de thread vous-même, il y aura plusieurs threads en arrière-plan, tels que le thread principal et le thread GC.
    • main() est appelé le thread principal, qui est le point d’entrée du système et est utilisé pour exécuter l’intégralité du programme.
    • Dans un processus, si plusieurs threads sont ouverts, l'exécution des threads est planifiée par le planificateur. Le planificateur est étroitement lié au système d'exploitation et l'ordre ne peut pas être interféré par les humains.
    • Lorsque vous travaillez sur la même ressource, il y aura un problème d'accaparement des ressources et un contrôle de concurrence doit être ajouté.
    • Les threads entraîneront une surcharge supplémentaire, telle que le temps de planification du processeur et la surcharge de contrôle de concurrence.
    • Chaque thread interagit dans sa propre mémoire de travail. Un contrôle inapproprié de la mémoire entraînera une incohérence des données.

2. Création de fil de discussion

2.1 Hériter de la classe Thread (points clés)

  1. Classe de thread personnalisée, hérite de la classe Thread
  2. Réécrivez la méthode run() et écrivez le corps d'exécution du thread
  3. Créez un objet thread dans la fonction principale et appelez la méthode start() pour démarrer le thread.

Remarque : Le thread ne peut pas être exécuté immédiatement lorsqu'il est démarré. Il est planifié pour être exécuté par le 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);
        }
    }
}

Cas : Téléchargement d'images

//练习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 Implémenter l'interface Runnable (points clés)

  1. Classe de thread personnalisée, implémentation de l'interface Runnable
  2. Réécrivez la méthode run() et écrivez le corps d'exécution du thread
  3. Le thread d'exécution doit être lancé dans la classe d'implémentation de l'interface exécutable et appeler la méthode 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);
        }
    }
}

Comparaison des deux méthodes ci-dessus :

Hériter de la classe Thread

  • La sous-classe hérite de la classe Thread et possède des capacités multithread.
  • Fil de démarrage : sous-classe object.start()
  • Obsolète : évitez les limitations de l'héritage unique POO
  • Implémenter l'interface Runnable

Implémenter l'interface Runnable

  • Possède des capacités multithread
  • Démarrer le thread : transmettre l'objet cible + Thread object.start()
    • new Thread(objet).start();
  • Utilisation recommandée : évitez les limitations de l'héritage unique et facilitez l'utilisation du même objet par plusieurs threads.

Problèmes de concurrence

//多个线程同时操作同一个对象
//买火车票的例子
//发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
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();
    }
}

Cas : Tortue et Lièvre

  1. Tout d’abord, définissons la distance de la piste, puis rapprochons-nous de plus en plus de la ligne d’arrivée.
  2. Déterminer si le jeu est terminé
  3. Imprimez le gagnant
  4. La tortue et le lièvre commencent
  5. Dans l'histoire, la tortue gagne et le lapin a besoin de dormir, donc nous simulons le lapin qui dort.
  6. Finalement, la tortue a gagné la course
//模拟龟兔赛跑
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 Implémenter l'interface Callable (comprendre)

Implémentez l'interface Callable et remplacez la méthode d'appel.

  1. L'implémentation de l'interface Callable nécessite un type de valeur de retour
  2. Remplacez la méthode d'appel et devez lever une exception
  3. Créer un objet cible
  4. Créez un service d'exécution : ExecutorService = Executor.newFixedThreadPool(1);
  5. Exécution de soumission : Future result1 = ser.submit(1);
  6. Obtenez le résultat : booléen r1 = result.get()
  7. Arrêtez le service : ser.shutdownNow() :

avantage:

  1. la valeur de retour peut être définie
  2. Peut lever des exceptions
//线程创建方式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.4Expression lambda

  • λ La onzième lettre de l'alphabet grec, le nom anglais est Lamda
  • Évitez trop de définitions de classes internes anonymes
  • Son essence appartient au concept de programmation fonctionnelle
  • Suppression d'un tas de code dénué de sens, ne laissant que la logique de base
(paraems) -> expressionp[表达式]
(params) -> statement[语句]
(params) -> {
    
    statements}
a->System.out.println("i like lamda-->"+a);
new Thread(()->System.out.println("多线程学习...")).start();

Comprendre l'interface fonctionnelle est la clé pour apprendre les expressions Java 8 Lambda

Définition de l'interface fonctionnelle :

  • Toute interface qui ne contient qu'une seule méthode abstraite est une interface fonctionnelle
public interface Runnable{
    
    
    public abstract void run();
}
  • Pour les interfaces fonctionnelles, les objets de l'interface peuvent être créés via des expressions 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 statique

Cas : Implémentation d'un proxy statique par rapport à Thread

Résumer:

  • L'objet réel et l'objet proxy doivent implémenter la même interface
  • L'objet proxy doit représenter le rôle réel

avantage:

  1. Les objets proxy peuvent faire beaucoup de choses que les objets réels ne peuvent pas faire
  2. Les objets réels se concentrent sur leur propre travail
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("结婚之前,布置现场");
    }
}

Cas avec paramètres

Résumé :

  • Une expression lambda ne peut être simplifiée en une seule ligne que si elle contient une seule ligne de code. S'il y a plusieurs lignes, elle peut être enveloppée dans un bloc de code.
  • Le principe est que l'interface est une interface fonctionnelle
  • Vous pouvez également supprimer des types de paramètres pour plusieurs paramètres. Si vous souhaitez les supprimer, vous devez ajouter des parenthèses.
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);
    }
}

Statut du sujet

Cinq grands États :

  • Créer un statut
  • état prêt
  • état de blocage
  • État de fonctionnement
  • état de mort

3.1. Quelques méthodes courantes de thread

méthode illustrer
setPriority (int nouvelle priorité) Changer la priorité d'un fil
sommeil vide statique (longs millis) Mettre en veille le corps du thread en cours d'exécution pendant le nombre de millisecondes spécifié
jointure vide() Attendez que le fil se termine
rendement du vide statique() Suspendre l'objet thread en cours d'exécution et exécuter d'autres threads
annuler l'interruption() Interrompre les threads, n'utilisez pas cette méthode
booléen isAlive() Tester si un fil de discussion est actif

3.2. Comment arrêter les threads

  • Il n'est pas recommandé d'utiliser les méthodes stop() et destroy() fournies par le JDK. [Obsolète]
  • Il est recommandé que le fil s'arrête tout seul
  • Il est recommandé d'utiliser un bit d'indicateur pour terminer la variable. Lorsque flag == false, le thread sera terminé.
//测试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. Fil de discussion sommeil-sommeil()

  • sleep (time) spécifie le nombre de millisecondes pendant lequel le thread actuel est bloqué
  • Il y a une exception InterruptedException en veille
  • Une fois le temps de veille atteint, le thread passe à l’état prêt.
  • le sommeil peut simuler un retard du réseau, un compte à rebours, etc.
  • Chaque objet endormi a un verrou, le sommeil ne libérera pas le verrou
//模拟网络延时:放大问题的发生性
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. Sujet de courtoisie - rendement()

  • Fil de discussion poli, permettant au thread en cours d'exécution de faire une pause mais pas de bloquer
  • Déplacer un thread de l'état en cours d'exécution à l'état prêt
  • Laissez le processeur replanifier, et il est toujours possible de planifier le fil de discussion poli.
//线程礼让不一定成功,看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. Fusionner les fils de discussion——Rejoindre()

Rejoindre fusionne les threads. Une fois l'exécution de ce thread terminée, d'autres threads seront exécutés et d'autres threads seront bloqués. Pensez-y comme si vous faisiez la queue.

//测试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. Observation de l'état du fil

  • Thread.State

État du thread : un thread peut être dans l’un des états suivants :

  • NOUVEAU : Les threads qui n'ont pas encore été démarrés sont dans cet état.
  • RUNNABLE : les threads exécutés dans la machine virtuelle Java sont dans cet état.
  • BLOQUÉ : un thread bloqué en attente d'un verrouillage du moniteur est dans cet état.
  • EN ATTENTE : un thread qui attend qu'un autre thread effectue une action spécifique est dans cet état.
  • TERMINATED : le thread quitté est dans cet état.

Un thread peut être dans un état à un moment donné. Ces états sont des états de machine virtuelle qui ne reflètent aucun état de thread du système d'exploitation.

//观测测试线程状态
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. Priorité des fils de discussion

Java fournit un planificateur de threads pour surveiller tous les threads du programme qui entrent dans l'état prêt après avoir été démarré. Le planificateur de threads détermine quel thread doit être planifié pour l'exécution en fonction de la priorité.

  • La priorité d'un thread est représentée par un nombre allant de 1 à 10
    • Sujet.MIN_PRIORITY=1
    • Sujet.MAX_PRIORITY=10
    • Sujet.NORM_PRIORITY=5
  • Utilisez les méthodes suivantes pour modifier ou obtenir la priorité
    • getPriority().setPriority(int xxx)

Il est recommandé de définir la priorité avant la planification 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. Fil démon

  • Les threads sont divisés en threads utilisateur et threads démon
  • La machine virtuelle doit s'assurer que le thread utilisateur a terminé son exécution
  • La machine virtuelle n'a pas besoin d'attendre que le thread démon termine son exécution.
  • Tels que l'enregistrement des journaux d'opérations en arrière-plan et la surveillance des attentes de récupération de place en mémoire.
//测试守护线程
//上帝守护你
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. Concurrence

Le même objet est exploité par plusieurs threads simultanément

Dans la vraie vie, nous rencontrerons le problème de "plusieurs personnes veulent utiliser la même ressource". Par exemple, s'il y a une file d'attente à la cantine pour obtenir de la nourriture, tout le monde veut manger. La solution la plus naturelle est de faire la queue par un.

Lorsque nous traitons de problèmes multithreads, plusieurs threads accèdent au même objet et certains threads souhaitent également modifier l'objet.À ce stade, nous avons besoin de synchronisation des threads. La synchronisation des threads est en fait un mécanisme d'attente, il faut accéder à plusieurs en même temps ! Les threads de cet objet entrent dans le pool d'attente de cet objet pour former une file d'attente et attendent que le thread précédent ait fini de l'utiliser avant que le thread suivant ne l'utilise.

Conditions de formation de la sécurité des threads : files d'attente et verrous

4. Synchronisation des fils

  • Concurrence : le même objet est exploité par plusieurs threads en même temps (saisie de tickets)
  • La synchronisation des threads est un mécanisme d'attente.Plusieurs threads qui doivent accéder à un objet secondaire en même temps entrent dans le pool d'attente de cet objet pour former une file d'attente et attendent que le thread précédent ait fini de l'utiliser avant que le thread suivant puisse l'utiliser.

Conditions de formation : file d'attente + verrou

Étant donné que plusieurs threads du même processus partagent le même espace de stockage, cela apporte non seulement de la commodité, mais entraîne également des problèmes de conflits d'accès. Afin de garantir l'exactitude des données lors de l'accès dans la méthode, un mécanisme de verrouillage synchronisé est ajouté lors de l' accès . Un thread obtient un verrou exclusif sur un objet et occupe des ressources exclusives. Les autres threads doivent attendre et libérer le verrou après utilisation. Il existe les problèmes suivants :

  • Le fait de maintenir un verrou par un thread entraîne le blocage de tous les autres threads qui ont besoin du verrou ;
  • Dans un contexte de concurrence multithread, le verrouillage et la libération des verrous entraîneront davantage de changements de contexte et de retards de planification, entraînant des problèmes de performances ;
  • Si un thread de haute priorité attend qu'un thread de faible priorité libère le verrou, cela entraînera une inversion de priorité et des problèmes de performances.

4.1. Exemples de threads non sécurisés

1. Billetterie dangereuse

//不安全的买票
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. Il n'est pas sûr de retirer de l'argent

//取钱线程不安全
//两个人去银行取钱,账户
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. Collectes dangereuses

//线程不安全的集合
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éthodes de synchronisation et blocs de synchronisation

  • Puisque nous pouvons utiliser le mot-clé private pour garantir que l'objet de données n'est accessible que par les méthodes, il nous suffit de proposer un mécanisme pour la méthode, qui est le mot-clé synchronisé.

  • Il comprend deux méthodes : la méthode synchronisée et le bloc synchronisé

méthode synchronisée :

public synchronized void method(int args){
    
    }
  • La méthode synchronisée contrôle l'accès aux "objets". Chaque objet correspond à un verrou. Chaque méthode synchronisée doit obtenir le verrou de l'objet qui appelle la méthode avant de pouvoir être exécutée. Sinon, le thread se bloquera. Une fois la méthode exécutée, il occupera exclusivement le verrou jusqu'à ce que la méthode soit exécutée. Il n'est libéré qu'après le retour de la méthode, et le thread qui est bloqué plus tard peut obtenir le verrou et continuer l'exécution.

  • Inconvénient : Si une méthode volumineuse est déclarée synchronisée, l’efficacité sera affectée.

bloc synchronisé :

Bloc synchronisé : synchronisé(Obj){}

Obj est appelé un moniteur de synchronisation

  • Obj peut être n'importe quel objet, mais il est recommandé d'utiliser des ressources partagées comme moniteurs de synchronisation
  • Il n'est pas nécessaire de spécifier un moniteur de synchronisation dans la méthode de synchronisation, car le moniteur de synchronisation de la méthode de synchronisation est celui-ci, qui est l'objet lui-même ou la classe.
  • Synchroniser l'exécution du moniteur
  1. Le premier thread accède, verrouille le moniteur de synchronisation et exécute le code qu'il contient
  2. Le deuxième thread accède et constate que le moniteur de synchronisation est verrouillé et inaccessible
  3. Le premier thread termine l'accès et déverrouille le moniteur de synchronisation
  4. Le deuxième thread accède, constate que le moniteur de synchronisation n'a pas de verrou, puis se verrouille et accède

acheter des billets

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--+"张票");
    }
}

Retirer de l'argent

//取钱线程不安全
//两个人去银行取钱,账户
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);
        }
    }
}

rassembler

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());
    }
}

Collection de types de sécurité JUC

**CopieOnWriteArrayList**

//测试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. Interblocage et verrouillage

Impasse:

Plusieurs threads occupent chacun des ressources partagées et attendent que les ressources de chacun soient occupées par d'autres threads avant de pouvoir s'exécuter, ce qui entraîne une situation dans laquelle deux threads ou plus s'attendent mutuellement pour libérer des ressources et tous deux arrêtent l'exécution. une synchronisation

Lorsqu'un bloc détient des « verrous sur plus de deux objets » en même temps, un problème de « blocage » peut survenir.

Quatre conditions nécessaires pour qu’une impasse se produise :

  1. Condition mutuellement exclusive : une ressource ne peut être utilisée que par un seul processus à la fois.
  2. Conditions de demande et de conservation : lorsqu'un processus est bloqué en raison d'une demande de ressources, il conserve les ressources obtenues.
  3. Condition de non-privation : les ressources obtenues par le processus ne peuvent pas être privées de force avant d'être épuisées.
  4. Conditions d'attente circulaires : plusieurs processus forment une relation d'attente circulaire tête-à-queue pour les ressources.

Tant qu’une ou plusieurs des quatre conditions ci-dessus ne sont pas respectées, un blocage peut être évité.

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 + "获得口红的锁");
            }
        }
    }
}

Verrouillage:

  • À partir du JDK 5.0, Java fournit un mécanisme de synchronisation des threads plus puissant : la synchronisation est obtenue en définissant explicitement des objets de verrouillage de synchronisation. Le verrouillage de synchronisation utilise l'objet Lock comme

  • L'interface java.util.concurrent.locks.Lock est un outil permettant de contrôler l'accès de plusieurs threads aux ressources partagées. Les verrous fournissent un accès exclusif aux ressources partagées. Un seul thread à la fois peut verrouiller l'objet Lock. Le thread doit obtenir l'objet Lock avant de commencer à accéder aux ressources partagées.

  • La classe ReentrantLock implémente Lock, qui a la même concurrence et la même sémantique de mémoire que synchronisé. Dans l'implémentation du contrôle thread-safe, ReentrantLock est plus couramment utilisé et peut afficher les verrous et les libérer.

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();
            }
        }
    }
}

Comparaison entre synchronisé et Lock :

  • Le verrouillage est un verrou explicite (activé et désactivé manuellement), synchronisé est un verrou implicite et est automatiquement libéré lorsqu'il est hors de portée.
  • Lock n'a que des verrous de code, synchronisé a des verrous de bloc de code et des verrous de méthode.
  • En utilisant Lock, la JVM passera moins de temps à planifier les threads et fonctionnera mieux. et a une meilleure évolutivité (offrant plus de sous-classes)
  • Verrouiller > Bloc de code synchronisé (est entré dans le corps de la méthode et a alloué les ressources correspondantes) > Méthode synchronisée (en dehors du corps de la méthode)

5. Discussion sur la communication

Scénario d’application : problématiques producteurs et consommateurs

  • Supposons qu’un seul produit puisse être stocké dans l’entrepôt, que le producteur place le produit fabriqué dans l’entrepôt et que le consommateur retire le produit de l’entrepôt pour le consommer.
  • S'il n'y a pas de produit dans l'entrepôt, le producteur mettra le produit dans l'entrepôt, sinon arrêtera la production et attendra que le produit dans l'entrepôt soit récupéré par le consommateur.
  • S'il y a un produit dans l'entrepôt, le consommateur peut emporter le produit pour le consommer, sinon la consommation s'arrêtera jusqu'à ce que le produit soit à nouveau placé dans l'entrepôt.

Il s'agit d'un problème de synchronisation des threads. Les producteurs et les consommateurs partagent la même ressource, et les producteurs et les consommateurs sont interdépendants et conditionnés les uns aux autres.

  • Pour les producteurs, avant de fabriquer des produits, ils doivent informer les consommateurs d'attendre, et après avoir produit des produits, ils doivent immédiatement informer les consommateurs de consommer.
  • Pour les consommateurs, après la consommation, les producteurs doivent être informés que la consommation est terminée et que de nouveaux produits doivent être fabriqués pour la consommation.
  • Dans le problème producteur-consommateur, la synchronisation seule ne suffit pas

**synchronisé : **Peut empêcher les mises à jour simultanées de la même ressource partagée, réaliser la synchronisation et ne peut pas être utilisé pour implémenter la transmission de messages (communication) entre différents threads

5.1. Plusieurs méthodes pour résoudre la communication par thread

Remarque : ce sont toutes des méthodes de la classe Object et ne peuvent être utilisées que dans des méthodes synchronisées ou des blocs de code synchronisés, sinon illégaleMonitorStateException sera levée.

nom de la méthode effet
attendez() Indique que le thread attend d'être averti par d'autres threads. Contrairement au mode veille, le verrou sera libéré.
attendre (long délai d'attente) Spécifiez le nombre de millisecondes à attendre
notifier() Réveillez un fil en attente
notifierTout() Réveillez tous les threads qui appellent la méthode wait() sur le même objet. Les threads avec une priorité plus élevée sont planifiés en premier.

5.2. Résoudre le problème de la communication entre les threads

5.2.1. Méthode pipeline

Modèle d'écriture concurrente "modèle producteur/consommateur" -> Méthode de traitement

  • Producteur : module chargé de produire des données (il peut s'agir de méthodes, d'objets, de threads, de processus)
  • Consommateur : module responsable du traitement des données (peut être des méthodes, des objets, des threads, des processus)
  • Buffer : les consommateurs ne peuvent pas utiliser directement les données du producteur, il existe un tampon entre eux

Le producteur place les données produites dans le tampon et le consommateur retire les données du tampon.

//测试:生产者消费者模型->利用缓冲区解决:管程法
//生产者、消费者、产品、缓冲区
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éthode lumineuse de signalisation

//测试:生产者消费者模型->利用缓冲区解决:信号灯法
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. Utiliser le pool de threads

Contexte : Les ressources fréquemment créées, détruites et particulièrement utilisées, telles que les threads dans des situations simultanées, ont un impact important sur les performances.

Idée : créez plusieurs threads à l'avance, placez-les dans le pool de threads, obtenez-les directement lorsqu'ils sont utilisés et remettez-les dans le pool après utilisation. Cela peut éviter la création et la destruction fréquentes et réaliser la réutilisation. Semblable aux transports en commun dans la vie.

avantage:

  • Réactivité améliorée (temps réduit pour créer de nouveaux fils de discussion)

  • Réduire la consommation de ressources (réutiliser les threads dans le pool de threads, pas besoin de les créer à chaque fois)

  • Facilite la gestion des threads…

    1. **corePoolSize :**La taille du pool principal
    2. **maximumPoolSize :** Nombre maximum de threads
    3. **keepAliveTime :** La durée maximale qu'un thread peut conserver avant de se terminer lorsqu'il n'a aucune tâche
  • JDK 5.0 fournit des API liées au pool de threads : ExecutorService et Executors

  • ExecutorServic : la véritable interface du pool de threads. Sous-classes communes ThreadPoolExecutor

    • void exécuter (commande Runnable) : exécuter une tâche/commande, aucune valeur de retour, généralement utilisée pour exécuter Runnable
    • Future submit(Callable task) : exécute la tâche, a une valeur de retour et est généralement utilisé pour exécuter Callable
    • void shutdown() : ferme le pool de connexions
  • Exécuteurs : classe d'outils, classe d'usine de pool de threads, utilisée pour créer et renvoyer différents types de pools de threads

6. Résumé du fil de discussion

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;
    }
}

Guess you like

Origin blog.csdn.net/weixin_42823298/article/details/128749759