第一章:多线程四、线程的一些基本操作
2022-10-16 16:27 阿方技术圈 阅读(29) 评论(0) 收藏 举报一、线程的sleep操作
sleep的作用是让目前正在执行的线程休眠,让CPU去执行其他的任务。从线程状态来说,就是从执行状态变成限时阻塞状态。Sleep()方法定义在Thread类中,是一组静态方法,有两个重载版本:
/**
* 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
* {@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");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}
sleep()方法会有InterruptException受检异常抛出,如果调用了sleep()方法,就必须进行异常审查,捕获InterruptedException异常,或者再次通过方法声明存在InterruptedException异常。
sleep方法的实例代码
package day02; public class SleepDemo { public static final int sleepTime = 6000; public static final int sleepNum = 10; static class SleepThread extends Thread{ static int threadNum = 1; public SleepThread(){ super("sleepThread-" + threadNum); threadNum++; } public void run(){ for (int i = 0;i<sleepNum;i++){ System.out.println(Thread.currentThread().getName() + "睡眠次数" + i); try { Thread.sleep(sleepTime); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "发生异常" + i); throw new RuntimeException(e); } System.out.println(Thread.currentThread().getName() + "运行结束" + i); } } } public static void main(String[] arg){ for (int i = 0;i<6;i++){ Thread thread = new SleepThread(); thread.start(); } System.out.println(Thread.currentThread().getName() + "运行结束"); } }
当线程睡眠时间满后,线程不一定会立即得到执行,因为此时CPU可能正在执行其他的任务,线程首先进入就绪状态,等待分配CPU时间片以便有机会执行
二、线程的interrupt
我们如何在一个线程中暂停另一个线程的运行?
这里不建议是使用stop方法,使用stop方法虽然可以强行终止正在运行或挂起的线程,但使用stop方法是很危险的。stop 方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁, 其它线程将永远无法获取锁
我们可以使用interrupt方法。在使用该方式时,它并不会像stop方法那样会中断一个正在运行的线程,而是线程会继续运行下去;但会设置该线程的中断状态,即设置为true,线程会不时地检测这个中断标示位,以判断线程是否应该被中断(中断状态是否为true)
(1)如果此线程处于阻塞状态(如调用了Object.wait()方法),就会立马退出阻塞,并抛出InterruptedException异常,线程就可以通过捕获InterruptedException来做一定的处理,然后让线程退出。更确切地说,如果线程被Object.wait()、Thread.join()和Thread.sleep()三种方法之一阻塞,此时调用该线程的interrupt()方法,该线程将抛出一个InterruptedException中断异常(该线程必须事先预备好处理此异常),从而提早终结被阻塞状态。
(2)如果此线程正处于运行之中,线程就不受任何影响,继续运行,仅仅是线程的中断标记被设置为true。所以,程序可以在适当的位置通过调用isInterrupted()方法来查看自己是否被中断,并执行退出操作。
实例代码
package day02; public class SleepDemo1 { public static final int sleepTime = 6000; public static final int sleepNum = 10; static class SleepThread extends Thread{ static int threadNum = 1; public SleepThread(){ super("sleepThread-" + threadNum); threadNum++; } public void run(){ for (int i = 0;i<sleepNum;i++){ System.out.println(Thread.currentThread().getName() + "睡眠次数" + i); try { Thread.sleep(sleepTime); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "发生异常" + i); throw new RuntimeException(e); } System.out.println(Thread.currentThread().getName() + "运行结束" + i); } } } public static void main(String[] arg) { Thread thread1 = new SleepThread(); thread1.start(); Thread thread2 = new SleepThread(); thread2.start(); try { Thread.sleep(2000); thread1.interrupt(); Thread.sleep(2000); thread2.interrupt(); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "运行中断" ); throw new RuntimeException(e); } } }
程序运行后中断异常信息
sleepThread-2睡眠次数0 sleepThread-1睡眠次数0 sleepThread-1发生异常0 Exception in thread "sleepThread-1" java.lang.RuntimeException: java.lang.InterruptedException: sleep interrupted at day02.SleepDemo1$SleepThread.run(SleepDemo1.java:22) Caused by: java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at day02.SleepDemo1$SleepThread.run(SleepDemo1.java:19) sleepThread-2发生异常0 Exception in thread "sleepThread-2" java.lang.RuntimeException: java.lang.InterruptedException: sleep interrupted at day02.SleepDemo1$SleepThread.run(SleepDemo1.java:22) Caused by: java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at day02.SleepDemo1$SleepThread.run(SleepDemo1.java:19)
Thread.interrupt()方法并不像Thread.stop()方法那样中止一个正在运行的线程,其作用是设置线程的中断状态位(为true),至于线程是死亡、等待新的任务还是继续运行至下一步,就取决于这个程序本身。线程可以不时地检测这个中断标示位,以判断线程是否应该被中断(中断标示值是否为true)。Thread.interrupt()方法只是改变中断状态,不会中断一个正在运行的线程,线程是否停止执行,需要用户程序去监视线程的isInterrupted()状态,并进行相应的处理。
三、线程的join操作
现在线程A在执行过程中对另一个线程B的执行有依赖,具体的依赖为:线程A需要将线程B的执行流程合并到自己的执行流程中(至少表面如此),这就是线程合并,被动方线程B可以叫作被合并线程
线程的join操作的三个基本操作
//重载版本1:此方法会把当前线程变为TIMED_WAITING,直到被合并线程执行结束
public final void join() throws InterruptedException:
//重载版本2:此方法会把当前线程变为TIMED_WAITING,直到被合并线程执行结束,或者等待被合并线程执行millis的时间
public final synchronized void join(long millis) throws InterruptedException:
//重载版本3:此方法会把当前线程变为TIMED_WAITING,直到被合并线程执行结束,或者等待被合并线程执行millis+nanos的时间
public final synchroinzed void join(long millis, int nanos) throws InterruptedException:
调用join()方法的要点:(1)join()方法是实例方法,需要使用被合并线程的句柄(或者指针、变量)去调用,如threadb.join()。执行threadb.join()这行代码的当前线程为合并线程(甲方),进入TIMED_WAITING等待状态,让出CPU。(2)如果设置了被合并线程的执行时间millis(或者millis+nanos),并不能保证当前线程一定会在millis时间后变为RUNNABLE。(3)如果主动方合并线程在等待时被中断,就会抛出InterruptedException受检异常。
调用join()方法的语句可以理解为合并点,合并的本质是:线程A需要在合并点等待,一直等到线程B执行完成,或者等待超时。
线程的合并示意图

四、线程的yield操作
线程的yield(让步)操作的作用是让目前正在执行的线程放弃当前的执行,让出CPU的执行权限,使得CPU去执行其他的线程。
线程在yield时,线程放弃和重占CPU的时间是不确定的,可能是刚刚放弃CPU,马上又获得CPU执行权限,重新开始执行。
yield()方法是Thread类提供的一个静态方法,它可以让当前正在执行的线程暂停,但它不会阻塞该线程,只是让线程转入就绪状态。yield只是让当前线程暂停一下,让系统的线程调度器重新调度一次,yield()方法只有一个版本:
总结起来,Thread.yeid()方法有以下特点:
(1)yield仅能使一个线程从运行状态转到就绪状态,而不是阻塞状态。
(2)yield不能保证使得当前正在运行的线程迅速转换到就绪状态。
(3)即使完成了迅速切换,系统通过线程调度机制从所有就绪线程中挑选下一个执行线程时,就绪的线程有可能被选中,也有可能不被选中,其调度的过程受到其他因素(如优先级)的影响。
浙公网安备 33010602011771号