我是靠谱客的博主 负责云朵,最近开发中收集的这篇文章主要介绍Thread类讲解,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

一、Thread 常见的构造方法

      (1)无参数的构造方法

      (2)传 Runnable 对象的构造方法

      (3)传入 String 类的构造方法 (给线程起名)

 二、Thread 类的几种常见属性

​        ① getId()方法

        ② getName()方法

        ③ getState()方法

        ④ getPriority()方法

        ⑤ isDaemon()方法

        ⑥ isAlive() 方法

三、启动线程

四、中断线程

五、线程等待

六、获取当前线程引用

七、线程休眠


一、Thread 常见的构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用 Runnable 对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名
【了解】Thread(ThreadGroup group,
Runnable target)
线程可以被用来分组管理,分好的组即为线程组,这
个目前我们了解即可

      (1)无参数的构造方法

Thread thread = new Thread();

      (2)传 Runnable 对象的构造方法

class MyRunnable implements Runnable{

    @Override
    public void run() {
           //... 线程具体的工作
    }
}

public class Demo8 {
    public static void main(String[] args) {
        Thread thread = new Thread(MyRunnable);
    }
}

      (3)传入 String 类的构造方法 (给线程起名)

我们在创建线程时,可以给线程起名字,方面我们调试观察分析。 

public class Demo8 {
    public static void main(String[] args) {
        Thread t = new Thread(() ->{
            while(true){
                System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"这是我的线程");
        t.start();
    }
}
注意:这里不要把变量名 ‘t’ 和线程名 “这是我的线程” 弄混,前者只存在代码代码中,运行起来就变成字节码文件了,后者只存在运行状态。

代码运行起来之后,我们可以在第三方软件里面看到我们创建线程的名字。(以 jconsole命令为例)

 二、Thread 类的几种常见属性

         ① getId()方法

作用:获取线程的id,返回一个 long 类型的值,值是随机的

    public static void main(String[] args) {
        Thread t = new Thread(()->{

        });
        t.start();
        System.out.println("t 线程id:"+t.getId());
        System.out.println("main 线程id:"+Thread.currentThread().getId());//获取 main 线程 ID
    }

 注意:这里获取的 id 是 Java 中给 Thread 对象安排的身份标识,和操作系统内核职工的PCB 的 pid,以及和操作系统提供线程 API 中的线程id 都不是一回事!!!

        ② getName()方法

 作用:当前获取线程的名字

    public static void main(String[] args) {
        Thread t = new Thread(()->{

        },"这是我的线程");
        t.start();
        System.out.println(t.getName());
    }

        ③ getState()方法

 作用:获取线程的状态

在 Java中,线程状态有六种分别是:

  1. New 初始
  2. Runnable  运行
  3. Blocked 阻塞
  4. Waiting  等待
  5. Timed_Waiting 超时等待
  6. Terminated 终止
    public static void main(String[] args) {
        Thread t = new Thread(()->{

        });
        t.start();

        System.out.println(t.getState());
    }

 

        ④ getPriority()方法

 作用:获取线程的优先级

    public static void main(String[] args) {
        Thread t = new Thread(()->{

        });
        t.start();

        System.out.println("t 线程的优先级:"+t.getPriority());
        Thread main = Thread.currentThread();
        System.out.println("main 线程的优先级:"+main.getPriority());
    }

 

        ⑤ isDaemon()方法

 作用:获取线程是否是 “后台”线程,是则返回 true,不是则返回 false。

    public static void main(String[] args) {
        Thread t = new Thread(()->{

        });
        t.start();

        System.out.println(t.isDaemon());
    }

   一般默认情况下,我们创建的都是 “前台”线程,前台线程会阻止进程退出。如果是后台线程,后台线程不阻止进程退出。

简而言之,如果 main 运行完了,前台线程还没运行完,进程不会退出。

如果 mian 等其他前台线程执行完了,这时即使后台线程没执行完,进程也会退出。

我们看下面代码:

    public static void main(String[] args) {
        Thread t = new Thread(() ->{
            while(true){
                System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"这是我的线程");
        t.start();

        System.out.println("main 线程执行完了");
    }

此时创建的是 前台线程,我们看运行结果,发现当 main主线程执行完时,我们创建的线程还在运行。

 现在来创建一个后台线程,大家来看一下结果。

使用 setDaemon() 方法可以把前台线程设置为后台线程

注意:setDaemon() 方法必须在 start()方法前调用,否则会修改不了

此时我们能看到,我们创建的线程只打印了一条 “hello”。当主线程执行完后,我们创建的后台线程也随之消失了。

        ⑥ isAlive() 方法

作用:检测线程是否还存活,是则返回 true,否则返回 false 

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
        });
        Thread t2 = new Thread(()->{
        });

        t1.start();
        System.out.println("t1 是否存活:"+t1.isAlive());

        t2.start();
        Thread.sleep(1000);//让 main 线程休眠 1000ms
        System.out.println("t2 是否存活:"+t2.isAlive());
    }

三、启动线程

启动线程一般用创建的线程对象来调用 start() 方法即可,如下

 只有调用 start 才会真正创建线程!!!不调用 start 就是没创建线程(在内核里创键PCB)

注意区分,调用start 和 run 的区别: 

        一般情况,我们创建的线程对象调用run方法和start方法时,产生的结果可能会相同,这就会导致我们误以为 run() 和 start() 没有区别!

   直接调用run方法并没有创建线程,只是在原来的线程中运行代码

   而直接调用start方法,则是创建一个新的线程,在新线程中执行代码(和原来的线程是并发的)

我们来看下面代码:

class MyThread extends Thread{
    @Override
    public void run() {
        while(true){
            try {
                System.out.println("hello Thread");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Thread t1 = new MyThread();

        t1.run();
        //t1.start();
        
        while(true){
            try {
                System.out.println("hello main");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

当t1只调用 run() 方法时的结果:

 当t1调用 start() 方法时的结果:

        我们会发现,t1调用run()方法只会打印“hello Thread”。这是因为是main线程调用的run()方法!!代码顺序执行,同时run()方法是一直循环,所以就不会打印“hello main”。

        而t1调用start()方法,则是创建一个新线程,新线程调用run,所以我们会看到 “hello Thread”和“hello main”都有打印。 

四、中断线程

       一般情况下,只有 run方法执行完了,线程才结束,但是有点时候我们想要线程提前结束那该怎么办呢?这就涉及到我们中断线程的方式了。

        中断线程一般会设置标志位,而设置标志位分两种方式:

  1. 直接自己定义一个标志位,作为线程是否结束的标记
  2. 使用标准库里自带的一个标志位

    (1)自定义标志位

自定义一个变量作为一个标志位,通过对变量的修改来结束run

public class Demo2 {
    private static boolean isQuit = false;

    public static void main(String[] args) {
        Thread t = new Thread(()->{
           while(!isQuit){
               System.out.println("hello Thread");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });

        t.start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        isQuit = true;
        System.out.println("设置让 t 线程结束");
    }
}

      (2)使用 Thread.currentThread().isInterrupted() 作为标志位

currentTrhead():是Thread类的静态方法,通过这个方法,可以拿到当前线程的实例

isInterrupted():调用这个方法判断标志位

         在主线程中,通过 t.interrupt 来中断这个线程,设置标志位为 true!

public class Demo3 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
           while(!Thread.currentThread().isInterrupted()){
               System.out.println("hello Thread");
           }
            System.out.println("t 执行完");
        });

        t.start();

        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t.interrupt();
        System.out.println("设置让 t 线程结束");
    }
}

 注意:interrupt 方法的行为,有两种情况

     ① 当 t 线程在运行状态时,会设置标志位(Thread.currentThread().isInterrupted())为 true.

例如上述代码。

     ② 当 t 线程在阻塞状态时(sleep),就不会设置标志位,而是触发一个 InterruptedException,这个异常会把 sleep 提前唤醒。

上图代码中,处理异常只是打印日志,并没有结束循环。所以这种情况并不能中断线程

 这时我们要想中断线程或者其他操作,我们就要在捕获异常的代码块中进程操作!

 提示:在 java 中,中断线程并非强制的!!! 由线程自身的代码来进行判断处理的!!!

线程怎么处理,取决于代码怎么写!

五、线程等待

        一般情况下,线程之间的调度顺序,是不确定的!但是可以通过一些特殊操作,来对线程的执行顺序做出干预。

        其中 join 就是一个方法,控制线程之间的结束顺序!!

例如:

       创建一个线程 t,并让他循环打印 “hello” 五次,同时在main线程中调用 t.join()。

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            for (int i = 0; i < 3; i++) {
                System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t.start();

        System.out.println("main 线程 join 之前");
        t.join();
        System.out.println("main 线程结束");
    }

结果如下: 

 我们发现 main 线程是等待 t 线程工作结束后才继续执行,结果是确定的。

t.join() 效果就是:让 mian 线程阻塞等待,等到线程之间的结束顺利!!

而如果我们不加 t.join(),运行结果就是随机的了。如下

       在实际开发中很少会用到 无限等待的方法,大多数都是指定了最大等待,避免程序因为死等,导致“卡死”的情况。

join() 带参数的方法如下:

方法说明
public void join(long millis)等待线程结束,最多等待 millis 毫秒
public void join(long millis,int nanos)

等待 millis 毫秒 + nanos 纳秒(精确版)

 六、获取当前线程引用

方法说明
public static Thread currentThread()返回当前线程对象的引用

        获取当前这个线程对应 Thread 对象的引用,哪个线程里面调用,得到的就是哪个线程的引用。 

    public static void main(String[] args) {
        Thread t = new Thread(()->{
            System.out.println(Thread.currentThread().getName());
        });
        t.start();

        Thread ct = Thread.currentThread();
        System.out.println(ct.getName());
    }

 七、线程休眠

        sleep 方法 可以指定休眠时间,让线程休息(相当于阻塞一会)

方法说明
public static void sleep(long millis) throws InterruptedException休眠当前线程 millis 毫秒
public static void sleep(long millis,int nanos) throws InterruptedException休眠 millis 毫秒 + nanos 纳秒
    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();//记录开始时间(时间戳)

        Thread.sleep(1000);//让main先休眠 1000毫秒

        Long end = System.currentTimeMillis();//记录结束时间

        System.out.println("耗时:"+(end - start));
    }

 

         sleep 的原理就是让原本在 就绪队列 中的线程,转移到 阻塞队列 中去,等 sleep 的时间到了,再转移到 就绪队列 中。​

最后

以上就是负责云朵为你收集整理的Thread类讲解的全部内容,希望文章能够帮你解决Thread类讲解所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(52)

评论列表共有 0 条评论

立即
投稿
返回
顶部