【Java多线程编程】Thread类

Thread类是什么?

Thread 类是 Java 提供的一个标准库,我们可以通过 Thread 类进行多线程编程。因此,今天我给大家讲解的是如何使用 Thread 类进行线程编程。

详细讲解 Thread 类中的:lambda 表达式、start 方法(启动线程)、sleep 方法(休眠线程)、currentThread 方法(返回当前线程的实例)、isInterrupted 方法(标志位false)、Interrupted 方法(标志位true)。

目录

1. Thread类创建线程

2. 启动线程strat()方法

3. 线程的等待join()方法

4. 获取当前线程的引用

5. 休眠当前线程

6. 中断一个线程

1. Thread类创建线程

Thread 类创建线程,我们得实例化一个 Thread 类对象。实例化 Thread 类对象来创建进程我们使用最多的就是 lambda 表达式。lambda 表达式语法简单表示为:()->{}

因此,我们可以这样创建一个线程:

    public static void main(String[] args) {
        Thread thread = new Thread(()-> {
            System.out.println("这是thread引用内的线程");  
        });
    }

以上代码,实例化了 Thread类的引用 thread,并在 thread 引用内创建了一个线程,线程为一句话“这是thread引用内的线程”。

当然,main 方法在上述代码中作为主线程,thread 引用作为子线程

我们通常创建线程使用的就是 lambda 表达式,但创建线程还有其他几种方式,创建线程的几个基本方式在这篇文章中有详细讲解:创建线程的基本方式


2. 启动线程strat()方法

当我们创建好一个线程后,我们可以使用 start() 方法来启动这个线程。start() 方法是 Thread 类里面的一个方法作用是启动线程,因此我们可以通过 Thread 类的引用来调用 start() 方法来启动线程。

    public static void main(String[] args) {
        Thread thread = new Thread(()-> {
            System.out.println("这是thread引用内的线程");
        });
        thread.start();
    }

运行后打印:

通过以上代码,我们可以了解到启动线程的方式是调用 start()方法。在启动线程的过程中,start() 方法作为线程的启动、thread 引用作为线程的入口

  • 在 thread 引用创建后,thread 引用内的 线程并为启动,因此不存在操作系统中
  • 调用 start() 方法后,才真正在操作系统底层创建一个线程

3. 线程的等待join()方法

当子线程、主线程里面都有相应的线程时,我们不能保证是子线程内的线程先执行还是主线程内的线程先执行,如下代码:

    public static void main(String[] args) {
        Thread thread = new Thread(()-> {
            System.out.println("Hello thread");//thread引用内的线程
        });
        thread.start();//启动thread引用内的线程
        System.out.println("Hello main");//main方法里面的线程
    }

以上代码可能输出 Hello thread、Hello main或者Hello main、Hello thread。

假设我们设计上述代码的初衷就是先输出 Hello thread 后输出 Hello main,这样就不能达到需求。

因此,我们可以使用 Thread 类底下的 join() 方法来等待当前线程执行完毕后,再执行后序线程

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("Hello thread");//run方法里面的线程
            }
        };
        thread.start();//启动线程
        thread.join();//等待线程
        System.out.println("Hello main");//main方法里面的线程
    }

运行后输出:

以上代码中调用了 join() 方法,使得 main 线程中的 thread 先执行完毕后再继续往下执行。因此就能到先输出 Hello thread 后输出 Hello main 的效果。

注意,我们在调用 join() 方法后,需要使用 Alt+Enter join() 来抛出 throws InterruptedException异常。这样就能很好的调用join() 方法。

  • main 线程中 thread 调用 join() 方法的时,如果 thread 还在运行,此时的 main 线程会发生阻塞,直到 thread 执行完毕后 main 方法继续往下执行。
  • main 线程 中 thread 调用 join() 方法时,如果 thread 已经结束,此时的 join() 方法不会造成阻塞,main 线程继续执行!

4. 获取当前线程的引用

获取当前线程的引用,我们可以 currentThread() 方法返回当前线程对象的引用:

    public static void main(String[] args) {
        Thread thread = new Thread(()-> {
            System.out.println("Hello thread");
            System.out.println("thread里面的线程: "+Thread.currentThread());
        });
        thread.start();//启动thread引用内的线程
    }

运行后输出:

 Thread[Thread-0,5,main]的含义为:

  1. Thread-0代表线程名
  2. 5指的是线程优先级为5(优先级默认为5)
  3. main指的是线程所处的线程组

线程名默认为 Thread-0 ,我们可以通过初始化的方式来设置线程名。设置方法在 lambda 表达式后设置,格式为: ()->{},线程名。如下代码:

    public static void main(String[] args) {
        Thread thread = new Thread(()-> {
            System.out.println("Hello thread");
            System.out.println("thread里面的线程: "+Thread.currentThread());
        },"thread线程");
        thread.start();//启动thread引用内的线程
    }

运行后输出:


也可以通过线程调用 getName() 方法来获取线程的名字。如下方代码所示:

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()-> {
            System.out.println("Hello thread");
        },"thread线程");
        thread.start();//启动thread引用内的线程
        thread.join();//等待thread线程执行完毕
        System.out.println(thread.getName());//输出thread的线程名
    }

运行后输出:


5. 休眠当前线程

休眠当前线程的方式为调用 sleep() 这个方法,sleep() 方法是 Thread 类底层的一个方法,用来休眠线程。sleep() 方法里面要加参数,参数值为毫秒单位,1秒对应1000毫秒

    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            int flag = 3;//线程运行次数
            while (flag > 0) {
                System.out.println("thread线程");
                try {
                    Thread.sleep(1000);//休眠一秒钟
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                flag--;
            }
        });
        thread.start();//启动线程
    }

运行后输出:

以上程序,通过 sleep(1000) 休眠了1秒线程,总共休眠了三次。最后输出三条语句,大家可以自行尝试一下感受停顿感。

注意,当我们调用 sleep 方法时会出现的 Unhandled exception... 这个异常意为:未处理中断异常

因此,我们可以 Alt+Enter sleep() 方法。来 try/carch 这个异常。大家不必关注 catch 内的内容 e.printStackTrace() 意为打印当前位置的调用栈,后期深入学习则会更加理解。 


6. 中断一个线程

举个例子:张三是一个线程,线程内容为:输入转账金额,输入密码,转账。

有一天,张三在微信上给陌生人转账,此时张三进入了工作状态。当达到输入密码步骤时,警察蜀黍给张三打个了电话要求张三停止输入密码,避免损失金钱。此时警察的阻止中断了张三这个线程。这样的一个例子就是中断线程的操作,那么在 Java 中如果我们想要中断一个线程该如何去做呢?

中断一个线程意味停止一个线程,并非终止一个线程。本质上来说,让一个线程终止,就是让该线程的入口方法提前执行完毕。因此,可设置一个标志位来达到该效果。


当我们使用循序运行一个线程:

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

当我们写出以上代码时,会一直打印 Hello thread。如果我们想要特定的时候线程就中断了,可以设置一个特定的标志位来达到该效果。

把 while 循序中的条件自定义为一个特定的标志位 isExit ,然后通过特定限制来达到中断效果,如以下代码:

public class TestDemo {
    public static boolean isExit = false;//全局变量isExit
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()-> {
            while (!isExit) {
                System.out.println("Hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();//启动thread线程
        
        Thread.sleep(3000);//main线程里面的sleep方法
        isExit = true;//修改全局变量isExit
    }
}

运行后输出:

以上代码中,thread 线程内的 sleep() 和 main 线程中的 sleep() 方法在操作系统中几乎是同时运行的,因此当 main 线程中的 sleep() 达到 3 秒钟后标志位 isExit 修改为 true 最后程序结束,输出了三条语句。

注意,标志位是全局变量,如果标志位是局部变量,lambda 表达式识别不出这个标志位。


当然,在 Thread 类内部提供了一个标志位可通过 isInterrupted() 方法,它的默认值为 false。和 Interrupted() 方法默认为 true,来中断线程,因此可以写出以下代码:

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()-> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("Hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();//启动thread线程

        Thread.sleep(3000);//main线程里面的sleep方法
        
        thread.interrupt();//interrupt把标志位设置为true
    }

运行后输出:

通过上述程序我们发现,打印三条语句后抛出了一个异常,继续无限打印 Hello thread。

解释:

抛出的异常为 catch 中的 e.printStackTrace() 意为打印当前位置的调用栈

interrupt 方法在线程阻塞(如正在执行 sleep 方法)时就会把阻塞状态唤醒也就会抛出一个异常让 sleep 方法立即结束。 

当 sleep 方法被唤醒时,sleep 会自动把标志位清空,也就是把通过 Interrupted 方法设置好的标志位的 true 清空,因此标志位恢复为 false,这样就会导致线程继续往下执行。

好比一个开关,有的开关按下去了就一直保持按下去的状体,有的开关按下去了会自动把按钮弹起来(类似于sleep方法)。

因此,我们直接在 catch 中加上一个 break 语句,这样在抛出异常后循环就会结束。

catch (InterruptedException e) {
        e.printStackTrace();
        break;
    }

🧑‍💻作者:程序猿爱打拳,Java领域新星创作者,阿里云社区博客专家。

🗃️文章收录于:Java多线程编程

🗂️JavaSE的学习:JavaSE

🗂️Java数据结构:数据结构与算法

  

本篇博文到这里就结束了,感谢点赞、收藏、关注~

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一只爱打拳的程序猿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值