线程等待:wait方法
- 调用wait方法的线程会进入WAITING状态,只有等到其他线程的
通知
或程序被中断
才会返回。- 调用wait方法后会
释放对象的锁
,因此 wait方法一般被用于同步方法或同步代码块中 。
线程睡眠:sleep方法
- 调用sleep方法会导致当前线程休眠。 sleep方法暂停指定的时间,
让出CPU
给其他线程,但其监控状态依然保持
,在指定的时间过后又会自动恢复运行状态。- sleep方法
不会释放当前占有的锁
,会导致线程进入TIMED-WATING状态。
sleep() 和wait() 有什么区别?
参考 :sleep()和wait()区别.
- 所属的类
sleep属于Thread类静态方法,wait则属于Object类;
- sleep自己会唤醒,wait需要被唤醒
- sleep 必须指定时间,wait不必须指定时间
- 线程状态:
sleep会导致线程进入TIMED-WATING状态,而wait方法会导致当前线程进入WATING状态。
- 适用范围:
wait,notify,notifyAll只
能在同步控制方法或者同步控制块里面使用
,而sleep可以任何地方使用
- 都需要异常处理
- sleep
哪个线程调用哪个线程等待
,wait会使当前拥有该对象锁的线程等待- sleep
释放CPU执行权不释放同步锁
,wait释放CPU执行权也释放同步锁
- sleep通常被
用于暂停执行
,wait通常被用于线程间交互
线程让步:yield方法
调用yield方法会使当前线程让出(释放)CPU执行时间片,与其他线程一起
重新竞争CPU时间片
在一般情况下,优先级高的线程更有可能竞争到CPU时间片,但这不是绝对的,有的操作系统对线程的优先级并不敏感。
线程中断:interrupt方法
interrupt方法用于向线程发行一个终止
通知信号
,会改变该线程内部的一个中断标识位,线程本身并不会因为调用了interrupt方法而改变状态(阻塞、终止等),状态的具体变化需要等待接收到中断标识的程序的最终处理结果来判定。
对interrupt方法的理解需要注意以下4个核心点。
- 调用interrupt方法并
不会中断
一个正在运行的线程,也就是说处于Running状态的线程并不会因为被中断而终止,仅仅改变了内部维护的中断标识位而已;- 若因为调用sleep方法而使线程处于TIMED-WATING状态,则这时调用interrupt方法会抛出
InterruptedException
,使线程提前结束TIMED-WATING状态。- 许多声明抛出InterruptedException的方法如Thread.sleep(long mills),在抛出异常前都会
清除中断标识位
,所以在抛出异常后调用isInterrupted方法将会返回false。- 中断状态是线程固有的一个标识位,可以通过此标识位
安全终止线程
。比如,处理线程结束前必要的一些资源释放和清理工作;比如:释放锁,存储数据到持久层,发出异常通知
线程加入:join方法
如果在当前线程中调用另一个线程的join方法,则当前线程转为阻塞状态,等到另一个线程结束,当前线程再由阻塞状态转为就绪状态,等待获取CPU的使用权。
通俗理解(让多个线程同步执行—>变成单线程)
如下有两个线程:
public class Join_OnlyOne extends Thread {
public void run(){
System.out.println("thread_one start");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread_one end");
}
}
public class Join_OnlyTwo extends Thread {
public void run() {
System.out.println("thread_two start");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread_two end");
}
}
Join_OnlyOne one = new Join_OnlyOne();
Join_OnlyTwo two = new Join_OnlyTwo();
one.start();
two.start();
如果这样写的话线程one和线程two是交替执行的,但是不一定谁先启动
如何设置顺序?
可以设置 two线程要在one的run里面创建-----------这样保证两个有先后顺序
public class Join_OnlyTwo_In_One extends Thread {
public void run() {
System.out.println("thread_one start");
Join_OnlyTwo two = new Join_OnlyTwo();
two.start();
System.out.println("thread_one End");
}
}
public static void main(String[] args) {
Join_OnlyTwo_In_One one = new Join_OnlyTwo_In_One();
one.start();
}
thread_one start
thread_one End
thread_two start
thread_two end
以上只是保证,线程1比线程2先开始执行,后面就开始争抢资源不一定谁先结束
如何保证线程2执行完了,再执行线程1呢?
两个线程合并成一个线程
public class Join_OnlyTwo_Join_One extends Thread {
public void run() {
System.out.println("thread_one start");
Join_OnlyTwo two = new Join_OnlyTwo();
two.start();
try {
two.join();//线程2加入到线程1里, 线程2执行完才会执行线程1
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread_one end");
}
}
thread_one start
thread_two start
thread_two end
thread_one end
这样就太给线程2特权了,线程2不执行完,线程1就一直等着!!!
还可以往回收收权利,给线程2特定时间,
表示:我只等你约定的时间,超过这时间,我要和你一起争夺资源了
//两个线程合并成一个线程
public class Join_OnlyTwo_Join_One extends Thread {
public void run() {
System.out.println("thread_one start");
Join_OnlyTwo two = new Join_OnlyTwo();
two.start();
try {
two.join(2000);//就等线程2 2000毫秒 超过就不等了
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread_one end");
}
}
thread_one start
thread_two start
thread_one end
thread_two end
参看下面例子体会Join
public class Join_Lock_ThreadOne extends Thread {
public void run(){
System.out.println(Thread.currentThread().getName() +" thread_one start: "+ new Date());
Join_Lock_ThreadTwo two = new Join_Lock_ThreadTwo();
two.start();
try {
two.join(2000);//线程2加入到线程1里, 线程2先执行。但是线程1只会等线程2 2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() +" thread_one end: "+ new Date());
}
}
public class Join_Lock_ThreadTwo extends Thread {
public void run() {
System.out.println(Thread.currentThread().getName() + " thread_two start: "+ new Date());
Join_Lock_ThreadThree three = new Join_Lock_ThreadThree(this);
three.start();
try {
Thread.sleep(5000);//线程2休眠 5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " thread_two end: "+ new Date());
}
}
public class Join_Lock_ThreadThree extends Thread{
private Join_Lock_ThreadTwo two;
public Join_Lock_ThreadThree(Join_Lock_ThreadTwo two) {
this.two = two;
}
public void run(){
//在two执行过程中 one等待的过程中 three将two对象锁定
System.out.println(Thread.currentThread().getName() + " thread_three start: "+ new Date());
synchronized (two){
System.out.println(Thread.currentThread().getName() + " two is locked:"+ new Date());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " two is free: "+ new Date());
}
System.out.println(Thread.currentThread().getName() + " thread_three end: "+ new Date());
}
}
public class Join_Lock_TestMain {
public static void main(String[] args) {
Join_Lock_ThreadOne one = new Join_Lock_ThreadOne();
one.start();
}
}
Thread-0 thread_one start: Mon Sep 07 14:15:27 GMT+08:00 2020 线程one启动并执行,线程two启动并join到线程one里
Thread-1 thread_two start: Mon Sep 07 14:15:27 GMT+08:00 2020 线程two执行,线程three启动,线程two休眠
Thread-2 thread_three start: Mon Sep 07 14:15:27 GMT+08:00 2020 线程three启动并执行。
Thread-2 two is locked:Mon Sep 07 14:15:27 GMT+08:00 2020 在2s(线程one join需要等2s)和5s(线程two sleep 5s)之内都是线程three优先执行,执行代码锁定two对象10s
过了2s之后,(线程two sleep 5s),(线程three sleep 10s),线程one执行,线程one想把线程two从自己的线程内删除,但是发现two对象不在自己手里,而是被线程three锁定了,而且锁定了10s,线程one只能等待
(5s以后线程two才会醒,还有3秒,这3秒就不清楚线程one一直在等待,还是循环获取到执行权然后被拒绝的这种循环???????????????????????)
Thread-1 thread_two end: Mon Sep 07 14:15:32 GMT+08:00 2020 5s以后线程two醒了,(线程three sleep 10s),线程two和线程one争夺资源,线程one获得执行权也是被拒绝,线程two获取到执行权才会去执行;
Thread-2 two is free: Mon Sep 07 14:15:37 GMT+08:00 2020 (尽管过了2s之后线程one想把线程two从自己的线程内删除,但是发现two对象不在自己手里,而是被线程three锁定了,而且锁定了10s,线程one只能等待),线程one获得执行权也是被拒绝,一直挺到,(线程three sleep 10s)结束后线程three将two对象释放
Thread-0 thread_one end: Mon Sep 07 14:15:37 GMT+08:00 2020 此时线程one和线程three争夺资源,谁执行完全随机。如果线程one获取到执行权的话,线程one把线程two从自己的线程内删除, 对于最后的输出语句,依旧线程one和线程three争夺资源,最后这两步不一定谁先执行
Thread-2 thread_three end: Mon Sep 07 14:15:37 GMT+08:00 2020
以上例子可以得到:
synchronized锁非常的厉害, 一旦对象被锁定不释放的情况下,其他的对象都需要等待
线程唤醒:notify方法
Object类有个notify方法,用于
唤醒
在此对象监视器上等待的一个线程,如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,选择是任意的。 notifyAll,用于唤醒在监视器上等待的所有线程
。
后台守护线程:setDaemon方法
setDaemon方法用于定义一个守护线程,也叫作“服务线程”,该线程是后台线程; 将一个用户线程设置为守护线程的方法是在线程对象创建之前用线程对象的setDaemon(true)来设置。
终止线程的4种方式
1.正常运行结束
指线程体执行完成,线程自动结束。
2.使用退出标志退出线程
在一般情况下,在run方法执行完毕时,线程会正常结束。然而,有些线程是后台线程,需要长时间运行,只有在系统满足某些特殊条件后,才能触发关闭这些线程
public class volatile_ extends Thread{
//volatile,这个关键字用于使exit线程同步安全,也就是说在同一时刻只能有一个线程修改exit的值,在exit为true时,while循环退出。
public volatile boolean exit = false;
public void run (){
while (!exit){
//执行业务代码逻辑
}
}
}
3. 使用Interrupt方法终止线程
(1)线程处于阻塞状态。
例如,在使用了sleep、调用锁的wait或者调用socket的receiver、accept等方法时,会使线程处于
阻塞状态
。在调用线程的interrupt方法时,会抛出InterruptException异常。通常很多人认为只要调用interrupt
方法就会结束线程,这实际上理解有误,一定要先捕获InterruptedException异常
再通过break
跳出循环,才能正常结束run方法。(2)线程未处于阻塞状态。
此时,使用
isInterrupted
方法判断线程的中断标志来退出循环。在调用interrupt方法时,中断标志会被设置为true,并不能立刻退出线程,而是执行线程终止前的资源释放操作,等待资源释放完毕后退出该线程。
4.使用stop方法终止线程:不安全
在程序中可以直接调用
Thread.stop
方法强行终止线程,但这是很危险的,就像突然关闭计算机的电源,而不是正常关机一样,可能会产生不可预料的后果
。在程序使用Thread.stop方法终止线程时,该线程的子线程会抛出ThreadDeatherror错误,并且释放子线程持有的所有锁。加锁的代码块一般被用于保护数据的一致性,如果在调用Thread.stop方法后导致该线程所持有的所有锁突然释放而使锁资源不可控制,被保护的数据就可能出现不一致的情况,其他线程在使用这些被破坏的数据时,有可能使程序运行错误。因此,并不推荐采用这种方法终止线程。
现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?
这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉。这个多线程问题比较简单,可以用join方法实现。(上面有实现的代码)