在学习了第一节之后,我们来看一下Thread常见api的学习。
static methods
以下方法基本上看源码代码,以及翻译一下源码的英文注释 。
1 sleep()方法,
使当前正在运行的线程睡眠多少毫秒,这取决于你的系统的时间定时器和调度器,这个线程
不会失去任何监视器的所有权 (线程会阻塞,但是不会释放资源和锁)
/**
* Causes the currently executing thread to sleep (temporarily cease
* execution) for the specified number of milliseconds, subject to
* the precision and accuracy of system timers and schedulers. The thread
* does not lose ownership of any monitors.
* @param millis
这个参数是以毫秒为级别的
* the length of time to sleep in milliseconds
* @throws IllegalArgumentException
如果是负数就会抛异常
* if the value of {@code millis} is negative
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public static native void sleep(long millis) throws InterruptedException;
/**
* Causes the currently executing thread to sleep (temporarily cease
* execution) for the specified number of milliseconds plus the specified
* number of nanoseconds, subject to the precision and accuracy of system
* timers and schedulers. The thread does not lose ownership of any
* monitors.
* @param millis
* the length of time to sleep in milliseconds
* @param nanos
如果不是[0,999999] 里面就会抛出异常
* {@code 0-999999} additional nanoseconds to sleep
* @throws IllegalArgumentException
* if the value of {@code millis} is negative, or the value of
* {@code nanos} is not in the range {@code 0-999999}
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
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");
}
其实也就是多加入了一个纳秒级别的,但是并不是加了就生效还是和nanos>=500000
一样
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}
2 yield()方法
/**
* A hint to the scheduler that the current thread is willing to yield
* its current use of a processor. The scheduler is free to ignore this
* hint.
*/
通知这个调度器,让这个正在运行的当前线程去让出当前的处理器
也就是说使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。
cpu会从众多的可执行态里选择,就是刚刚的那个线程还是有可能会被再次执行到的,
并不是说一定会执行其他线程而该线程在下一次中不会执行到了。
public static native void yield();
3 interrupted()方法 英文注释太长了,楼主直接给翻译过来注释上去了
/**
测试当前线程是否已被中断,此方法会清楚线程的中断状态。换句话说,如果连续两次调用
此方法,则第二次调用将返回false(除非当前线程在第一次调用已清除其中断状态之后且在
第二次调用检查之前再次中断)
*/
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
作用是测试当前线程是否被中断(检查中断标志),返回一个boolean并清除中断状态,
第二次再调用时中断状态已经被清除,将返回一个false。
Instance Methods
这里源码里面有参考另外两个实例方法 public void interrupt() 以及 public boolean isInterrupted(),一起讲述一个新知识:
如何优雅的停止一个线程?首先来看一下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();}
具体的英文注释楼主大概解释一下,Java线程里面都是协作形式的,协作式的意思就是一个线程不会立马要另外一个线程停止,例如 A线程中 调用了 B.interrupt()方法,白话来说就是A线程现在给你B线程打了一个招呼你要停止了,至于你B线程停止与否还是看你B线程自己。那么interrupt()方法具体有哪些用处呢?,翻译源码可知
中断调用该方法的线程,除非当前线程正在中断自己,否则checkAccess方法有可能抛出SecurityException异常。
(1)如果当前线程由于调用Object的wait(),或者Thread的join和sleep方法阻塞,则退出阻塞且中断状态将被清除,并且抛出InterrruptedException异常,(同时中断状态为false),这样,我们就可以捕捉到中断异常,并根据实际情况对该线程从阻塞方法中异常退出而进行一些处理。
(2)如果线程由于InterruptibleChannel的IO操作阻塞,则通道将关闭,线程设置中断状态,并且线程收到一个ClosedInterruptException异常。
(3)如果线程由于Selector阻塞,线程将处于中断状态,并且从selection操作立刻返回,可能是一个非零值。除此之外,线程将被设置中断状态。
总结来说 :interrupt方法有两个作用:(1)将线程的中断状态设置为true(默认为false),调用这个方法并不是一个会把线程停止,只是把中断状态设置为true,至于停止与否还需要该线程自己来处理这个中断状态 (2)同上面描述的第一个红字作用。
再看一下isInterrupted() 方法 public boolean isInterrupted() 调用
检测调用该方法的线程是否被中断线程一旦被中断,该方法返回true中断状态不会被清除。下面用代码测试一下如何停止线程
static class StopThread extends Thread{
@Override
public void run() {
//当该线程调用interrupt()方法之后会把中断状态改为true
while (!interrupted()){
//默认中断状态为false,如果线程一直alive 就会一直运行while循环体内容
System.out.println("Thread name = "+getName());
}
System.out.println("Thread is stop");
}
}
public static void main(String[] args) {
StopThread thread = new StopThread();
thread.start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
}
如果改成下面代码,就会走入异常,并且把中断状态重新设置为false
另外 stop () 停止线程这个方法已经不建议使用,因为太过暴力和绝对,直接死掉不会释放相关资源。
suspend()使线程挂起不会释放类似锁这样的资源 resume()使线程恢复如果之前没有使用suspend暂停线程则不起作用,那么suspend不释放资源,如果一条线程将去resume目标线程之前尝试持有这个重要的系统资源再去resume目标线程,那么就和suspend挂起的线程死锁了,所以这三个方法不建议使用了 !!!
再来一个判断是否线程存活的方法
最后一个常见的Join方法
/**
* Waits for this thread to die.
*
* <p> An invocation of this method behaves in exactly the same
* way as the invocation
*
* <blockquote>
* {@linkplain #join(long) join}{@code (0)}
* </blockquote>
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final void join() throws InterruptedException {
join(0);
}
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
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;
}
}
}
如果一个线程A调用了 B线程的join()方法,则让出CPU的执行权,让B线程执行完毕之后 A才接着执行。
t.join(); //使调用线程 t 在此之前执行完毕。
t.join(1000); //等待 t 线程,等待时间是1000毫秒。Join方法实现是通过wait(小提示:Object 提供的方法)。 当main线程调用t.join时候,main线程会获得线程对象t的锁(wait 意味着拿到该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒main线程 ,比如退出后。这就意味着main 线程调用t.join时,必须能够拿到线程t对象的锁,下面看个例子
public class JoinTest {
static class JumpQueue implements Runnable{
private Thread thread;
JumpQueue(Thread thread){
this.thread = thread;
}
@Override
public void run() {
try {
//此时构造初始完毕 thread 传入进来是主线程main 则main执行完毕之后当先子线程0才能拿到返回对象 0 继续执行,否则一直等待main执行
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" terminate subclass");
}
}
public static void main(String[] args) throws InterruptedException {
Thread previous = Thread.currentThread();
//首先第一步获取得到的previous是主线程
for(int i= 0;i<10;i++){
Thread thread = new Thread(new JumpQueue(previous),String.valueOf(i));
//第一次得到的是子线程 thread [0 ,5,main] 线程主是main线程
System.out.println(previous.getName()+" jump a queue the thread :"+thread.getName());
//所以第一次打印 main jump a queue the thread : 0
thread.start();
previous = thread;
}
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+" terminate class");
}
}
基本上Thread类常用API已经分析了,Thread类里面有个枚举类 public enum State { NEW, RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED }
NEW(新建/初始状态): 新建的一个Thread,还没有调用start()方法
RUNNABLE (可运行状态): 调用了start()方法,变成可运行的线程状态,但是否运行还是取决于操作系统和资源的调度
BLOCKED (阻塞状态): 比如线程里面调用 t.join() Thread.sleep() 等
WAITING/TIME_WAITING(等待状态) : 具有指定等待时间的等待线程的hread状态
TERMINATED(结束状态):线程运行完了/异常结束都算结束状态。
总结 :
wait()、notify()/notifyAll()、sleep()、yield()、join() 的理解
1)sleep()是Thread的静态方法,不用在同步环境使用,作用当前线程,不释放锁。
2)yield()是Thread的静态方法,作用当前线程,释放当前线程持有的CPU资源,将CPU让给优先级不低于自己的线程用,调用后进入就绪状态,不释放锁。
3)join()是Thread的实例方法,作用是阻塞当前线程,让新加入的线程运行,由于join()里面源码还是调用的wait()方法
下面重点说一下 wait,notify,nitifyAll
1、wait()、notify/notifyAll() 方法是Object的本地final方法,无法被重写。
2、wait()使当前线程阻塞,前提是 必须先获得锁,一般配合synchronized 关键字使用,即,一般在synchronized 同步代码块里使用 wait()、notify/notifyAll() 方法。
3、 由于 wait()、notify/notifyAll() 在synchronized 代码块执行,说明当前线程一定是获取了锁的。当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。只有当 notify/notifyAll() 被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。也就是说,notify/notifyAll() 的执行只是唤醒沉睡的线程,而不会立即释放锁,锁的释放要看代码块的具体执行情况。所以在编程中,尽量在使用了notify/notifyAll() 后立即退出临界区,以唤醒其他线程让其获得锁。
4、notify 和wait 的顺序不能错,如果A线程先执行notify方法,B线程在执行wait方法,那么B线程是无法被唤醒的。
5、notify 和 notifyAll的区别 notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。notifyAll 会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。如果当前情况下有多个线程需要被唤醒,推荐使用notifyAll 方法。比如在生产者-消费者里面的使用,每次都需要唤醒所有的消费者或是生产者,以判断程序是否可以继续往下执行。
6、在多线程中要测试某个条件的变化,使用if 还是while?
要注意,notify唤醒沉睡的线程后,线程会接着上次的执行继续往下执行。所以在进行条件判断时候,可以先把 wait 语句忽略不计来进行考虑;显然,要确保程序一定要执行,并且要保证程序直到满足一定的条件再执行,要使用while进行等待,直到满足条件才继续往下执行。