1.创建线程
2.中断线程
让线程结束(就是让内核中的pcb销毁)其关键就是让线程对应的入口方法,执行完毕
其中,入口方法指的就是:
继承thread重写的run,实现runnable重写的run,lambda.只要run执行完毕,线程就随之结束,更多的情况,线程不一定这么快就执行完run
例如:
实际,我们并不希望run就是一个死循环,更希望能过控制这个线程,按照我们的需要随时结束;
为了实现这个效果:
- 用boolean变量来作为循环结束标志
- 刚才是使用自己定义的变量来作为循环标记,还可以使用标准库中内置的标记
获取线程内置的标志位:线程的isInterrupted()判断当前线程是不是应该要结束循环
修改线程内置的标记位:Thread.interrupt()来修改这个标记位
当3s之后;
interrupt方法貌似没有修改这个标记位,循环看起来还是在执行,
同时这里有个异常
这里的interrupt方法可能会有两种行为:
当前线程正在运行中,此处会修改Thread.currentThread().isInterrupted()标记为true;
如果当前线程正在sleep/wait/等待锁,,,此时会触发InterrutedException
isInterrupted()这个是Thread的实例方法
和这个方法类似的:
interrupted()这个是Thread的类方法(static)
public void interrupt()
中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,
否则设置标志位
public static boolean interrupted()
判断当前线程的中断标志位是否设置,调用后清除标志位
//开关按下去 会自动弹回来
public boolean isInterrupted()
判断对象关联的线程的标志位是否设置,调用后不清除标志位
//开关按下去,不会自动弹回来
实际上,这两者有区别,使用静态的,会自动清除标记位,使用非静态的不会对标记位进行清除
thread 收到通知的方式有两种:
- 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通
知,清除中断标志
当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择
忽略这个异常, 也可以跳出循环结束线程. - 否则,只是内部的一个中断标志被设置,thread 可以通过
Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志
Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志
这种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到。
缘由
很多人认为多线程中的Thread.currentThread()和this都是显示当前线程的意思,其实不然,他们两个代表不同的意思,下面用一个小例子说明一下。
package currentThreadAndThis;
public class MyThread extends Thread {
public MyThread(){
System.out.println("当前线程的名字:"+Thread.currentThread().getName());
System.out.println("当前线程的名字:"+this.getName());
}
@Override
public void run(){
System.out.println("当前线程的名字:"+Thread.currentThread().getName()+" run=="+Thread.currentThread().isAlive());
System.out.println("当前线程的名字:"+this.getName()+" run=="+this.isAlive());
}
}
//启动类
package currentThreadAndThis;
public class Run {
public static void main(String[] args) {
MyThread myThread=new MyThread();
//初始化Thread对象,方便调用start();
//此时myThread作为参数传入Thread中,其实是myThread委托thread去执行;
Thread thread=new Thread(myThread);
//初始化自定义线程名称
thread.setName("C");
//启动线程
thread.start();
}
}
运行结果
1.当前线程的名字:main
2.当前线程的名字:Thread-0
3.当前线程的名字:C run == true
4.当前线程的名字:Thread-0 run ==false
然后我们会看到执行构造函数 MyThread myThread=new MyThread();,然后打印的结果是“当前线程的名字:main ;当前线程的名字:Thread-0”.
为什么会是这样呢?来分析一下源码:
分析
(一)首先看一下首先看Thread.currentThread方法源码:
它返回的是当前执行的线程;然后我们首先启动的是main线程,所以我们打印出------- “当前线程的名字:main”
(二)来看一下,this的调用,首先分析一下Thread的构造函数:
线程在初始化对象的时候, this代表的当前对象MyThread,然后Thread在初始化对象的时候,会给线程起一个默认的初始名,所以this.getname()打印出来是“当前线程的名字:Thread-0”。
(1) 在执行“ thread.start();”时,因为myThread作为参数传入Thread中,其实是myThread委托thread去执行;所以在执行–“ thread.setName(“C”);”,通过“Thread.currentThread().getName()”打印出来时“当前线程的名字:C”。
(2) 但是此时执行this.getname()仍然代表的是Mythread这个对象,所以打印出来的仍然是“当前线程的名字:Thread-0”
结论
①Thread.currentThread表示当前代码段正在被哪个线程调用的相关信息。
②this表示的是当前对象,与Thread.currentThread有很大的区别。
3 线程等待
线程与线程之间,调度顺序是完全不确定的(取决于操作系统调度器自身的实现)
而但是有时候 希望顺序可控制
此时线程等待是一个方法
这里的线程等待,主要就是控制线程结束的先后顺序
一种常见的逻辑:t1线程,创见t2,t3,t4这三个新的线程来执行一些任务,然后t1线程最后在这里汇总结果
这种场景就需要t1的结束时机必须比t2,t3,t4都迟
Thread t=new Thread();
t.join();//执行到这个代码,调用这个代码的线程就会阻塞等待,代码不继续往下走了,具体来说就是操作系统短时间内不会把这个线程调度到cpu上面了
之前有阻塞等待的代码:
Scanner scanner =new Scannner(System.in);
scanner.nextInt();//尝试从键盘中读入一个数据-》用户输入的 此时触发阻塞等待
public class Test5 {
public static void main(String[] args) {
//创建两个线程t1,t2
Thread t1=new Thread(){
@Override
public void run() {
int count=0;
while(count<5){
count++;
System.out.println("程序运行中");
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println("程序运行结束");
}
};
//执行start方法的时候,会立刻创建出一个新的线程来,
//同时main这个线程也会立刻往下进行,直到执行到t1.join();
//执行到t1.join()的时候发现,此时t1线程正在运行
//只要t1在运行中,join方法就会一直阻塞等待,一直等到t1的run执行结束
t1.start();
try{
//当t1执行完了,再调用join会出现什么,此时不会阻塞等待,直接运行了
Thread.sleep(7000);
//此时的join就会阻塞等待
System.out.println(Thread.currentThread().getName()+"join执行开始");
t1.join();
System.out.println("join执行结束");
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
public void join() 等待线程结束,死等,往往比较危险,典型是网络编程中,发了一个请求,希望得到一个回应,但是由于种种原因,回应没有到达,会一直等待
public void join(long millis) 等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos) 同理,但可以更高精度,纳秒等待
CurrentThread可以获取当前Thread线程实例的引用
public class Test6 {
public static void main(String[] args) {
Thread t=new Thread(){
@Override
public void run(){
System.out.println(Thread.currentThread().getId());//22
System.out.println(this.getId());//22
}
};
//在这个代码中,得到的就是t这个引用,相当与在run中直接使用this;
t.start();
}
}
public static void sleep(long millis) throws InterruptedException
休眠当前线程 millis
public static void sleep(long millis, int nanos) throws InterruptedException
可以更高精度的休眠
sleep这个方法,本质上是把线程PCB从就绪队列,移动到了阻塞队列
操作系统管理线程:
- 描述:PCB;
- 组织:双向链表(其实不仅仅是以一个)
4.线程的状态
用于辅助 系统对于线程进行调度的属性
线程的状态是用一个枚举类型Thread.State
public class Test7 {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(){
@Override
public void run(){
while(!Thread.currentThread().isInterrupted()){
System.out.println("线程正在运行中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
System.out.println("线程已经结束");
}
};
//NEW:Thread对象创建出来了,但是内核中的PCB还没有创建出来
//RUNNABLE:当前的PCB也创建出来了,同时这个PCB随时待命(就绪),这个线程可能是正在cPU上运行,可能就是在就绪队列中排队
//TIMED_WAITING:表示当前的PCB在阻塞队列中等待,这个等待是带有“结束时间”的等待
//TERMINATED:表示当前的PCB已经结束了,Thread对象还在,此时调用获取状态,得到的就是这状态
System.out.println(t.getId()+":"+t.getState());//NEW
t.start();
System.out.println(t.getId()+":"+t.getState());//RUNNABLE
try {
System.out.println("当线程在cpu上运行,第一次等待三秒");
Thread.sleep(3000);
}catch(InterruptedException e){
e.printStackTrace();
}
//改变状态,该为false run结束
t.interrupt();
try {
System.out.println("此时线程结束,第二次等待三秒,不会再等待3秒,直接结束");
Thread.sleep(3000);
}catch(InterruptedException e){
e.printStackTrace();
}
//TERMINATED:表示当前的PCB已经结束了,Thread对象还在,此时调用获取状态,得到的就是这状态
System.out.println(t.getId()+":"+t.getState());
}
}
//22:NEW
//线程正在运行中
//22:RUNNABLE
//当线程在cpu上运行,第一次等待三秒
//线程正在运行中
//线程正在运行中
//此时线程结束,第二次等待三秒,不会再等待3秒,直接结束
//线程已经结束
//java.lang.InterruptedException: sleep interrupted
// at java.base/java.lang.Thread.sleep(Native Method)
// at Test7$1.run(Test7.java:16)
//22:TERMINATED
NEW:Thread对象创建出来了,但是内核中的PCB还没有创建出来
RUNNABLE:当前的PCB也创建出来了,同时这个PCB随时待命(就绪),这个线程可能是正在cPU上运行,可能就是在就绪队列中排队
TIMED_WAITING:表示当前的PCB在阻塞队列中等待,这个等待是带有“结束时间”的等待
WAITING:线程中如果调用了wait方法,也会阻塞等待,此时处在WAITING状态(死等)除非其他线程唤醒了此线程
BLOCKED:线程中尝试进行加锁,结果发现锁已经被其他线程占用,此时该线程也会阻塞等待,这个等待会在其他线程释放锁之后,被唤醒
此三种都表示阻塞等待,结束阻塞等待的条件不一样
TERMINATED:表示当前的PCB已经结束了,Thread对象还在,此时调用获取状态,得到的就是这状态
当前这几个状态,就是Java中的Thread类中的状态,他与操作系统中的pcb中的状态不一样
yield() 让线程主动让出cpu,不会改变线程的状态