美文网首页
Java 多线程之Thread类

Java 多线程之Thread类

作者: 程序员汪汪 | 来源:发表于2021-04-15 22:55 被阅读0次

    为什么要使用多线程

    1. 耗时的操作另起一个线程,提高应用程序的响应。
    2. 提高计算机系统CPU的利用率。
    3. 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改。

    应用场景

    1. 程序需要同时执行两个或多个任务。
    2. 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
    3. 需要一些后台运行的程序。

    Thread类

    Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类来体现

    Thread类的特性

    每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体,通过该Thread对象的start()方法来启动这个线程,而非直接调用run()

    常用创建线程的两种方式

    继承Thread类

    步骤:

    1. 创建一个继承于Thread类的子类。
    2. 重写Thread类的run() --> 将此线程执行的操作声明在run()中。
    3. 创建Thread类的子类的对象。
    4. 通过此对象调用start():①启动当前线程 ② 调用当前线程的run()

    需要注意的点:

    1. 我们启动一个线程,必须调用start(),如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。 如果再启动一个线程,必须重新创建一个Thread子类的对象,调用此对象的start()
    2. run()方法由JVM调用,什么时候调用,执行的过程控制都由操作系统的CPU调度决定。
    3. 想要启动多线程,必须调用start()方法。
    4. 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出异常lllegalThreadStateException

    代码示例:

    // 1.继承Thread类
    class MyThread extends Thread {
        // 2.重写run方法
        @Override
        public void run() {
            for (int i = 0; i <= 100; i++) {
                if (i % 2 == 0) {
                    System.out.println(Thread.currentThread().getName() + ": " + i);
                }
            }
        }
    }
    
    public class ThreadTest {
        public static void main(String[] args) {
            // 3.新建Thread对象
    //        MyThread t1 = new MyThread();
            // 4.调用start方法
    //        t1.start();
    //
    //        MyThread t2 = new MyThread();
    //        t2.start();
            //方法二:这个方法用匿名子类
            new Thread(){
                @Override
                public void run() {
                    for (int i = 0; i <= 100; i++) {
                        if (i % 2 == 0) {
                            System.out.println(Thread.currentThread().getName() + ": " + i);
                        }
                    }
                }
            }.start();
    
            new Thread(){
                @Override
                public void run() {
                    for (int i = 0; i <= 100; i++) {
                        if (i % 2 != 0) {
                            System.out.println(Thread.currentThread().getName() + ": " + i);
                        }
                    }
                }
            }.start();
        }
    }
    

    实现Runnable接口的方式

    步骤:

    1. 创建一个实现了Runnable接口的类。
    2. 实现类去实现Runnable中的抽象方法:run()
    3. 创建实现类的对象。
    4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象。
    5. 通过Thread类的对象调用start()

    代码示例:

    // 1.创建一个实现了Runnable接口的类
    class MyRunnable implements Runnable {
        // 实现Runnable接口中的抽象方法run()
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
    
    public class RunnableTest {
    
        public static void main(String[] args) {
            // 3.创建实现类的对象
            MyRunnable runnable = new MyRunnable();
            // 4.将此对象作为参数传递到Thread类的构造其中,创建Thread对象
            Thread thread = new Thread(runnable);
            // 5.通过Thread类的对象调用start()
            thread.start();
    
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "**main**");
            }
            // 还可以这么写,和Thread类的匿名子类很相似,但是注意区别。
    //        new Thread(new Runnable() {
    //            @Override
    //            public void run() {
    //                for (int i = 0; i < 100; i++) {
    //                    System.out.println(Thread.currentThread().getName() + ":" + i);
    //                }
    //            }
    //        }).start();
        }
    
    }
    

    两种方式的对比

    开发中优先选择:实现Runnable接口的方式。

    原因:

    1. 实现的方式没有类的单继承性的局限性。
    2. 实现的方式更适合来处理多个线程共享数据的情况。
    // Thread类实现了Runnable
    public class Thread implements Runnable 
    

    相同点:

    两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。 目前两种方式,要想启动线程,都是调用的Thread类中的start()

    Thread类的常用方法

    常用方法

    1. start():启动当前线程;调用当前线程的run(),只有Thread类和他的子类才能调用start()方法。
    2. run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中。
    3. currentThread():静态方法,返回执行当前代码的线程。
    4. getName():获取当前线程的名字。
    5. setName():设置当前线程的名字。
    6. yield():释放当前cpu的执行权。
    7. join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态。
    8. stop():已过时。当执行此方法时,强制结束当前线程。
    9. sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态。
    10. isAlive():判断当前线程是否存活。

    线程的优先级

    总共有1~10,10个级别

    • MAX_PRIORITY:10
    • MIN _PRIORITY:1
    • NORM_PRIORITY:5 -->默认优先级

    获取和设置当前线程的优先级:

    • getPriority():获取线程的优先级
    • setPriority(int p):设置线程的优先级

    说明:高优先级的线程要抢占低优先级线程CPU的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只当高优先级的线程执行完以后,低优先级的线程才执行。

    线程通信:wait()notify()notifyAll() :此三个方法定义在Object类中的。

    线程的分类

    • 守护线程,如:垃圾回收线程,依赖于主线程而存在。
    • 用户线程,如:main方法的线程。

    Thread的生命周期

    线程的五种状态:

    • 新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
    • 就绪:处于新建状态的线程被star()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
    • 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能
    • 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态
    • 死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
    image-20210415000504972.png

    说明:

    1. 生命周期关注两个概念:状态、相应的方法。
    2. 关注状态a切换为状态b:哪些方法执行了;或者某个方法主动调用导致状态a切换到状态b。
    3. 阻塞:临时状态,不可以作为最终状态。
    4. 死亡:最终状态。

    相关文章

      网友评论

          本文标题:Java 多线程之Thread类

          本文链接:https://www.haomeiwen.com/subject/mtfslltx.html