> 文章列表 > JavaEE--Thread 类的基本用法(不看你会后悔的嘿嘿)

JavaEE--Thread 类的基本用法(不看你会后悔的嘿嘿)

JavaEE--Thread 类的基本用法(不看你会后悔的嘿嘿)

Thread类是JVM用来管理线程的一个类,换句话说,每个线程都唯一对应着一个Thread对象.

因此,认识和掌握Thread类弥足重要.

本文将从

  1. 线程创建
  2. 线程中断
  3. 线程等待
  4. 线程休眠
  5. 获取线程实例

等方面来进行具体说明.

1)线程创建

方法1:通过创建Thread类的子类并重写run () 方法

class MyThread extends Thread{@Overridepublic void run() {System.out.println("thread");}
}
public class Thread1 {public static void main(String[] args) {Thread thread = new MyThread();thread.start();System.out.println("main");}
}

方法2:通过创建Runnable接口的实现类并重写run ()方法,传实现类的对象作为构造器

class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println("thread");}
}
public class Thread2 {public static void main(String[] args) {Thread thread = new Thread(new MyThread());thread.start();System.out.println("main");}
}

方法3:使用匿名类,new类,也就是创建该类的子类

public class Thread3 {public static void main(String[] args) {Thread thread = new Thread(){@Overridepublic void run() {System.out.println("thread");}};thread.start();System.out.println("main");}
}

方法4:使用匿名内部类,new接口,也就是创建该接口的实现类

public class Thread4 {public static void main(String[] args) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("thread");}});thread.start();System.out.println("main");}
}

方法5:使用lambda表达式(日常开发中使用最多的形式)

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

2)线程中断

顾名思义,也就是让线程停止.

本质上而言,让线程停止,方法就一种 -->执行完线程入口方法(run ()方法).

只不过此处可能是正常执行结束,也可能是因为异常而导致结束.

目前常见的有以下两种方式:
1.使用自定义的变量来作为标志位
2.使用Thread提供的变量来作为标志位

下面来介绍:

方法1.使用自定义的变量来作为标志位

public class ThreadDemo9 {public static boolean isQuit = false;public static void main(String[] args) {// boolean isQuit = false;Thread t = new Thread(() -> {while (!isQuit) {System.out.println("hello t");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("t 线程终止");});t.start();// 在主线程中, 修改 isQuittry {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}isQuit = true;}
}

执行结果:

此处代码的逻辑大概就是,主线程sleep ()3秒后,t线程里的循环差不多执行3次(sleep()了3秒),此时isQUit被置为ture,t线程里循环结束.t线程继续向下执行逻辑,最终t线程正常结束.

假设:此处设置的isQuti不是类变量,而是局部变量,可行吗?

结果是显然不行的.那到底是什么原因呢?其实是因为-->lambda表达式存在变量捕获.

变量捕获,只能捕获到由1)final修饰2)实际上final的局部变量.

但是如果该局部变量的值被修改了,那么lamoda表达式就捕获不到该值了.

所以此处该如何做呢? --> 设置成类变量.也就是由static修饰的变量.

因为类变量不受变量捕获的限制.

方法2:使用Thread提供的变量来作为标志位

使用Thread.currentThreadOisInterrupted0 代替自定义标志位.

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

public class ThreadDemo10 {public static void main(String[] args) {Thread t = new Thread(() -> {// currentThread 是获取到当前线程实例.// 此处 currentThread 得到的对象就是 t// isInterrupted 就是 t 对象里自带的一个标志位.while (!Thread.currentThread().isInterrupted()) {System.out.println("hello t");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}// 把 t 内部的标志位给设置成 truet.interrupt();}
}

 

原因如下:

首先需要明白interrupt 方法的作用:

1)设置标志位为 true

2)如果该线程正在阻塞中(比如在执行 sleep)此时就会把阻塞状态唤醒.通过抛出异常的方式让 sleep 立即结束.(线程在执行sleep(),join (),wait() 方法时,都会进入阻塞状态)

同时需要注意:
当 sleep 被唤醒的时候, sleep 会自动的把 Thread线程内置的标志位给清空(true -> false).这就导致下次循环,循环仍然可以继续执行了!!!

因此:如果需要结束循环,就得在 catch 中搞个 break.

此处再具体说明一下sleep() 的工作原理:(分3种情况)
1)如果 sleep 执行的时候看到这个标志位是 false --> sleep 正常进行休眠操作.

2)如果当前标志位为 true,sleep 无论是刚刚执行还是已经执行了一半,都会触发两件事 :1.立即抛异常2.清空标志位为 false.
3)如果设置 interrupt 的时候,恰好, sleep 刚醒~~ 这个时候赶巧了,执行到下一轮循环的条件,就直接结束了但是这种概率非常低,毕竟 sleep 的时间已经占据了整个循环体的 99.99999999% 的时间了.几乎可以视为不可能事件.

那可能就又会有人问了,为啥 sleep 要清空标志位呢???
目的就是为了让线程自身能够对于线程何时结束,有一个更明确的控制~
当前interrupt 方法,不是让线程立即结束,而是告诉他,你该结束了,至于他是否真的要结束,立即结束还是等会结束,都是代码来灵活控制的。
interrupt 只是通知,而不是"命令".
为什么这么说呢?原因就是,t线程是否结束,是取决于自身的,也就是说,根据catch里的逻辑实现,可以结束,也可以不结束.

 那肯定又会有人问了:那为啥java 这里 不强制设定成命令结束"的操作? 只要调用 interrupt 就立即结束?? 

主要是设定成这种,非常不友好的~

线程 t 何时结束,一定是 t 自己最清楚~~ 交给 t 自身来决定比较好~~

映射到生活:如果正在跟领导打电话,你的女票让你去帮她拿东西.那此时,肯定是工作优先嘛!!些竟拿东西不是太重要的事,可以先搁置一会!!

3)线程等待

线程之间是并发执行的,操作系统对于线程的调度是无序的.无法判定两个线程谁先执行结束, 谁后执行结束!!!

在某些业务场景下,需要明确规定线程的结束顺序.可以使用线程等待来实现 -->join 方法

public class ThreadDemo {public static void main(String[] args) throws InterruptedException {Runnable target = () -> {for (int i = 0; i < 10; 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("王五工作结束了");}
}

输出结果:

当前是在main线程里执行的thread1.join()(也就是说是让main线程等待thread1线程).

那么在main线程里执行的thread1.join0 的时候,此时有两种情况:

1)如果 thread线程仍在继续运行,那么main线程就会暂时不参与程调度,等待thread1 线程结束main线程才继续执行代码逻辑.
2)如果thread1线程已经结束,那么main线程也就不用等待了,就直接继续执行自己的代码逻辑了.

但不管是哪种情况,都能保证thread1线程是先于main线程结束的.

4)线程休眠

方法:public static void sleep(long millis) throws InterruptedException

说明:

1)单位是ms

2)sleep是Thread类里的静态方法,通过类名可以直接调用.

3)作用是休眠当前进程.

4)需要用try-catch处理InterruptedException中断异常(意思就是 sleep 睡眠过程中,还没到点就提前唤醒了).

5)获取线程实例

方法:public static Thread currentThread()

说明:在哪个线程里调用,返回的就是哪个线程的对象引用.

public class ThreadDemo {public static void main(String[] args) {Thread thread = Thread.currentThread();System.out.println(thread.getName());}
}

输出结果:main

多提一句:多线程编程在Java中很常见,也很重要.务必掌握!!!

今天不想敲代码,所以才去敲.

uu们加油呀!!