• java多线程深度修炼


    1.线程常用方法

    第一组

    /**
     * 线程常用方法
     */
    public class ThreadMethod {
        public static void main(String[] args) throws InterruptedException {
            TextThread textThread = new TextThread();
            // 设置线程名称
            textThread.setName("dahe");
            // 设置线程优先级
            textThread.setPriority(Thread.MIN_PRIORITY);
            // 启动子线程
            textThread.start();
            // 获取线程优先级
            System.out.println(textThread.getPriority());
            // 中断线程休眠
            // textThread.interrupt();
    
        }
    }
    
    class TextThread extends Thread {
        @Override
        public void run() {
            // 线程休眠
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            // 获取当前线程的名称
            System.out.println(Thread.currentThread().getName());
        }
    }
    ------------------------------------
    输出:
    1
    dahe
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    第二组(新增join和yield)

    /**
     * 线程常用方法
     */
    public class ThreadMethod {
        public static void main(String[] args) throws InterruptedException {
            TextThread textThread = new TextThread();
            // 设置线程名称
            textThread.setName("dahe");
            // 设置线程优先级
            textThread.setPriority(Thread.MIN_PRIORITY);
            // 启动子线程
            textThread.start();
            // 获取线程优先级
            System.out.println(textThread.getPriority());
            // 中断线程休眠
            // textThread.interrupt();
            DDD ddd = new DDD();
            ddd.start();
            for (int i = 0; i < 10; i++) {
                // 线程礼让,让出CPU,但礼让的时间不确定,所以也不一定礼让成功
                if (i == 2) {
                    Thread.yield();
                }
                // 让子线程插队,ddd先执行完再继续执行子线程
                if (i == 5) {
                    ddd.join();
                }
                Thread.sleep(1000);
                System.out.println("主线程吃包子" + i);
            }
        }
    }
    
    class TextThread extends Thread {
        @Override
        public void run() {
            // 线程休眠
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            // 获取当前线程的名称
            System.out.println(Thread.currentThread().getName());
        }
    }
    
    class DDD extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("我是DDD!我在吃包子" + i);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    2.守护线程

    线程分为用户线程和守护线程

    • 用户线程:也叫工作线程,当线程的任务执行完或者以通知的形式中止
    • 守护线程:一般是为工作线程服务的,当所有的用户线程结束后,守护线程自动开启

    常见的守护线程:垃圾回收机制🐭

    程序示例:

    /**
     * 守护线程
     */
    public class DaemonThread {
        public static void main(String[] args) throws InterruptedException {
            AAA aaa = new AAA();
            // 希望主线程结束后,子线程可以自动结束,将子线程设置为守护线程即可
            aaa.setDaemon(true);
            aaa.start();
            for (int i = 0; i < 10; i++) {
                Thread.sleep(300);
                System.out.println("主线程");
            }
        }
    }
    
    class AAA extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 1000; i++) {
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("我是AAA!我在吃包子" + i);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    输出分析:

    主线程
    我是AAA!我在吃包子0
    主线程
    我是AAA!我在吃包子1
    主线程
    我是AAA!我在吃包子2
    主线程
    我是AAA!我在吃包子3
    主线程
    我是AAA!我在吃包子4
    主线程
    我是AAA!我在吃包子5
    主线程
    我是AAA!我在吃包子6
    主线程
    我是AAA!我在吃包子7
    主线程
    我是AAA!我在吃包子8
    主线程
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    虽然子线程存在1000此的循环输出,但是其为守护线程,主线程退出后,其自动销毁😶‍🌫️


    3.线程的七大状态

    线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

    下图显示了一个线程完整的生命周期。

    在这里插入图片描述


    4.互斥锁

    使用了synchronized标识之后,所标识的内容就会加入互斥锁🔒在任意时刻,只能有一个线程访问该对象

    注意:这会导致程序的执行效率降低😈

    例如:(在方法上面加锁🔒)

    class SellTicketRes implements Runnable {
        private int ticketNum = 100;
        private boolean loop = true;
    
        /**
         * 使用synchronized实现线程同步,解决超卖
         * 这时的锁在this对象
         */
        public synchronized void Sell() {
            if (ticketNum <= 0) {
                System.out.println("没有余票!");
                loop = false;
                return;
            }
            // 休眠卖票
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("窗口" + Thread.currentThread().getName() +
                    "售出一张票。" + "剩余票数:" + (--ticketNum));
        }
        @Override
         public void run() {
            while (true) {
                if (loop == false) {
                    break;
                }
                Sell();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    为了增大效率,也可以不在方法中同步,转而在代码块中进行同步加锁:

    注意,这时的锁依然是在this对象

    class SellTicketRes implements Runnable {
        private int ticketNum = 100;
        private boolean loop = true;
    
        /**
         * 使用synchronized实现线程同步,解决超卖
         * 这时的锁在this对象
         */
        public void Sell() {
            synchronized (this) {
                if (ticketNum <= 0) {
                    System.out.println("没有余票!");
                    loop = false;
                    return;
                }
                // 休眠卖票
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("窗口" + Thread.currentThread().getName() +
                        "售出一张票。" + "剩余票数:" + (--ticketNum));
            }
        }
        @Override
         public void run() {
            while (true) {
                if (loop == false) {
                    break;
                }
                Sell();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    同步静态方法的锁,是加在当前类本身,例如:ThreadLock.class

    // 同步静态方法的锁,是加在当前类本身ThreadLock.class
    public synchronized static void mm(){
    
    }
    
    • 1
    • 2
    • 3
    • 4

    所以在静态方法的内部的代码块枷锁,需要传入当前的类信息:

    public static void yy() {
        synchronized (ThreadLock.class) {
            System.out.println("猪猪!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5.线程的死锁

    多个线程都占用了对方的锁资源,但不肯相让,导致了死锁🔒,死锁🔒是多线程开发的大忌!❌

    为了方便理解,我们来看下面一段程序:

    /**
     * 线程死锁
     */
    class DeadLock extends Thread {
        static Object o1 = new Object();
        static Object o2 = new Object();
        boolean flag;
    
        public DeadLock(boolean flag) {
            this.flag = flag;
        }
    
        @Override
        public void run() {
            if (flag) {
                synchronized (o1) {
                    System.out.println(Thread.currentThread().getName() + "进入1");
                    synchronized (o2) {
                        System.out.println(Thread.currentThread().getName() + "进入2");
                    }
                }
            } else {
                synchronized (o2) {
                    System.out.println(Thread.currentThread().getName() + "进入3");
                    synchronized (o1) {
                        System.out.println(Thread.currentThread().getName() + "进入4");
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    分析:

    • 如果flag为T,线程A就会先持有o1对象锁
    • 接下来继续执行,出现了o2锁,线程A会尝试获取o2对象锁,但是如线程A得不到o2锁,就会进入blocked的状态
    • 如果flag为F,线程B就会线持有o2对象锁
    • 接下来继续执行,出现了o1锁,线程B会尝试获取o1对象锁,但是如果此时线程A在阻塞状态(也就是占用了o1锁)。那么线程B也会进入blocked状态
    • 双方都拿到了对方都想要的锁,互相不放行,最终造成线程死锁🔒

    最终两个线程并发执行的时候,会发生死锁,输出以下并且卡住❌:

    Thread-0进入1
    Thread-1进入3
    
    • 1
    • 2

    6.释放锁

    释放锁的情况:

    • 当前线程的同步方法/代码块执行完毕,自动释放🔒
    • 在同步方法/代码块中遇到break,return
    • 在同步方法/代码块中出现了未处理的ErrorException
    • 当前线程在同步方法/代码块中执行了线程对象的wait方法,当前线程会暂停并暂时释放锁🔒

    注意,以下的操作并不会释放锁:

    • 调用Thread.sleep()方法,Thread.yield()方法暂停当前线程的执行,并不会释放锁
    • 其他线程调用了该线程的suspend()方法将该线程挂起,不会释放锁
  • 相关阅读:
    ConfigurationClassPostProcessor 如何放入processors中
    Flume学习笔记(4)—— Flume数据流监控
    .NET周报【10月第2期 2022-10-17】
    代码比较模板
    3.7Docker consul的容器服务更新与发现
    jsp城市案件
    【无人机】基于A星算法解决无人机二维路径规划问题含matlab代码
    一文学会Zookeeper
    深度学习笔记(4)——TextCNN、BiLSTM实现情感分类(weibo100k数据集)
    【JavaWeb】第一章 HTML标签
  • 原文地址:https://blog.csdn.net/Gherbirthday0916/article/details/126086320