Java-Thread 类的基本用法(详细说明)

 

本篇博客,我将为大家介绍Thread类的基本用法,包括线程创建、中断、等待、休眠和获取线程实例等方面,希望对大家有一些帮助和参考

目录

一、线程的创建

1.1 继承Thread的方式去创建线程

 1.2 通过实现Runnable接口创建线程

1.3 使用匿名内部类创建线程

1.4 使用lambda 表达式创建 Runnable 子类对象

1.5 start方法和run方法的区别

二、线程中断

三、线程等待

四、线程休眠

五、获取当前线程引用


 

一、线程的创建

1.1 继承Thread的方式去创建线程

1) 继承 Thread 来创建一个线程类.

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("这里是线程运行的代码");
   }
}

2) 在main方法里创建 MyThread 类的实例

MyThread t = new MyThread();

3) 调用 start 方法启动线程

t.start(); // 线程开始运行

完整代码如下:

public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("t");
    }

    public static void main(String[] args) {
        MyThread t=new MyThread();
        t.start();//启动线程
        System.out.println("main");//主线程的打印
    }
}

运行结果如下:

c43a15aa54714567b4d89c03d5742734.png

 1.2 通过实现Runnable接口创建线程

1) 实现 Runnable 接口

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("这里是线程运行的代码");
   }
}

2) 创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数.

Thread t = new Thread(new MyRunnable());

3) 调用 start 方法

t.start(); // 线程开始运行

完整代码如下:

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("这里是线程运行的代码");
    }
    public static void main(String[] args) {
        Thread t=new Thread(new MyRunnable());
        t.start();
        System.out.println("main");
    }
}

运行结果如下:

2f98e99f583241c8b6e04a51858534d6.png

1.3 使用匿名内部类创建线程

这里将介绍俩种实现方式
以下是第一种方式,就是继承thead的方式去实现

//使用匿名内部类实现继承thread
public class ThreadDemo3 {
    public static void main(String[] args) {
        Thread t=new Thread(){
            @Override
            public void run(){
            while (true){
                System.out.println("t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            }
        };
        t.start();
        while (true){
            System.out.println("main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 另外一种是实现runable接口的方式去实现线程的创建

//匿名内部类实现ThreadRunable接口

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

1.4 使用lambda 表达式创建 Runnable 子类对象

public class MyThread {
    public static void main(String[] args) {
        Thread t=new Thread(() ->{
            while (true){
                System.out.println("t");
            
            }
        });
        t.start();
        while (true) {
            System.out.println("main");
           
        }
    }

使用lambda表达式是为了使代码看起来更加简洁,跟其他创建线程方法功能一样,除此之外,工作中使用的最多的也是这种

运行结果如下:

93daee1459a94bfda85a1f95eaf30781.png

可能大家会好奇为什么不是依次交替执行的呢,本期疑问下期会给大家讲,先留一个悬念 :)

1.5 start方法和run方法的区别

下面来介绍一下,start方法和run方法的区别:
run() 方法只是线程的一个普通方法,调用它不会创建一个新的线程,而只是按顺序执行其中的代码。因此,如果直接调用 run() 方法,那么其中的代码会在当前线程中执行,不会开启新的线程。

start() 方法才是真正启动一个新线程并开始执行其中的 run() 方法。当调用 start() 方法时,Java 会创建一个新的线程,并在新的线程中执行 run() 方法中的代码。因此,如果要启动一个新线程并执行其中的代码,就必须使用 start() 方法。


二、线程中断

我们现来具体的描述一下 ,什么是线程中断,我们来描述一下:线程中断是指在多线程程序中,中断正在运行的线程的执行,简单来说就是让程序停下来,通常是由其他线程通过调用目标线程的interrupt()方法来实现。

我们来看下面一个例子:

李四一旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我们 需要增加一些机制,例如老板突然来电话了,说转账的对方是个骗子,需要赶紧停止转账,那张三该如 何通知李四停止呢?这就涉及到我们的停止线程的方式了。

目前常见的有以下两种方式: 1. 通过共享的标记来进行沟通 2. 调用 interrupt() 方法来通知

案例1-使用自定义的变量来作为标志位.

需要给标志位上加 volatile 关键字,这个关键字的功能我们后面介绍

public class ThreadDemo {
    private static class MyRunnable implements Runnable {
        public volatile boolean isQuit = false;
        @Override
        public void run() {
            while (!isQuit) {
                System.out.println(Thread.currentThread().getName()
                        + ": 别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
           }
            System.out.println(Thread.currentThread().getName()
                    + ": 啊!险些误了大事");
       }
   }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        System.out.println(Thread.currentThread().getName()
                + ": 让李四开始转账。");
        thread.start();
        Thread.sleep(10 * 1000);
        System.out.println(Thread.currentThread().getName()
                + ": 老板来电话了,得赶紧通知李四对方是个骗子!");
        target.isQuit = true;
   }
}

案例2-使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位.

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

方法说明
public void interrupt()中断对象关联的线程,如果线程正在阻塞,则以异常方式通知, 否则设置标志位
public static boolean interrupted()判断当前线程的中断标志位是否设置,调用后清除标志位
public boolean isInterrupted()判断对象关联的线程的标志位是否设置,调用后不清除标志位

使用 thread 对象的 interrupted() 方法通知线程结束.

public class ThreadDemo {
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            // 两种方法均可以
            while (!Thread.interrupted()) {
            //while (!Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getName()
                        + ": 别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
               } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().getName()
                            + ": 有内鬼,终止交易!");
                    // 注意此处的 break
                    break;
               }
           }
            System.out.println(Thread.currentThread().getName()
                    + ": 啊!险些误了大事");
       }
   }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        System.out.println(Thread.currentThread().getName()
                + ": 让李四开始转账。");
        thread.start();
        Thread.sleep(10 * 1000);
        System.out.println(Thread.currentThread().getName()
                + ": 老板来电话了,得赶紧通知李四对方是个骗子!");
        thread.interrupt();
   }
}

thread 收到通知的方式有两种: 1. 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通 知,清除中断标志

附:当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择 忽略这个异常, 也可以跳出循环结束线程.

2. 否则,只是内部的一个中断标志被设置,thread 可以通过Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志

或者通过Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志

这种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到。

案例3-观察标志位是否清除

使用 Thread.isInterrupted() , 线程中断会清除标志位.

public class ThreadDemo {
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.interrupted());
           }
       }
   }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        thread.start();
        thread.interrupt();
   }
}

运行结果如下:

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

​

使用 Thread.currentThread().isInterrupted() , 线程中断标记位不会清除.

public class ThreadDemo {
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
 System.out.println(Thread.currentThread().isInterrupted());
           }
        }
   }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        thread.start();
        thread.interrupt();
   }
}

运行结果如下:

true // 全部是 true,因为标志位没有被清
true
true
true
true
true
true
true
true
true

三、线程等待

有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转 账成功,才决定是否存钱,这时我们可以使用join方法明确等待线程的结束。

方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos)同理,但可以更高精度
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Runnable target = () -> {
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() 
                                       + ": 我还在工作!");
                    Thread.sleep(1000);
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
           }
            System.out.println(Thread.currentThread().getName() + ": 我结束了!");
       };
        Thread thread1 = new Thread(target, "李四");
        Thread thread2 = new Thread(target, "王五");
        System.out.println("先让李四开始工作");
        thread1.start();
        thread1.join();
        System.out.println("李四工作结束了,让王五开始工作");
        thread2.start();
        thread2.join();
        System.out.println("王五工作结束了");
   }
}

运行结果如下:

我们可以看到王五线程会等李四线程结束了再开始工作,工作中某些场景可能会涉及到

0af61887a13942ac9a92375474e8c8a1.png

四、线程休眠

也是我们比较熟悉一组方法,有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证实 际休眠时间是大于等于参数设置的休眠时间的

方法说明
public static void sleep(long millis) throws InterruptedException休眠当前线程 millis 毫秒
public static void sleep(long millis, int nanos) throws InterruptedException可以更高精度的休眠
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(System.currentTimeMillis());
        Thread.sleep(3 * 1000);//让此线程休眠3秒
        System.out.println(System.currentTimeMillis());
   }
}

上面的代码演示是让上面的线程休眠3秒

 

五、获取当前线程引用

方法说明
public static Thread currentThread();返回当前线程对象的引用
public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();//获取当前线程
        System.out.println(thread.getName());//获取当前线程的名字
   }
}

本期内容就到这里,下期我会给大家分享更多内容

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马可波罗.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值