代码改变世界

第一章:多线程四、线程的一些基本操作

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)即使完成了迅速切换,系统通过线程调度机制从所有就绪线程中挑选下一个执行线程时,就绪的线程有可能被选中,也有可能不被选中,其调度的过程受到其他因素(如优先级)的影响。