Thread类及常见方法【Java】

36 篇文章 0 订阅
29 篇文章 0 订阅

多线程

Thread类及常见方法
线程状态



前言

Thread类是JVM用来管理线程的一个类,每个线程都有唯一的一个Thread对象与之对应。


一、Thread 类及常见方法

Thread 类的构造方法

Thread类提供的构造方法有许多,目前我们需要了解的主要有:
在这里插入图片描述

  • Thread ( ) 创建线程对象
public static void main(String[] args) {
        Thread t=new Thread(){
            @Override
            public void run() {
                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        };
        t.start();
    }
  • Thread (Runnable) 使用Runnable对象创建对象
public class Test2 {
    public static void main(String[] args) {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("使用Thread t=new Thread(new Runnable())创建线程对象");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}
  • Thread (String name) 创建线程对象并命名
public static void main(String[] args) {
        Thread t=new Thread("线程1"){
            @Override
            public void run() {
                while (true){
                    System.out.println("线程名为“线程1”的线程");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                

            }
        };
        t.start();
    }

这里使用一个死循环保持程序运行不退出,然后在jconsole.exe处查看线程情况:

在这里插入图片描述

  • Thread (Runnable String) 使用Runnable 对象创建线程对象,并命名;
public static void main(String[] args) {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println("使用Runnable创建对象并命名为“线程2”");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"线程2");
        t.start();
    }

同样查看线程情况:
在这里插入图片描述

Thread 的几个常见属性

在这里插入图片描述

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

                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

        },"线程1");
        t.start();

        System.out.println(t.getId());
        System.out.println(t.getName());
        System.out.println(t.getState());
        System.out.println(t.getPriority());
        System.out.println(t.isDaemon());
        System.out.println(t.isAlive());
        System.out.println(t.isInterrupted());
    }

运行结果:
在这里插入图片描述

getId( )获取到的是线程在JVM中的身份标识;
getName( )获取到的是我们再构造方法创建的名字;
getState( )这里获取到的状态是JVM内部设立的状态体系,是一瞬间的状态,相比操作系统内置的状态要更加丰富;
getPriority()优先级越高越容易被操作系统调度到;
isDaemon( )方法中,daemon称为“守护线程”或“后台线程”;线程可以分为前台线程和后台线程,一个线程创建成功以后默认是前台线程,只有所有的前台线程都执行完毕,进程才会退出,而并不关注后台线程是否执行完毕,因此前台线程会阻止进程结束,后台线程不会;
可以通过setDaemon( )将线程设置为后台线程;
isAlive()可以简单理解为run()方法是否执行结束;


二、启动一个线程

通过重写run( )方法,可以创建一个线程对象,想要线程真正开始启动,就需要借助start( )方法;
run()方法与start ( )方法的区别:

  • 重写run()只是指明了线程需要做的事并没有创建新的线程;
  • start( )则是真正的创建了一个新线程,这个新线程调用了run( )方法;

调用 start 方法, 才真的在操作系统的底层创建出一个线程;


三、中断线程

手动设置标志位

public class Test6 {
    //设置一个布尔变量(成员变量)来表示线程是否要结束

    private static boolean isQuit=false;    //初始默认不结束
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while (!isQuit){
                System.out.println("线程运行中...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程执行结束");
        });
        t.start();

        Thread.sleep(3000);
        System.out.println("控制新线程退出");
        isQuit=true;
    }
}

在这里插入图片描述

使用Thread自带的标志位

Thread.currentThread().isInterrupted()

public class Test7 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while (!Thread.currentThread().isInterrupted()){
                System.out.println("线程运行中");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();

                }
            }
        });
        t.start();
        Thread.sleep(5000);
        System.out.println("控制新线程退出");
        t.interrupt();
    }
}

在这里插入图片描述

这里的currentThread()是一个静态方法,可以获取到当前线程的实例;
isInterrupted()就是用来判定内置情况的标志位,当它为true时,表示线程要被中断;
interrupt()方法用来中断Thread对象关联的线程,当这个线程没有处在阻塞状态时,该方法就会修改内置的标志位;当该线程正处在阻塞状态时,这个方法就会让线程内部产生阻塞的方法抛出异常;

因此关于上面代码的运行结果:使用isInterrupted()方法判断了内置的标志位为false,线程不被中断,进入了循环,打印了一系列的 “线程运行中…” ,之后由于sleep()的使用,使线程处在了阻塞状态, 之后interrupt()方法的调用让sleep()方法抛出了异常,我们使用e.printStackTrace()打印了异常的信息,而这样的一系列操作下来,其内置的标志位并没有被修改,因此异常抛出以后程序依旧在运行。

而正是这样的异常捕获操作,使得程序员可以自行控制线程的退出行为:

  • 直接退出
public class Test8 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("线程运行中...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //打印异常信息之后直接退出
                    break;
                }
            }

        });
        t.start();
        Thread.sleep(5000);
        System.out.println("控制线程退出");
        t.interrupt();
    }
}

在这里插入图片描述

  • 稍作等待,然后退出
public class Test8 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("线程运行中...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    //e.printStackTrace();
                    System.out.println("线程即将退出");
                    try{
                        Thread.sleep(4000);
                    }catch (InterruptedException e1){
                       e1.printStackTrace();
                    }
                    break;

                }
            }
            System.out.println("线程已经退出");

        });
        t.start();
        Thread.sleep(5000);
        System.out.println("控制线程退出");
        t.interrupt();
    }
}

在这里插入图片描述

  • 忽略异常,不退出
public class Test7 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while (!Thread.currentThread().isInterrupted()){
                System.out.println("线程运行中");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("线程即将退出");

                }
            }
        });
        t.start();
        Thread.sleep(5000);
        System.out.println("控制新线程退出");
        t.interrupt();
    }
}

在这里插入图片描述
这样,通过这3种方式,当主线程宣布退出的时候,新线程就可以自己来决定用哪种方式退出;
Thread.currentThread().isInterrupted()是"不清除标志位":

public class Test9 {
    public static void main(String[] args) {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println(Thread.currentThread().isInterrupted());
                }

                }

        });
        t.start();
        t.interrupt();
    }
}

在这里插入图片描述
标志位一直都是true,因为标志位没有被清;

Thread.interrupted()

Thread.isInterrupted()与Thread.currentThread().isInterrupted()的区别就在于:前者是清除标志位,以开关举例,就是会自动恢复的开关;

public class Test9 {
    public static void main(String[] args) {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println(Thread.interrupted());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t.start();
        t.interrupt();
    }
}

在这里插入图片描述

只有一开始是 true,后边都是 false,因为标志位被清;


四、等待线程-join

有时候,我们需要等待一个线程结束以后再进行下一步的操作,等待一个线程结束就需要使用 join( );

/*
* main等待t2结束,t2等待t1结束
* */
public class Test1 {
    private static Thread t1=null;
    private static Thread t2=null;

    public static void main(String[] args) throws InterruptedException {
        System.out.println("main begin");
        t1=new Thread(()->{
            System.out.println("t1.begin");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1.end");
        });
        t1.start();
        t2=new Thread(()->{
            System.out.println("t2.begin");
            try {
                t1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t2.end");
        });
        t2.start();

        t2.join();

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

在这里插入图片描述

/*
* 先执行t1,t1执行完再执行t2
* */
public class Test2 {
    private static Thread t1=null;
    private static Thread t2=null;


    public static void main(String[] args) throws InterruptedException {
        System.out.println("main begin");

        t1=new Thread(()->{
            System.out.println("t1 begin");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("t1 end");
        });
        t1.start();
        t1.join();

        t2=new Thread(()->{
            System.out.println("t2 begin");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t2 end");
        });
        t2.start();

        t2.join();

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

在这里插入图片描述

线程之间的执行顺序是随机的,因此我们无法决定哪个线程先执行,但是使用join( )我们可以控制哪个线程先结束;

join( )这种方式的等待类似于死等,即调用者没有结束时将会一直等待下去;
join(long millis) 这种等待方式是设置了一个时间,若在规定的时间内没有结束,将不再继续等待;
join(long millis, int nanos) 这种与上面设置时间的等待类似,但是时间的设置可以更加精确;


五、获取线程实例

很多时候,为了对线程进行自然就需要获取到线程的实例,我们使用currentThread( )来获取当前线程的引用;
在这里插入图片描述


六、休眠线程

sleep( )即让线程休眠一定的时间;

当一个线程调用sleep( ),就是把这个线程从就绪队列调到了阻塞队列;
而就绪队列的特点是这里的线程可以随时上CPU运行;
阻塞队列中的线程则是暂时不参与操作系统的调用,不上cpu执行;
因此 t.sleep(1000)就代表着,线程 t 从就绪队列调到了阻塞队列,在1000ms之后再调回到就绪队列;
因此sleep(1000)表示休眠时间不小于1000ms,一般情况下是略大于1000ms的,因为在就绪队列的线程也不一定就会马上调用,还是依据操作系统的调用情况;


over!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值