Java中Thread类的基本用法

目录

创建线程的五种方法

线程等待和阻塞

Thread中的核心属性和方法


 创建线程的五种方法

Java中创建线程有五种方法。

1)创建Thread类的子类,重写run方法

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

注:run方法描述了线程要执行什么任务,start方法表示创建线程,线程就会自动的把run进行执行。

2)实现Runnable接口,重写run,把Runnable实例传入到Thread中。

class MyRunnable implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
         Thread t = new Thread(new MyRunnable());
         t.start();
         while (true){
             System.out.println("hello main");
             try {
                 Thread.sleep(1000);
             } catch (InterruptedException e) {
                 throw new RuntimeException(e);
             }
         }
    }
}

注:Runnable的作用,是描述了一个“任务”,这个任务和具体的执行机制无关,(通过线程的方式执行,还是通过其他的方式执行),run也就是要执行的任务内容本身了。

第一种写法是Thread自己记录我要干啥,自己记下来自己的作业。

第二种写法,通过Runnable记录作业是啥。Thread负责执行,别人给你把作业记下来了,然后Thread负责执行。(将两个功能剥离开,可以起到解耦合的作用)

3、4)本质上是一个写法,只是使用匿名内部类实现

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

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

注:这里创建了Thread的子类,子类内部重写了父类的run方法,同时创建了该子类的实例,对于匿名内部类来说,只能创建一个实例,这个实例创建完了之后,再也拿不到这个匿名内部类了。

正常的编程语言一般是使用“匿名函数”的机制来完成的,但是在Java中函数不能独立存在,必须要包裹上一个类,于是引入了匿名内部类。而匿名内部类的写法很别扭,于是就引入了lambda表达式。

5)使用lambda表达式,就是对匿名内部类的代替。本质上就是一个“匿名函数”,一次性的函数,用完就丢。

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

上述五种写法,本质上都是1)要把线程执行的任务内容表示出来。2)通过Thread的start来创建/启动系统中的线程。(Thread对象和操作系统内核中的线程是一一对应的关系)

线程等待和阻塞

线程终止

  终止线程,在Java中,都只是“提醒,建议”,真正要不要终止,还得线程本体来进行决定的。系统原生的线程中,其实是有办法让别的线程被强制终止的。这种设定,其实不太好,所以java没有采纳过来。

Thread类提供了interrupt方法和isInterruptted方法,来实现上述的效果。

currentThread()是个static方法,能够获取到当前线程,能获取到t这个引用。

isInterrupted()查看一个boolean变量,是线程内置的标志位,true表示线程终止,false表示线程继续执行

interrupt方法相当于直接设置标志位为true.

线程等待

多个线程,调度顺序,在系统中是无序的(抢占式执行)。多个线程啥时候被调度执行,不确定。我们不想要这种效果。那么通过线程等待,就能够确定线程结束的先后顺序

Thread类中提供join方法令线程等待(进入阻塞状态)

阻塞:该线程暂时不参与cpu调度执行,解除阻塞继续执行,线程重新参与到cpu调度了。

main线程中调用t.join()就是让main等待t,也就是t先结束,main后结束

join()除了无参数版本,还有带参数版本 

传入的参数即是等待的最大时间,如果时间到了t还没结束,就继续往下走。

线程休眠

  sleep()方法控制线程休眠,及强制令线程进入阻塞状态一段时间。注意此处设置sleep的时间是线程阻塞的时间。设定时间之内,线程一定不会去cpu上执行(阻塞),当时间到了之后,线程从阻塞状态恢复到就绪状态,不代表就能立即去cpu上执行。从恢复就绪到真正去cpu上执行,还是需要一定时间的(具体是多少,就看机器配置,系统繁忙程度了)。

Thread中的核心属性和方法

  Thread还提供了关于线程的其他属性和方法。

优先级:虽然java提供了优先级接口,实际上就算修改了优先级,现象也不明显。修改是一回事,系统调度又是另外一回事,这里的优先级只能是一个“建议参考”,具体还是人家系统以自身为准。

是否前台线程: 

前台线程:这样的线程不运行结束的话,此时java进程是一定不会结束的。

后台线程:这样的线程,即使继续在执行,也不能阻止java进程结束。

前台线程可以有多个,多个前台线程必须得是最后一个前台线程结束,整个进程才结束。

在java代码中,main线程就是前台线程,程序员创建出来的线程,默认都是前台线程,可以通过上述setDaemon方法来把线程设置为后台线程。

是否存活:判断系统中的线程pcb是否还存在。

Thread对象的生命周期,和pcb的生命周期是不一定完全一样的。

这个代码已经创建了Thread的实例,Thread对象已经诞生了,但是,此时内核中的pcb还没有诞生。t.start()之后,才是真正在系统中创建出线程(pcb才真正创建出来并且加入到链表中)。

由于t线程中的内容,啥都没写。所以t瞬间就结束了,内核中的线程和pcb就被销毁了。但是在sleep结束之前,t引用指向的Thread对象,仍然是存在的,并没有被GC(垃圾回收机制)回收掉。此时就看到,系统中的线程先结束了,但是t仍然还在。

这个写法,就会导致线程还没执行完毕,但是t指向的对象就要被GC回收了。所以Thread对象的生命周期和系统中的线程的生命周期是不一致的。就可以通过上述的isAlive方法,判定系统中的线程是否依然存在。

  以上,关于Thread类的介绍,希望对你有所帮助。

  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值