• java多线程-多线程技能


    继承Thread类

    一个类继承Thread类后创建新线程

    package mycode.chapter1;
    
    /**
     * @author 29467
     * @date 2022/9/2 22:30
     */
    public class MyThread extends Thread{
        @Override
        public void run() {
            super.run();
            System.out.println("MyThread");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    package mycode.chapter1;
    
    /**
     * @author 29467
     * @date 2022/9/2 15:12
     */
    public class Run3 {
        public static void main(String[] args) {
            MyThread myThread = new MyThread();
            myThread.start();
            System.out.println("运行结束!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    运行结果可以看出,新线程创建比运行结束!文字输出要慢

    运行结束!
    MyThread
    
    • 1
    • 2

    如果让主线程休眠200ms,那样顺序将会反过来

    package mycode.chapter1;
    
    /**
     * @author 29467
     * @date 2022/9/2 15:12
     */
    public class Run3 {
        public static void main(String[] args) throws InterruptedException {
            MyThread myThread = new MyThread();
            myThread.start();
            // 主线程休眠三秒
            Thread.sleep(200);
            System.out.println("运行结束!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    运行结果

    MyThread
    运行结束!
    
    • 1
    • 2

    使用常见的命令分析现成的信息

    1. 使用jps+jstack,在java的bin目录下打开cmd,运行以下代码后输入jps即可看到现成的状态,输入jstack -l 进程id后可以看指定id的状态
    package mycode.chapter1;
    
    /**
     * @author 29467
     * @date 2022/9/2 22:40
     */
    public class Run {
        public static void main(String[] args) {
            for(int i = 0; i<5; i++){
                new Thread(){
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(500000);
                        }catch (InterruptedException e){
                            e.printStackTrace();
                        }
                    }
                }.start();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    1. 利用官方工具jmc查看
    2. 利用工具jvisualvm查看

    线程随机性的展现

    package mycode.chapter1;
    
    /**
     * @author 29467
     * @date 2022/9/2 22:50
     */
    public class MyThread2 extends Thread{
        @Override
        public void run() {
            for(int i =0; i<10000;i++){
                System.out.println("run="+Thread.currentThread().getName());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    package mycode.chapter1;
    
    /**
     * @author 29467
     * @date 2022/9/2 22:51
     */
    public class Run2 {
        public static void main(String[] args) {
            MyThread2 thread2 = new MyThread2();
            thread2.setName("thread2");
            thread2.start();
            for (int i = 0; i < 10000; i++) {
                System.out.println("main=" + Thread.currentThread().getName());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    通过输出可见,main和thread2两个线程随机打印

    执行start()的顺序不代表执行run()的顺序

    实现Runnable接口

    package mycode.chapter1;
    
    /**
     * @author 29467
     * @date 2022/9/2 23:03
     */
    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println("运行中");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    package mycode.chapter1;
    
    /**
     * @author 29467
     * @date 2022/9/2 23:04
     */
    public class Run4 {
        public static void main(String[] args) {
            Runnable runnable =new MyRunnable();
            Thread thread =new Thread(runnable);
            thread.start();
            System.out.println("运行结束");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    使用Runnable接口实现多线程的优点

    可以实现接口来达到“多继承”的功能

    实现Runnable接口与继承Thread类的内部流程

    实现Runnable接口法在执行过程上相比继承Thread法稍微复杂一些

    实例变量共享造成的非线程安全问题与解决方案

    1. 不共享数据的情况
    package mycode.chapter1;
    
    /**
     * @author 29467
     * @date 2022/9/2 23:22
     */
    public class MyThread3 extends Thread{
        private int count = 5;
    
        public MyThread3(String name) {
            super();
            this.setName(name);
        }
    
        @Override
        public void run() {
            super.run();
            while (count>0){
                count--;
                System.out.println("由"+ currentThread().getName()+"计算,count="+count);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    package mycode.chapter1;
    
    /**
     * @author 29467
     * @date 2022/9/2 23:26
     */
    public class Run5 {
        public static void main(String[] args) {
            MyThread3 a = new MyThread3("A");
            MyThread3 b = new MyThread3("B");
            MyThread3 c = new MyThread3("C");
            a.start();
            b.start();
            c.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    从运行结果可见,他们的count值是非共享的,每个线程单独负责自己的count

    由C计算,count=4
    由A计算,count=4
    由B计算,count=4
    由A计算,count=3
    由C计算,count=3
    由A计算,count=2
    由C计算,count=2
    由B计算,count=3
    由C计算,count=1
    由A计算,count=1
    由C计算,count=0
    由B计算,count=2
    由A计算,count=0
    由B计算,count=1
    由B计算,count=0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    1. 共享数据的情况
    package mycode.chapter1;
    
    /**
     * @author 29467
     * @date 2022/9/2 23:22
     */
    public class MyThread4 extends Thread{
        private int count = 5;
    
        @Override
        public void run() {
            super.run();
            while (count>0){
                count--;
                System.out.println("由"+ currentThread().getName()+"计算,count="+count);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    package mycode.chapter1;
    
    /**
     * @author 29467
     * @date 2022/9/2 23:30
     */
    public class Run6 {
        public static void main(String[] args) {
            MyThread4 myThread4 = new MyThread4();
            Thread a = new Thread(myThread4,"A");
            Thread b = new Thread(myThread4,"B");
            Thread c = new Thread(myThread4,"C");
            Thread d = new Thread(myThread4,"D");
            Thread e = new Thread(myThread4,"E");
            a.start();
            b.start();
            c.start();
            d.start();
            e.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    这样的话多个线程将会共用数据,因为不加锁,会导致一系列问题,改进方案如下:添加关键字synchronized在run()方法前

    package mycode.chapter1;
    
    /**
     * @author 29467
     * @date 2022/9/2 23:22
     */
    public class MyThread4 extends Thread{
        private int count = 5;
    
        // 添加关键字synchronized
        @Override
        synchronized public void run() {
            super.run();
            while (count>0){
                count--;
                System.out.println("由"+ currentThread().getName()+"计算,count="+count);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    Servlet技术造成的非线程安全问题与解决方案

    多线程调用servlet会导致赋值时机错误问题,可在线程调用的servlet方法前添加synchronized关键字加锁解决


    留意System.out.println()出现的非线程安全问题

    println()方法在内部是同步的,作用范围仅限内部


    currentThread()方法

    返回代码段正在被哪个线程调用


    isAlive()方法

    判断当前的线程是否存活


    sleep(long millis)方法

    sleep()方法能让“正在执行的进程”休眠x毫秒


    sleep(long million, int nanos)方法

    毫秒+纳秒


    StackTraceElement[] getStackTrace()方法

    返回一个表示该线程的堆栈跟踪元素数组。第一个元素表示栈顶,是该数组中的最新方法调用,最后一个元素是栈底,表示最早的方法调用。


    static void dumpStack()方法

    将当前线程的堆栈跟踪信息输出至标准错误流。


    static Map geyAllStackTrace()方法

    返回所有活动线程的堆栈跟踪的一个映射。


    getId()方法

    可以取得线程的唯一标识


    停止线程

    1. interrupted():测试currentThread()是否已经中断,执行后具有清除状态标志值的功能
    2. isInterrupted():测试this关键字所在类的对象是否已经中断,不清除状态标志值

    用stop()方法暴力停止线程

    该方法并不能确定停止的位置,调用时会抛出java.lang.ThreadDeath异常,不需要显式捕捉。


    使用stop()释放锁给数据造成不一致的结果


    使用"return"语句停止线程的缺点与解决方案

    多个判断条件下,使用return会导致代码冗余,推荐抛异常throw new InterruptedException()


    暂停线程

    使用suspend()方法暂停线程,使用resume()方法来恢复线程的执行。


    suspend()方法与resume()方法的缺点—独占

    在独占期间,其他线程无法访问


    suspend()与resume()方法的缺点—数据不完整


    yield()方法

    yield()方法的作用是放弃当前的CPU资源,让其他任务去占用CPU执行时间,放弃的时间不确定


    线程的优先级

    优先级较高的线程获得的cpu资源更多,设置线程的优先级用setPriority()方法,优先级分为1-10个等级,超出范围会报错


    线程优先级的继承特性

    线程的优先级具有继承性,线程继承后优先级相同


    优先级的规律性

    优先级可以用setPriority()方法设置,cpu尽量给优先级较高的线程


    优先级的随机性

    优先级高的线程并不是先执行完毕


    优先级对线程运行速度的影响

    优先级高的运行速度快


    守护线程

    Java中分为用户线程(非守护线程),和守护线程
    当进程中不存在非守护线程了,则守护线程自动销毁
    凡是调用setDaemon(true)代码并且传入值的线程才是守护线程

  • 相关阅读:
    Mybatis框架
    还在为 Dubbo 服务写 Controller?因为未使用 ShenYu 网关
    GIS工具maptalks开发手册(五)01-用JSON载入地图——json格式绘制多个面之基础版
    学长大数据面试------头条面试题
    【链表的说明、方法---顺序表与链表的区别】
    关于接口|常见电商API接口种类|接口数据类型|接口请求方法
    Git 的基本概念和使用方式
    Protobuf编码规则
    Java架构师数据库设计
    【最简便方法】element-plus/element-ui走马灯配置图片以及图片自适应
  • 原文地址:https://blog.csdn.net/weixin_51343683/article/details/126671525