Thread类是什么?
Thread 类是 Java 提供的一个标准库,我们可以通过 Thread 类进行多线程编程。因此,今天我给大家讲解的是如何使用 Thread 类进行线程编程。
详细讲解 Thread 类中的:lambda 表达式、start 方法(启动线程)、sleep 方法(休眠线程)、currentThread 方法(返回当前线程的实例)、isInterrupted 方法(标志位false)、Interrupted 方法(标志位true)。
目录
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]的含义为:
- Thread-0代表线程名
- 5指的是线程优先级为5(优先级默认为5)
- 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数据结构:数据结构与算法
本篇博文到这里就结束了,感谢点赞、收藏、关注~