start方法与run方法
- 直接调用run方法-》主线程实现,并不会启动一个新线程。
- 多次调用start方法-》会抛出非法线程异常的错,当线程变成了runnable状态就不能用start方法了。
sleep方法与yield方法
- 调用sleep会让当前线程从running进入 timed waiting状态。
- 其他线程可以用interrupt方法唤醒sleep线程,sleep方法会抛出打断的异常。
- 睡眠结束后的线程未必会立即得到执行。
- 建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性。(好在有时间单位)
//睡眠1s,内部用的也是Thread.sleep但是用了单位换算可读性更好
TimeUnit.SECONDS.sleep(1);
Thread.sleep(1000);
- 调用yield会让当前线程从running进入runnable就绪状态,然后调度其他线程。
- 具体的实现依赖于操作系统的任务调度器。
sleep实现
- 在没有利用cpu来计算时,不要让while(true)空转浪费cpu。
- 可以用wait或条件变量达到类似结果,但都需加锁并由相应的唤醒操作,一般用于同步。
- sleep使用于无需锁同步的场景。
线程优先级
- 优先级会提示调度器优先调度,但仅仅是一个提示,调度器可以忽略它。
- 如果cpu比较忙,优先级高的线程会有更多的时间片,但cpu闲时,优先级几乎没作用。
join方法
- 用于同步,若添加时间参数则是限时同步。
interrupt方法(两阶段终止模式)
- 打断sleep/wait/join线程,会清空打断状态(打断标记)。
- 使用interrupt方法并不会打断正常运行的线程,可以用打断标记来正常停止。
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(()->{
while(true){
boolean interrupted = Thread.currentThread().isInterrupted();
if(interrupted){
break;
}
}
},"t1");
t1.start();
Thread.sleep(1000);
t1.interrupt();
}
中断错误思路
- 使用线程对象的stop方法停止线程:会真正杀死线程,如果此时线程锁住了共享资源,那么当它杀死后再也没有机会释放锁,其他线程将永远无法获取锁。
- 使用System.exit(init)方法停止线程:整个程序都会停止。
守护线程
默认情况下,Java进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其他非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。
t1.setDaemon(true);
- 垃圾回收器线程是守护线程
- Tomcat中的Acceptor和Poller线程都是守护线程,所以Tomcat接收到shutdown命令后,不会等待它们处理完当前请求。