sleep方法是一个静态方法。他有两个重载方法。
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}
sleep方法会使当前线程进人指定毫秒数的休眠,暂停执行,虽然给定了一个休眠的时间,但是最终要以系统的定时器和调度器的精度为准,休眠有一个非常重要的特性,那就是其不会放弃monitor锁的所有权。用法:
//当前线程休眠1s
Thread.sleep(1000);
在JDK1.5以后,JDK引入了一个枚举TimeUnit,其对sleep方法提供了很好的封装,使用它可以省去时间单位的换算步骤,比如线程想休眠1小时23分12秒44毫秒,使用TimeUnit来实现就非常的简便优雅了:
TimeUnit.HOURS.sleep(1);
TimeUnit.MINUTES.sleep(23);
TimeUnit.SECONDS.sleep(12);
TimeUnit.MILLISECONDS.sleep(44);
同样的时间表达,TimeUnit显然清晰很多,所以,在使用Thread.sleep的地方,完全使用TimeUnit来代替,因为sleep能做的事,TimeUnit全部都能完成,并且功能更加的强大。
yield方法属于一种启发式的方法,其会提醒调度器我愿意放弃当前的CPU资源,如果CPU的资源不紧张,则会忽略这种提醒。
调用yield方法会使当前线程从RUNNING状态切换到RUNNABLE状态,一般这个方法不太常用。使用方法:
Thread.yield();
注意:yield只是一个提示,CPU并不会每次都满足他。
在JDK1.5以前的版本中yield的方法事实上是调用了sleep(0), 但是它们之间存在着本质的区别,具体如下。
进程有进程的优先级,线程同样也有优先级,理论上是优先级比较高的线程会获取优先被CPU调度的机会,但是事实上往往并不会如你所愿,设置线程的优先级同样也是一个(提示)hint操作,具体如下。
所以,不要在程序设计当中企图使用线程优先级绑定某些特定的业务,或者让业务严重依赖于线程优先级。
设置优先级的方法是setPriority
。MAX_PRIORITY
和MIN_PRIORITY
分别等于10
和 1
。
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
public long getId()
获取线程的唯一ID, 线程的ID在整个JVM进程中都会是唯一的,并且是从0开始逐次递增。如果你在main线程( main函数)中创建了一个唯一的线程, 并且调用getld()后发现其并不等于0,因为在一个JVM进程启动的时候,实际上是开辟了很多个线程,自增序列已经有了一定的消耗,因此我们自己创建的线程绝非第0号线程。
线程在创建的时候,会执行init方法,在最后会执行tid = nextThreadID();
在调用getId()
方法后会返回一个tid
。
public long getId() {
return tid;
}
public static Thread currentThread()
用于返回当前执行线程的引用,这个方法虽然很简单,但是使用非常广泛,来看一段示例代码:
System.out.println(Thread.currentThread());
输出Thread[main,5,main]
。
public ClassLoader getContextClassLoader()
获取线程上下文的类加载器,简单来说就是这个线程是由哪个类加器加载的,如果是在没有修改线程上下文类加载器的情况下,则保持与父线程同样的类加载器。
public void setContextClassLoader(ClassLoader cI)
设置该线程的类加载器,这个方法可以打破JAVA类加载器的父委托机制,有时候该方法也被称为JAVA类加载器的后门。
线程interrupt,是一个非常重要的API,也是经常使用的方法,与线程中断相关的API有如下几个
1. public void interrupt()
2. public static boolean interrupted()
3. public boolean isInterrupted()
如下方法的调用会使得当前线程进人阻塞状态,而调用当前线程的interrupt方法,就可以打断阻塞。
interrupt这个方法到底做了什么样的事情呢?
在一个线程内部存在着名为interrupt flag的标识,如果-一个线程被interrupt,那么它的flag将被设置,但是如果当前线程正在执行可中断方法被阻塞时,调用interrupt方法将其中断,反而会导致flag被清除,关于这点我们在后面还会做详细的介绍。另外有一点需要注意的是,如果一个线程已经是死亡状态,那么尝试对其的interrupt会直接被忽略。
isInterrupted是Thread的一个成员方法,它主要判断当前线程是否被中断,该方法仅仅是对interrupt标识的一一个判断,并不会影响标识发生任何改变。
interrupted是一个静态方法,虽然其也用于判断当前线程是否被中断,但是它和成员方法isInterrupted还是有很大的区别的,调用该方法会直接擦除掉线程的interrupt标识,需要注意的是,如果当前线程被打断了,那么第一次调用interrupted方法会返回true,并且立即擦除了interrupt标识;第二次包括以后的调用永远都会返回false,除非在此期间线程又一次地被打断。
Thread的join方法同样是一个非常重要的方法,使用它的特性可以实现很多比较强大的功能,与sleep一样它也是一个可中断的方法,也就是说,如果有其他线程执行了对当前线程的interrupt操作,它也会捕获到中断信号,并且擦除线程的interrupt标识,Thread的API为我们提供了三个不同的join方法,具体如下。
package com.example.demo.thread;
/**
* @author : pengweiwei
* @date : 2020/2/2 8:19 下午
*/
public class Thread1 extends Thread {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread1 runing");
}
}
package com.example.demo.thread;
/**
* @author : pengweiwei
* @date : 2020/2/2 8:19 下午
*/
public class Thread2 extends Thread {
@Override
public void run() {
System.out.println("thread2 runing");
}
}
package com.example.demo.thread;
/**
* @author : pengweiwei
* @date : 2020/2/2 5:35 下午
*/
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
Thread1 t1 = new Thread1();
Thread2 t2 = new Thread2();
t1.start();
//主线程到这里会阻塞3秒,等待t1执行完成,主线程才会继续执行
t1.join();
t2.start();
}
}
join方法会使当前线程永远地等待下去,直到期间被另外的线程中断,或者join的线程执行结束,当然你也可以使用join的另外两个重载方法,指定毫秒数,在指定的时间到达之后,当前线程也会退出阻塞。