【JAVAEE && 多线程2 】

本文介绍了Java中的Thread类,包括其常见构造方法、线程属性如ID和优先级,以及如何启动、中断、等待线程。通过start()方法启动线程,使用interrupt()方法通知线程中断,join()方法用于等待线程结束,而sleep()方法则用于让线程休眠。文章还讨论了线程的不同状态,如NEW、RUNNABLE、TERMINATED等。
摘要由CSDN通过智能技术生成

目录

1 Thread类及常见方法

1.1 Thread的常见构造方法 

1.2 Thread的几个常见属性

1.3 启动线程 start()方法

1.4 中断线程

1.5 等待线程 join()

1.6 休眠当前线程 sleep()

 2 线程的状态

1 Thread类及常见方法

  Thread类是JVM用来管理线程的一个类,换句话说,每个线程都有一个唯一的Thread对象与之相关联。

1.1 Thread的常见构造方法 

方法说明
Thread( )创建线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runable target,String name)

使用Runnable对象创建线程对象,并命名

   Thread(String name)中,name参数,是给线程起了个名字,这里的名字,不影响程序执行,只是方便在调试的时候,快速找到该线程。

Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这个是线程的名字");
Thread t4 = new Thread(new MyRunnable(),"这是线程的名字");

1.2 Thread的几个常见属性

属性获取方法

ID 线程编号

getId()
名称getName()
状态getState()
优先级getPriority()
是否后台为线程isDaemon()
是否存活isAlive()
是否被中断islnerrupted()

关于Thread 的常见属性说明:

  ID: 是线程的唯一标识,不同线程不会重复

  名称:是各种调试工具会用到

  状态:表示线程当前所处的一个情况,比如NEW、RUNNABLE、WAITING等等

  优先级:优先级高的线程理论上来说更容易被调度到

  是否为后台线程:JVM会在一个进程的所有非后台线程结束后,才会结束运行

  是否存活:可以理解为run方法是否运行结束了

  是否被中断:涉及到线程如何中断,下文会详细说明

1.3 启动线程 start()方法

   从上一篇文章中提到了创建一个线程,可以通过覆写run方法 进行创建,但创建好的线程对象并不意味着线程就开始运行了,这时需要调用start方法,使线程成功运行起来。

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

没有调用start方法的时候,程序运行起来是这样的:

调用start()方法后:

public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (true){
                System.out.println("hello t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();//千万不能漏写
}

所以说,只有调用了start方法,才算真正在操作系统的底层创建出一个线程 

1.4 中断线程

   中断一个线程,中断的意思就是让一个线程停下来。让一个线程终止,办法就一种,就是让该线程的入口方法执行完毕。

   目前常见的有以下两种方式:

   1、给线程中设定一个结束标志位

   2、调用interrupt()方法来通知

第一种方法:

 public static boolean isQuit = false;//设定一个结束标志位
public class ThreadDemo9 {
    public static boolean isQuit = false;
    public static void main(String[] args) {
        Thread t = new Thread(()->{
           while (!isQuit){
               System.out.println("hello t");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
            System.out.println("t 线程终止");
        });
        t.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isQuit = true;
    }
}

   在上述代码中,如果不设置结束标志位,代码为while(true)循环,这就是一个死循环,会导致入口方法无法执行完毕,自然不能结束线程。

   如果此时把循环条件用一个变量来控制,就能结束线程。该代码首先会启动 t 线程,让t 线程先跑,每隔一秒会进行打印,主线程main休眠3秒后,把isQuit这个退出标记修改为true,此时while循环条件为!true 即false,线程自然就跳出循环,打印终止语句,线程自然就结束了。

    注意:lambda表达式 / 匿名内部类的变量捕获机制:如果把isQuit成员变量修改为局部变量,就不能成功运行,原因是变量捕获;JAVA要求变量捕获,捕获的变量必须是final或者实际final  或者更改为成员变量,lambda就不用受之前变量捕获规则的限制。

第二种方法

  使用Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 来代替自定义标志位。

   Thread内部包含了一个boolean类型的变量作为线程是否被中断的标记

public class ThreadDemo {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            //currentThread是获取到当前线程实例
            //此处currentThread得到的对象就是t
            //isInterrupted就是t对象里自带的一个标志位
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("hello t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //把t内部的标志位给设置成true
        t.interrupt();
    }
}

   上述代码在运行过程中,会发现休眠时间到了之后,调用t.interrupt方法的时候,线程并没有真的结束,而是打印了个异常信息,又继续执行了 。

   原因是 interrupt 方法。

   interrupt方法的作用:

   1 设置标志位为 true

   2 如果该线程正在阻塞中(比如在执行sleep)此时就会把阻塞状态唤醒,通过抛出异常的方式让sleep立即结束。

   注意:当sleep被唤醒的时候,sleep会自动把islnterrupted标志位给清空(true - false)

这就导致下次循环,循环仍然可以继续执行,如果需要结束循环,就得在catch中设置个break。

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

   清空标志位的原因,目的是为了让线程自身能够对于线程何时结束,有一个更明确的控制,当前,interrupt方法的效果,不是让线程立即结束,而是告诉线程,该结束了,至于是否真的要结束,立即结束还是等会结束,都是代码来灵活控制。

  总结:interrupt只是通知,而不是命令。

1.5 等待线程 join()

  由于线程之间是并发执行的,操作系统对于线程的调度是无序的,所以无法判定两个线程谁先执行结束,谁后执行结束。

  有时,我们需要等待一个线程完成它的工作后,才能进行下一步工作。比如微信零钱得先存钱,才可以支付。

public class ThreadDemo11 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            System.out.println("hello t");

        });
        t.start();
        t.join();

        System.out.println("hello main");
    }
}

   上述代码,在main线程中,调用t.join方法,表示让main线程等待 t 线程先结束,再往下执行

  在t.join执行的时候,如果 t 线程还没结束,main线程就会阻塞等待;如果 t 线程已经结束了,此时join不会阻塞,就会立即执行。此外,join还提供了两个版本,都可以填写一个参数,作为“超时时间”。

   join的无参版本,是死等。

   join的有参数版本,则是指定最大超时时间,如果等待的时间到了上限,还没等到,也就不等了。

方法说明
public void join()等待线程结束
public void join(long  millis)等待线程结束,最多等millis毫秒
public void join(long millis,int nanos)同理,但可以更高精度

1.6 休眠当前线程 sleep()

   sleep() 方法也是上一篇文章和这一篇代码中有使用过,不过有一点,因为线程的调度是不可控的,所以sleep方法只能保证实际休眠时间是大于等于参数设置的休眠时间的。

方法说明
public static void sleep(long millis) throws InterruptedException
休眠当前线程 millis 毫秒
public static void sleep(long millis, int nanos) throws
InterruptedException
可以更高精度的休眠

 2 线程的状态

  操作系统里的线程,自身是有一个状态的,但是Java Thread是对系统线程的封装,把这里的状态又进一步精细化了。代码中可以通过getState()方法去或去线程的状态。

线程的状态说明
NEW

系统中的线程还没创建出来,只是有一个Thread对象

TERMINATED

系统中的线程已经执行完了,但Thread对象还在

RUNNABLE

就绪状态,分为正在CPU上运行或者准备好随时可以去CPU上运行

BLOCKED

表示等待锁出现的状态

WAITING

使用wait方法出现的状态

   Thread类以及一些常见方法就介绍到这里啦,后面文章就会更新多线程比较重点的地方啦,多线程带来的风险--线程安全问题

                                                  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值