Java多线程系列之二 ------------ 常见API,优化的中断线程,线程的几种状态

         在学习了第一节之后,我们来看一下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进行等待,直到满足条件才继续往下执行。

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值