1、线程睡眠——sleep
如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread的sleep方法
注意如下几点问题
①、sleep是静态方法,最好不要用Thread的实例对象调用它,因为它睡眠的始终是当前正在运行的线程,而不是调用它的线程对象,它只对正在运行状态的线程对象有效。
②、使用sleep方法之后,线程是进入阻塞状态的,只有当睡眠的时间结束,才会重新进入到就绪状态,而就绪状态进入到运行状态,是由系统控制的,我们不可能精准的去干涉它,所以如果调用Thread.sleep(1000)使得线程睡眠1秒,可能结果会大于1秒。
2、线程让步——yield
yield()方法和sleep()方法有点相似,它也是Thread类提供的一个静态的方法,它也可以让当前正在执行的线程暂停,让出cpu资源给其他的线程。
但是和sleep()方法不同的是,它不会进入到阻塞状态,而是进入到就绪状态。yield()方法只是让当前线程暂停一下,重新进入就绪的线程池中,让系统的线程调度器重新调度器重新调度一次。
完全可能出现这样的情况:当某个线程调用yield()方法之后,线程调度器又将其调度出来重新进入到运行状态执行。
实际上,当某个线程调用了yield()方法暂停之后,优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的线程更有可能获得执行的机会,当然,只是有可能,因为我们不可能精确的干涉cpu调度线程。
关于sleep()方法和yield()方的区别如下:
①、sleep方法暂停当前线程后,会进入阻塞状态,只有当睡眠时间到了,才会转入就绪状态。而yield方法调用后 ,是直接进入就绪状态,所以有可能刚进入就绪状态,又被调度到运行状态。
②、sleep方法声明抛出了InterruptedException,所以调用sleep方法的时候要捕获该异常,或者显示声明抛出该异常。而yield方法则没有声明抛出任务异常。
③、sleep方法比yield方法有更好的可移植性,通常不要依靠yield方法来控制并发线程的执行。
3、线程合并——join
线程的合并的含义就是将几个并行线程的线程合并为一个单线程执行,应用场景是当一个线程必须等待另一个线程执行完毕才能执行时,Thread类提供了join方法来完成这个功能,注意,它不是静态方法。
从上面的方法的列表可以看到,它有3个重载的方法:
void join()
当前线程等该加入该线程后面,等待该线程终止。
void join(long millis)
当前线程等待该线程终止的时间最长为 millis 毫秒。 如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度
void join(long millis,int nanos)
等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度
public class Test1 {
public static void main(String[] args) throws InterruptedException {
MyThread thread=new MyThread();
thread.start();
//将主线程加入到子线程后面,不过如果子线程在1毫秒时间内没执行完,
// 则主线程便不再等待它执行完,进入就绪状态,等待cpu调度
thread.join(1);
for(int i=0;i<30;i++){
System.out.println(Thread.currentThread().getName() + "线程第" + i + "次执行!");
}
}
}
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(this.getName() + "线程第" + i + "次执行!");
}
}
4、如何结束一个线程
class MyThread extends Thread {
int i=0;
@Override
public void run() {
while (true) {
if(i==10)
break;
i++;
System.out.println(i);
}
}
}
或者
class MyThread extends Thread {
int i=0;
boolean next=true;
@Override
public void run() {
while (next) {
if(i==10)
next=false;
i++;
System.out.println(i);
}
}
}
或者
class MyThread extends Thread {
int i=0;
@Override
public void run() {
while (true) {
if(i==10)
return;
i++;
System.out.println(i);
}
}
}
只要保证在一定的情况下,run方法能够执行完毕即可。
5、使用interrupt结束一个线程。
诚然,使用第4步标识符来结束一个线程,是一个不错的方法,但是如果,该线程是处于sleep、wait、join的状态的时候,while循环就不会执行,那么我们的标识符就无用武之地了,当然也不能再通过它来结束处于这3种状态的线程了。
可以使用interrupt这个巧妙的方式结束掉这个线程。
我们看看sleep、wait、join方法的声明:
public final void wait() throws InterruptedException
public static native void sleep(long millis) throws InterruptedException
public final void join() throws InterruptedException
可以看到,这三者有一个共同点,都抛出了一个InterruptedException的异常。
在什么时候会产生这样一个异常呢?
每个Thread都有一个中断状状态,默认为false。
可以通过Thread对象的isInterrupted()方法来判断该线程的中断状态。
可以通过Thread对象的interrupt()方法将中断状态设置为true。
当一个线程处于sleep、wait、join这三种状态之一的时候,如果此时他的中断状态为true,那么它就会抛出一个InterruptedException的异常,并将中断状态重新设置为false。
public class Test1 {
public static void main(String[] args) throws InterruptedException {
MyThread thread=new MyThread();
thread.start();
Thread.sleep(3000);
thread.interrupt();
}
}
class MyThread extends Thread {
int i=0;
@Override
public void run() {
while (true) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("中断异常被捕获了");
return;
}
i++;
}
}
}
多测试几次,会发现一般有两种执行结果:
0
1
2
中断异常被捕获了
或者
0
1
2
3
中断异常被捕获了
这两种结果恰恰说明了 只要一个线程的中断状态一旦为true,只要它进入sleep等状态,或者处于sleep状态,立马回抛出InterruptedException异常。
第一种情况,是当主线程从3秒睡眠状态醒来之后,调用了子线程的interrupt方法,此时子线程正处于sleep状态,立马抛出InterruptedException异常。
第一种情况,是当主线程从3秒睡眠状态醒来之后,调用了子线程的interrupt方法,此时子线程还没有处于sleep状态。然后再第3次while循环的时候,在此进入sleep状态,立马抛出InterruptedException异常。