目录
一、图片总结:
1.1 补充知识
下图是线程的的六种状态
- 初始态
- 运行态(就绪是指等待CPU分配执行)
- 阻塞态
- 等待态
- 超时等待态
- 终止态
下图取之Java线程状态
1.2 函数整体总结
二、sleep、yield、join
2.1 sleep:
- Api:
sleep(long millis) //参数为毫秒
sleep(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒
- 作用: sleep就是让线程睡眠,交出CPU
注意点:但是值得注意的是它不会释放锁(即如果该线程持有该对象的锁,那么sleep后其他线程也无法访问该对象。)
2.2 yield
- Api:
yield()
- 作用:
yield
让当前线程交出CPU,但是不能控制时间
注意点: 不会释放锁,但是值得注意的是yield只能让等于或大于自己优先级的线程有机会获得CPU执行机会
2.3 join
- 结论:先说结论
- join 是通过wait来实现的。
- 但是join不会释放锁,因为他wait的线程是你执行
thread.join
所在的线程(一般是main
线程)
- Api:
join()
join(long millis) //参数为毫秒
join(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒
- 作用:让当前线程等待子线程结束之后才能运行
注意点
:join实现的顺序如下
1.假设在main线程里运行了子线程A
2.接着设置 A.join()
3.接着main线程会处于waiting状态
4.直到子线程完成后通知main线程结束等待
-------------特别注意的一个概念----------------->
1.主线程调用的方法就是主线程获得锁,子线程run方法里的调用才是子线程获得锁。
2.所以join通过wait来等待的是主线程,子线程是不会释放锁的
- 例子
lic class JoinTest {
public static int a = 0;
public static void main(String ...args) throws InterruptedException {
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++)
a++;
}
});
thread.start();
//在main线程里执行了join,所以main线程获得了join的锁
//后面join源码分析里执行wait是main线程(不要搞乱了)
thread.join();
println(a);
}
//如果不加 join 正常情况下应该是小于10的
//但是加了 join 后,它会等待thread线程执行结束后再执行主线程
//所以答案等于10
- 源码解析
public final void join(long millis) throws InterruptedException {
synchronized(lock) {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
//判断子线程是否执行完毕,如果执行完毕的话进行等待
//这边等待对象是你执行 thread.join 所在的线程 即main
//这边只要子线程没执行完毕就无限循环
while (isAlive()) {
lock.wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
lock.wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
}
三、interrupt
- Api
thread.interrupt()
通知线程中断(只是一个信号,真正如何处理还是需要自己判断)Thread.interrupted()
得到线程是否中断的状态并且重置状态(false)Thread.currentThread().isInterrupted()
得到线程是否中断的状态
- 注意
调用interrupt()方法,立刻改变的是中断状态,但如果不是在阻塞态,就不会抛出异常;如果在进入阻塞态后,中断状态为已中断,就会立刻抛出异常(阻塞状态 run方法无法执行得通过其他方法来判断)
- 例子
这边举一个正在阻塞状态中的例子,如果正常运行的话Run方法会正常执行只用通过判断isInterrupted
就可以来
业务重点
其中sleep被阻塞100秒这时候如果提前终止使用isInterrupted
是不行的。我们通过阻塞时执行interrupt()
会抛异常的特性来完成该操作,捕捉到异常后,会提前结束阻塞状态,所以更改stop的属性来挑出循环,等待run方法执行完毕。
static boolean stop = true;
public static void main(String... args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (stop) {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
stop = false;
}
}
}
});
thread.start();
thread.interrupt();
}
四、优先级
- Api
setPriority
-
作用:
设置优先级,高优先级被优先执行的几率更大 -
优先级等级(1-10):
- 最低优先级 1:Thread.MIN_PRIORITY
- 最高优先级 10:Thread.MAX_PRIORITY
- 普通优先级 5:Thread.NORM_PRIORITY
如果无设置优先级,默认时父线程的优先级,比如我们平时在主线程创建线程优先级都是5.
五、守护线程
- 补充知识:
守护线程是为其他线程服务的,若只剩下守护线程,那么守护线程就会自动中断。 - Api
setDaemon(true)
- 作用:设置为守护线程
- 注意点
- thread.setDaemon(true)必须在thread.start()之前设置
- 你不能把正在运行的常规线程设置为守护线程。
- 在Daemon线程中产生的新线程也是Daemon的
4.守护线程中不能做重要的操作,因为它随时可能中断
- 例子
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Thread daemonThread = new Thread(() -> {
while (true){
System.out.print("我是守护线程" + "\n");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//设置为守护线程
daemonThread.setDaemon(true);
daemonThread.start();
System.out.print("开始运行了" + "\n");
}
});
thread.start();
//正常来说daemonThread会一直循环
//当时当他设置为守护线程后 thread执行结束后 daemonThread也随之结束。
六、resume()、suspend()、stop()
resume():重启线程
suspend():挂起线程
stop():停止线程
这三个方法都可能会出现一些不可预料的问题, 所以被废弃了。