第二篇 Java并发编程之Thread API介绍
在Java中,一般创建线程的两种方式是:继承Thread类和实现Runnable接口。本篇将介绍一下Thread类中相关的API的作用以及用法。
1.currentThread方法
public static native Thread currentThread();
currentThread()方法返回的是对当前正在执行线程对象的引用
2. sleep方法
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException
sleep()方法会使当前线程进入指定毫秒数的休眠,暂停执行,虽然给定了一个休眠的时间,但是最终要以系统的定时器和调度器的精度为准,在休眠期间,该线程不会放弃monitor对象锁的所有权,这一点不同于wait()方法。sleep的用法:Thread.sleep(1000L)
在JDK1.5之后,引入了一个枚举类TimeUnit,其对sleep进行了封装,使用该类可以省去时间单位换算的步骤,比如想实现对线程休眠3小时30分30秒66毫秒,使用如下方式即可:
TimeUnit.HOURS.sleep(3);//小时
TimeUnit.MINUTES.sleep(30);// 分钟
TimeUnit.SECONDS.sleep(30);// 秒
TimeUnit.MILLISECONDS.sleep(66);// 毫秒
TimeUnit对sleep分装的方法如下:
public void sleep(long timeout) throws InterruptedException {
if (timeout > 0) {
long ms = toMillis(timeout);
int ns = excessNanos(timeout, ms);
Thread.sleep(ms, ns);
}
}
3. yield方法
public static native void yield();
yield方法暂停当前执行的线程对象,并执行其他线程。这个暂停是会放弃CPU资源的,并且放弃CPU的时间不确定,有可能刚放弃,就获得CPU资源了,也有可能放弃好一会儿,才会被CPU执行,调用yield方法会使当前线程从RUNNING状态切换到RUNNABLE状态
4. 线程interrupt方法
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
调用如下方法会使得当前线程进入阻塞状态,而调用当前线程的interrupt方法,即,上面的interrupt方法就可以打断阻塞,
使线程阻塞的方法:
Object的wait方法以及重载的的方法
Thread的sleep方法以及对应的重载方法
Thread的join方法以及对应的重载方法
=========================================
// 这是一个静态方法,但和isInterrupted()方法有很大区别,该方法会直接擦除线程的interrupt标识
// 需要注意的是,如果当前线程被打断了,那么第一次调用interrupted方法会返回true,并且立即擦除interrupt标识
// 第二次以及之后的调用返回都是false,除非在此期间线程再一次被打断
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
=========================================
//主要判断当前线程是否被中断,该方法仅仅是对interrupt标识的一个判断,并不会影响标识发生任何该表
public boolean isInterrupted() {
return isInterrupted(false);
}
=========================================
// 本地方法 interrupted方法和isInterrupted方法底层都是调用的这个方法
private native boolean isInterrupted(boolean ClearInterrupted);
5. 获取线程ID
public long getId() {
return tid;
}
在一个Java应用中,有一个long型的全局唯一的线程ID生成器threadSeqNumber,每new出来一个线程都会把这个自增一次,并赋予线程的tid属性,这个是Thread自己做的。如下,在线程初始化的方法中进行设置:
/* Set thread ID */
tid = nextThreadID();// Thread 的init方法
// 生成线程ID的方法
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
6. 设置和获取优先级
// 获取线程优先级
public final int getPriority() {
return priority;
}
// 设置线程的优先级
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);
}
}
一般情况下,我们是不会用到线程优先级的,或者说,我们一直在使用默认的线程优先级5,而优先级的范围是1~10。越大代表优先级越高。优先级高的线程得到的CPU资源比较多,设置优先级有助于帮"线程规划器"确定下一次选择哪一个线程优先执行。换句话说,两个在等待CPU的线程,优先级高的线程越容易被CU选择执行,但这并不意味着优先级低的线程就会等待优先级高的线程先执行完再执行。所以,不要在程序设计中企图使用线程优先级绑定某些特定的业务,或者让业务严重依赖线程的优先级,这可能达不到你的预期。
7. 设置线程上下文类加载器
//获取线程上下文类加载器,获取这个线程是由哪个类加载器加载的,如果是没有修改线程上下文类加载器的情况下,则保持与父线程同样的类加载器
public ClassLoader getContextClassLoader() {
if (contextClassLoader == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader.checkClassLoaderPermission(contextClassLoader,
Reflection.getCallerClass());
}
return contextClassLoader;
}
// 设置线程上下文类加载器:制定类加载器加载
public void setContextClassLoader(ClassLoader cl) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("setContextClassLoader"));
}
contextClassLoader = cl;
}
8. 线程的join方法
Thread类中提供了三个join方法,最终使用的是第三个join方法,如下:
public final void join() throws InterruptedException {
join(0);
}
===================================================================
public final synchronized void join(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++;
}
join(millis);
}
===================================================================
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
由上面的源码可以看出,在使用join时,调用的是wait方法去阻塞线程,而我们知道,wait方法会释放对象锁,这是join方法和sleep方法的不同之处。join方法可以理解为,线程排队执行,比如有A、B、C三个线程,分别执行了join方法之后,会以此执行,代码如下:
public class ThreadStu {
public static void main(String[] args) throws Exception {
// join
join();
}
private static void join() throws InterruptedException {
for (int i = 0; i < 2; i++) {
Thread thread = new Thread(() -> {
for (int j = 0; j < 3; j++) {
try {
System.out.println(Thread.currentThread().getName() + "#" + j);
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
thread.join();
}
}
}
执行结果如下:
Thread-0#0
Thread-0#1
Thread-0#2
Thread-1#0
Thread-1#1
Thread-1#2