ThreadAPI详解
线程休眠(sleep)
- sleep方法
public static void sleep(long milis) throws InterruptedException
public static void sleep(long milis,int nanos) throws InterruptedExceptionInterruptedException
- sleep方法会使当前线程进入指定毫秒数的休眠,暂停执行,最终以系统的定时器和调度器的精度为准。
- 每个线程休眠都互不影响,Thread.sleep只会导致当前线程进入指定时间的休眠
- 休眠不会放弃monitor锁的所有权
- 在实现线程休眠的时候推荐使用枚举TimeUnit
- 示例:
//线程休眠3小时24分17秒88毫秒(一秒等于1000毫秒)
Thread.sleep(12257088L)
TimeUnit:
TimeUnit.HOURS.sleep(3);
TimeUnit.MINUTES.sleep(3);
TimeUnit.SECONDS.sleep(3);
TimeUnit.MILLISECONDS.sleep(3);
- 好处:对sleep提供了很好的封装,不必去做时间单位的换算
线程yield
- yield方法属于一种启发式的方法,会提醒调度器线程愿意放弃当前CPU资源,如果CPU资源不紧张(即没有其他线程使用抢占CPU),则会忽略提醒。
- 该方法会使得线程从Running状态切换到Runnable状态,方法不常用,不会主动放弃执行
- 就算线程调用yield方法,也有可能线程依旧执行,当CPU资源充足的时候
sleep与yield对比
- yield实际上是调用了sleep(0)
- sleep会导致当前线程暂停指定的事件,没有CPU时间片的消耗
- yield只是对CPU调度器的一个提示,如果CPU调度器没有忽略这个提示,它会导致线程上下文的切换
- sleep会使线程短暂block,会在给定的事件内释放CPU资源
- yield会使Running状态的Thread进入Runnable状态(如果CPU调度器没有忽略这个提示的话)
- sleep几乎百分之百的完成给定时间的休眠,而yield的提示不一定成功
- 一个线程sleep另一个线程调用interrupt会捕获到中断信号,而yield则不会
线程的优先级设置
- 方法:
public final void setPriority(int newPriority);//为线程设定优先级
public final void getPriority();//获取线程优先级
- 进程有优先级,同样线程也有,优先级的设置同样是一个提示操作(hint),
- 对于root用户,它会hint操作系统所设置的优先级别,否则会被忽略
- 当CPU忙时,设置优先级的优先级的会获取更多的CPU时间片,闲时优先级设置不产生作用
- 注意:不要让某些业务严重的依赖线程的优先级别,一般使用线程的默认优先级就行了,默认优先级为5
获取线程ID
public long getId();//获取线程的唯一ID,线程的ID在整个JVM进程中都是唯一的
获取当前线程
public static Thread currentThread()//用于返回当前执行线程的引用
设置线程上下文类加载器
/*获取线程上下文的类加载器,如果没有修改线程上下文类加载器的情况下,
则保持与父线程同样的类加载器*/
public ClassLoader getContextClassLoader();
//设置线程的类加载器,打破Java类加载器的父委托机智
public void setContextClassLoader(ClassLoader classLoader)
线程interrupt(较为重要)
Interrupt
- 线程interrupt是一个常用的重要API
- interrupt 相关API
public void interrupt();
public static boolean interrupted();
public boolean isInterrupted();
- 使得线程进入阻塞状态的方法:
Object.wait();
Object.wait(long);
Object.wait(long,int);
Thread.sleeo(long);
Thread.sleeo(long,int);
Thread.join();
Thread.join(long);
Thread.join(long,int);
//InterruptibleChannel的IO操作
//Selector的wakeup()方法
//其他导致阻塞的方法
- 如果一个线程调用被阻塞线程的interrupt方法,则会打断阻塞,所以方法可被称为可中断方法
- 打断一个线程并不等于该线程的生命周期结束,只是打断了当前线程的阻塞状态
- 一旦线程被打断,就会抛出一个InterruptedException的异常,是一个信号通知当前线程被打断
- 死亡状态的线程调用interrupt是无效的
isInterrupted
-isInterrupted是Thread的一个成员方法,主要判断当前线程是否被中断,只是对interrupt标识的一个判断,不会影响标识
interrupted
- 是一个静态方法,也是用于判断当前线程是否被中断,调用该方法会擦出掉线程的interrupt标识
- 注意:当前线程被打断,那么调用interrupted会返回true,并且擦除掉interrupt标识,变成false,之后在调用永远返回的都是false
Thread的Join方法
- 接口:
public final void join()throws InterruptedExcption
public final synchronized void join(long millis)throws InterruptedExcption
public final synchronized void join(long millis,int nanos)throws InterruptedExcption
- 作用:使得线程进入阻塞状态,和sleep相同都是可中断方法。
- 示例:
- 有一个线程B,调用线程B的join()方法,则当前线程进入阻塞状态,知道线程B结束,A线程才能就是进入Runnable状态
- 主线程创建子线程,子线程调用join方法,只有子线程运行结束,主线程才能执行。
stop方法关闭一个Thread
- stop方法已经被移除,该方法关闭线程时可能不会释放掉monitor的锁
@Deprecated
public final void stop();
正常关闭线程
- 线程完成任务后正常结束生命。
- 在new Thread时,派生成本较高,因此线程往往会循环的执行某个任务,如心跳检查等。线程退出,需要借助中断线程的方式,通过try-catch捕获InterruptException异常来中断线程。
- 示例:
public class InterruptThreadTest {
public static void main(String[] args) throws InterruptedException{
Thread thread = new Thread(){
@Override
public void run() {
super.run();
System.out.println("start work!");
System.out.println(isInterrupted());
while (isInterrupted() == false)
{
interrupt();
System.out.println(isInterrupted());
try {
TimeUnit.SECONDS.sleep(50);
} catch (Exception e){
e.printStackTrace();
}
System.out.println(interrupted());
System.out.println(isInterrupted());
//working
}
System.out.println("i will be exiting!");
}
};
thread.start();
TimeUnit.SECONDS.sleep(1);
System.out.println("System will be shutDown.");
thread.interrupt();
}
}
- 使用volatile开关控制
- interrupt方式存在标识被擦除或逻辑单元中不会调用任何可中断方法
- 使用volatile修饰的开关flag关闭线程也可完成关闭线程
import java.util.concurrent.TimeUnit;
public class VolatileThreadTest {
static class MyTask extends Thread{
private volatile boolean closed = false;
@Override
public void run() {
super.run();
System.out.println("I will start work");
while (!closed && !isInterrupted()){
}
System.out.println("I will be exiting");
}
public void close(){
this.closed = true;
this.interrupt();
}
}
public static void main(String[] args) throws InterruptedException{
MyTask task = new MyTask();
task.start();
TimeUnit.MINUTES.sleep(1);
System.out.println("System will ben shutdown");
task.close();
}
}
异常退出
- 在一个线程的执行单元中,不允许抛出checked异常的,如果线程在运行中需要捕获checked异常并且判断是否还有运行下去的必要,那么此时可将checked异常封装成unchecked异常(RuntimeException)抛出进而结束线程的生命周期
进程假死
- 线程假死,就是线程虽然存在,但没有日志输出,程序不进行任何的作业,看起来像死了一样,事实上已经死了,原因就是某个线程阻塞了或线程出现了死锁的情况。