目录
[1.1 getId()、getName()、getState()、getPririty()](#1.1 getId()、getName()、getState()、getPririty())
[1.2 start()](#1.2 start())
[1.3 isDaemon()、setDaemon()](#1.3 isDaemon()、setDaemon())
[1.4 isAlive()](#1.4 isAlive())
[1.5 currentThread()](#1.5 currentThread())
[1.6 Interrupt()、interrupted()、isInterrupted()](#1.6 Interrupt()、interrupted()、isInterrupted())
[1.6.1 方法一:添加共享的标志位](#1.6.1 方法一:添加共享的标志位)
[1.6.2 方法二:使用内置的标志位](#1.6.2 方法二:使用内置的标志位)
[1.6.3 Java中终止线程不是强制性的](#1.6.3 Java中终止线程不是强制性的)
[1.7 sleep()](#1.7 sleep())
[1.8 join()](#1.8 join())
一、Thread概述
|----------------------------------------------------------------------------------------------|
| Thread类是JVM用于管理线程的类,每一个线程都与一个唯一的Thread对象相关联,即每个执行流都由一个Thread对象进行描述,这些对象被JVM组织,用于线程调度和管理。 |
二、构造方法
|-------------------------------------------|---------------------------------------|
| 构造方法 | 说明 |
| Thread() | 创建线程对象 |
| Thread(Runnable target) | 使用Runnable接口实现类对象,创建线程对象 |
| Thread(String name) | 创建线程对象,并为线程对象命名 |
| Thread(Runnable target, String name) | 使用Runnable接口实现类对象,创建线程对象,并为线程对象命名 |
| Thread(TreadGroup group, Runnable target) | 指定线程组,使用Runnable接口实现类对象,创建线程对象 |
三、常用方法
|---------------------|-----------------------|
| 常用方法 | 说明 |
| getId() | 获取线程ID |
| getName() | 获取线程名 |
| getState() | 获取线程状态 |
| getPririty() | 获取线程优先级 |
| start() | 启动线程 |
| isDaemon() | 判断线程是否为后台线程(守护线程) |
| setDaemon() | 设定线程是否为后台线程(守护线程) |
| isAlive() | 判断线程是否"存活" |
| currentThread() | 获取当前线程的引用 |
| Interrupt() | 终止一个线程 |
| interrupted() | 判断当前线程标志位状态 |
| isInterrupted() | 判断对象线程标志位状态 |
| sleep() | 休眠线程 |
| join() | 阻塞线程 |
1.1 getId()、getName()、getState()、getPririty()
|---------------------------------------|
| getId() |
| 获取线程ID,ID是线程的唯一标识,由JVM自动分配并确保唯一性。 |
|-------------------------------------|
| getName() |
| 获取线程名,线程名可以自动生成,也可以自定义。线程名可以重复。 |
|-------------------------------------------------------------|
| getState() |
| 获取线程状态,线程的状态有就绪、阻塞等。Java中现成的状态使用枚举保存,可以通过遍历枚举获得所有状态的描述。 |
阅读指针 -> 《Java中线程有多少种状态(State)?状态之间的关系有什么关系?》
|------------------------------------------------|
| getPririty() |
| 获取线程优先级,优先级高的线程理论上更容易被调度使用,但是在Java中优先级的效果并不明显。 |
++操作演示:++
java
class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println("这是MyThread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Thread_Demo6 {
public static void main(String[] args){
Thread thread = new MyThread();
System.out.println("ID:"+thread.getId());
System.out.println("线程名:"+thread.getName());
System.out.println("状态:"+thread.getState());
System.out.println("优先级:"+thread.getPriority());
}
}
++打印结果:++
ID:20
线程名:Thread-0
状态:NEW
优先级:5
1.2 start()
|------------------------------------------------------------------------------------|
| start() 启动线程 |
| 1)通过重写Thread中的run方法可以创建一个线程对象,再通过调用start()方法,启动这个线程。此时,操作系统中的线程才真正被创建出来。 |
| 2)Thread调用start()创建出的线程,底层仍然是调用系统的API来进行创建线程的操作。 |
| 3)Thread类使用start方法启动线程,对于同一个Thread对象,start方法只能调用一次,需要启动多少个线程,就需要创建多少个Thread对象。 |
| 4)start()和run()的区别在于,run方法是提供了线程需要运行的内容,而start方法才是真正让线程运行起来。 |
++同一个Thread对象不能多次调用start方法演示:++
java
class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<2;i++){
System.out.println("这是MyThread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("MyThread-run()运行结束");
}
}
public class Thread_Demo9 {
public static void main(String[] args) {
Thread thread = new MyThread();
//第一次启动线程thread;
thread.start();
//第二次启动线程thread;
thread.start();
}
}
++打印结果:++
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:708)
at Learn_Thread.Demo9.Thread_Demo9.main(Thread_Demo9.java:32)这是MyThread
这是MyThread
MyThread-run()运行结束
可以看到两次调用start方法,只有一次成功执行,另一次报错IllegalThreadStateException
1.3 isDaemon()、setDaemon()
|-----------------------------------------------------------------------------------|
| isDaemon() 判断线程是否为后台线程 |
| setDaemon() 设定线程是否为后台线程 |
| 1)daemon的意思是守护,因此也将后台线程称为守护线程。与后台线程相呼应,还有前台线程。 |
| 2)代码创建的线程,默认为前台线程。当setDaemon()方法的参数为false时,线程将被设置为前台线程,当参数为true时,线程将被设置为后台线程。 |
| 3)前台线程的运行时,将阻止进程结束;后台线程运行时,不会阻止进程结束。 |
| 4)因此为什么将后台线程称为守护线程?就是说进程需要我就在,进程不要我就走,在背后默默守护进程的线程嘛??? |
++后台进程的执行演示:++
java
class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println("这是MyThread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Thread_Demo7 {
public static void main(String[] args) {
Thread thread = new MyThread();
//设置为守护线程(后台线程);
thread.setDaemon(true);
//开始thread;
thread.start();
//main线程等待两秒后结束,此时守护线程还有代码没打印,但也随之结束了;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main线程结束");
}
}
++打印结果:++
这是MyThread
这是MyThread
main线程结束
启动main线程和thread线程,在main线程sleep两秒结束线程后,可以看到thread线程的run()方法打印结果确实不是预期中的五次"这是MyThread"。这意味着thread线程也随着main线程的结束而结束了。
1.4 isAlive()
|---------------------------------------------------------------------------------|
| isAlive() 判断线程是否"存活" |
| 1)Java中的线程类Thread对象实例,虽然表示一个线程,但这个实例的生命周期与系统内核中的线程的生命周期是不同的。 |
| 2)Thread对象创建了就存在,但此时如果调用isAlive()得到的结果将会是false,因为内核中的线程此时还不存在。 |
| 3)只有当调用start()启动线程之后,内核中的线程启动,调用isAlive()得到的结果才会是true。 |
| 4)当线程运行结束,系统内核中的线程也随之结束,此时虽然Thread对象还存在,但是调用isAlive()得到的结果也将是false。 |
| 5)因此,可以简单将这个方法认为是用于判断系统内核中的线程(PCB)是否存在。 从代码层面来讲,可以认为是用于判断run()方法是否执行完毕。 |
++演示判断线程是否"存活":++
java
class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<2;i++){
System.out.println("这是MyThread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("MyThread-run()运行结束");
}
}
public class Thread_Demo8 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new MyThread();
//线程已有实例,判断isAlive();
System.out.println("start前,thread是否存活:"+thread.isAlive());
//启动线程,判断isAlive();
thread.start();
System.out.println("start中,thread是否存活:"+thread.isAlive());
//等待run()执行结束,此时实例依旧存在,判断isAlive();
Thread.sleep(5000);
System.out.println("start结束,thread是否存活:"+thread.isAlive());
}
}
++打印结果:++
start前,thread是否存活:false
start中,thread是否存活:true这是MyThread
这是MyThread
MyThread-run()运行结束
start结束,thread是否存活:false
1.5 currentThread()
|------------------------------------------------------------|
| currentThread() 获取当前线程的引用 |
| 1)在currentThread()方法返回的打印信息中,有三个值,分别代表[线程名,线程优先级,所在线程组]。 |
++获取线程引用操作演示:++
java
public class Thread_Demo11 {
public static void main(String[] args) {
//打印main线程信息;
System.out.println(Thread.currentThread());
}
}
++打印结果:++
Thread[main,5,main]
中括号中的三个值分别代表:线程名,线程优先级,所在线程组。
1.6 **Interrupt()、interrupted()、**isInterrupted()
++如果想要中断(终止)一个线程,可以有多种方法,以下介绍两种:++
|----------|----------------------|
| 方法一: | 通过共享的标志进行线程间沟通。 |
| 方法二: | 调用Interrupt()方法。 |
1.6.1 方法一:添加共享的标志位
java
class MyThread extends Thread{
//设置共享的标志位;
public volatile boolean isQuit = false;
@Override
public void run() {
//根据标志位的变化,决定后续执行;
while (!isQuit){
System.out.println("这是MyThread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("MyThread-run()运行结束");
}
}
public class Thread_Demo10 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new MyThread();
//启动线程;
thread.start();
//延时两秒后更改标志位;
Thread.sleep(2000);
((MyThread) thread).isQuit = true;
}
}
++打印结果:++
这是MyThread
这是MyThread
这是MyThread
MyThread-run()运行结束
1.6.2 方法二:使用内置的标志位
|-----------------------------------------|----------------------------------------------|
| 方法 | 说明 |
| public void interrupt() | 如果线程处于阻塞状态,则抛出异常; 如果线程不处于阻塞状态,则终止线程。 |
| public static boolean interrupted() | 判断当前线程标志位状态。 |
| public boolean isInterrupted() | 判断对象线程标志位状态。 |
| 1)上文由程序员手动设置了一个共享的标志位,用于控制线程的执行。Java中也提供了相应的封装好的方法,内置了标志位。使用以上三个方法,一样可以达到控制线程的执行效果。 ||
++通过Interrupt()终止线程,然后对线程标志位状态进行判断:++
java
class MyThread extends Thread{
@Override
public void run() {
//使用isInterrupted()判断标志位状态;
while (!Thread.currentThread().isInterrupted()){
System.out.println("这是MyThread");
//打印当前线程标志位的状态;
System.out.println(Thread.interrupted());
}
System.out.println("MyThread-run()运行结束");
}
}
public class Thread_Demo12 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
//延时两秒后更改标志位;
Thread.sleep(2000);
thread.interrupt();
}
}
++打印结果:++
......
这是MyThread
false
这是MyThread
false
这是MyThread
false
MyThread-run()运行结束
休眠两秒后,thread通过调用interrupt()方法修改了标志位,线程终止执行。
++当更改标志位,但线程处于阻塞状态时:++
|------------------------------------------------------------------------------------------------------------------|
| 在更改标志位时,如果线程因为调用 wait/join/sleep 等方法而阻塞,则此时会抛出异常InterruptedException,并重置终止标志位。此时程序的后续执行通过catch子句中的异常处理方案来决定。 |
| 如果在更改标志位时,线程非为阻塞状态,则标志位不会重置,可以通过interrupted()或isInterrupted()进行判断。 |
java
class MyThread extends Thread{
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
System.out.println("这是MyThread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("MyThread-run()运行结束");
}
}
public class Thread_Demo14 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
//等待两秒后唤醒;
Thread.sleep(2000);
thread.interrupt();
}
}
++打印结果:++
这是MyThread
这是MyThread
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at Learn_Thread.Demo14.MyThread.run(Thread_Demo14.java:16)这是MyThread
这是MyThread
这是MyThread
可以看到,在抛出异常后,仍然继续打印,这意味着原先由interrupt()方法修改的标志位,在sleep唤醒时,又被重置为false了。
++在异常处理中,加入更多的处理方法:++
java
class MyThread extends Thread{
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
System.out.println("这是MyThread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
//在异常处理中加入break,跳出循环;
break;
}
}
System.out.println("MyThread-run()运行结束");
}
}
public class Thread_Demo14 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
//等待两秒后唤醒;
Thread.sleep(2000);
thread.interrupt();
}
}
++打印结果:++
这是MyThread
这是MyThread
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at Learn_Thread.Demo14.MyThread.run(Thread_Demo14.java:16)MyThread-run()运行结束
线程通过interrupt()方法修改了标志位,但由于线程此时大概率处于sleep(即,阻塞状态),因此,抛出异常,并将标志位重置。
这里线程会终止,是由异常处理中的break跳出循环得到的结果。
1.6.3 Java中终止线程不是强制性的
|-----------------|---------------------------------------------------------------------------------------------------------------------------|
| 操作系统中的API: | 提供了强制终止线程的操作,无论线程执行到何种程度,都强行结束线程。 |
| Java中的的API: | 终止线程需要对应线程互相配合,而不是直接"剪断"。 |
| 优劣: | 强制结束线程的方式更"随心所欲,为所欲为",但如果线程执行过程中被强行终止,可能导致出现一些临时性质的"错误"数据 。而相互配合的线程终止,虽然使终止线程时需要考虑的事情变多了,但也使得线程的终止更"安全",系统运行更稳定了。 |
1.7 sleep()
|----------------------------------------------------------------------------------|---------------------|
| 方法 | 说明 |
| public static void sleep(long millis) throws InterruptedException | 以毫秒级别的精度,指定休眠时间 |
| public static void sleep(long millis, int nanos) throws InterruptedException | 以纳秒级别的精度,指定休眠时间 |
|------------------------------------------------------------------|
| 1)sleep()方法只能保证实际休眠时间大于等于参数设置的休眠时间。 |
| 2)sleep被提前唤醒(如被上文的interrup唤醒)时,会抛出异常,并将Thread对象的标志位重置为false。 |
++sleep被提前唤醒时,为什么要重置标志位?++
|-----------------------------------------------------------------------------------|
| sleep重置标志位,可以给程序员更多的"可操作空间"。 通过抛出异常,处理异常,程序的后续执行可以且不仅可以让线程立即结束,增加了代码的灵活性。 |
1.8 join()
|--------------------------------------------------------------|
| join() 等待线程结束 |
| join(long millis) 等待线程结束,指定最长等待时间 |
| join(long millis, int nanos) 等待线程结束,以纳秒级别的精度指定最长等待时间 |
| 1)由于随即调度,抢占式执行,多线程的执行顺序是不确定的。但是通过应用程序中的API,可以影响到线程的执行顺序。 |
java
class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println("这是MyThread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("MyThread-run()运行结束");
}
}
public class Thread_Demo13 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
//让主线程阻塞,保证MyThread先运行完;
thread.join();
System.out.println("在MyThread结束后打印");
}
}
++打印结果:++
这是MyThread
这是MyThread
这是MyThread
这是MyThread
这是MyThread
MyThread-run()运行结束
在MyThread结束后打印
可以看到,main线程中的"在MyThread结束后打印"确实是在thread线程结束后才打印的。
阅读指针 -> 《线程安全(重点!!)》
链接生成中..........