如何创建线程

Java中,创建线程的话,一般有两种方式

  • 继承Thread类
  • 实现Runnable接口

继承Thread类

    public static void main(String[] args) {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyThread thread1 = new MyThread("thread1");
        thread1.start();
        MyThread thread2 = new MyThread("thread2");
        thread2.run();
    }
public class MyThread extends Thread {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("name:" + name + " 子线程ID:" + Thread.currentThread().getId());
    }
}

结果

主线程ID:1
name:thread2 子线程ID:1
name:thread1 子线程ID:12

start():

先来看看Java API中对于该方法的介绍:使该线程开始执行;Java 虚拟机调用该线程的 run 方法。结果是两个线程并发地运行;当前线程(从调用返回给 start 方法)和另一个线程(执行其 run 方法)。多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动,用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体中的代码执行完毕而直接继续执行后续的代码。通过调用Thread类的 start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里的run()方法 称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。

run():

同样先看看Java API中对该方法的介绍:如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。 Thread 的子类应该重写该方法。run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。

结论

根据以上信息可以得出结论

thread1和thread2的线程ID不同,thread2和主线程ID相同,说明调用start方法方可启动新的线程,而run方法只是thread类中的一个普通方法调用,还是在主线程里执行,并不会创建新的线程

虽然thread1的start方法调用在thread2的run方法前面调用,但是先输出的是thread2的run方法调用的相关信息,说明新线程创建的过程不会阻塞主线程的后续执行

实现Runnable接口

public class Main {
    public static void main(String[] args) {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}
public class MyRunnable implements Runnable {

    public MyRunnable() {

    }

    @Override
    public void run() {
        System.out.println("子线程ID:" + Thread.currentThread().getId());
    }
}

结果

主线程ID:1
子线程ID:12

通过实现Runnable接口,我们定义了一个子任务,然后将子任务交给Thread去执行,这种方式需要将Runnable作为Thread类的参数,然后通过Thread的start方法来创建一个新的线程来执行子任务,如果调用Runnable的run方法就不会创建线程,和普通方法没有区别,实际上Thread类就是实现了Runnable接口。

public class Thread implements Runnable {

在Java中,两种方式都可以用来创建线程去执行子任务,但是Java只允许单继承,所以如果自定义类需要继承其他类,就只能实现Runnable接口。

Thread类的使用

首先我们来介绍下线程的状态

线程状态从大的方面来说,可归结为:初始/新建状态(new)、可运行/就绪状态(runnable)、运行状态(running)、阻塞状态(blocked、time waiting、waiting)、消亡状态(dead)五个状态:

新建状态(New):

​ 当用new操作符创建一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码

就绪状态(Runnable):

一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。

运行状态(Running)

当线程获得*CPU*时间后,它才进入运行状态,真正开始执行run()方法.

阻塞状态(Blocked):

线程运行过程中,可能由于各种原因进入阻塞状态:

  • 线程通过调用sleep方法进入睡眠状态;
  • 线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
  • 线程试图得到一个锁,而该锁正被其他线程持有;
  • 线程在等待某个触发条件;
    所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。

死亡状态(Dead):

有两个原因会导致线程死亡:
1) run方法正常退出而自然死亡,
2) 一个未捕获的异常终止了run方法而使线程猝死。
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.

image

上下文切换

线程上下文是指某一时间点 CPU 寄存器和程序计数器的内容,CPU通过时间片分配算法来循环执行任务(线程),因为时间片非常短,所以CPU通过不停地切换线程执行。

CPU在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另外一个线程,这个叫做线程上下文切换,这样就需要记录不同线程的运行状态,以便下次重新切换回来时能够继续切换到之前的状态运行,上下文的切换实际上就是存储和恢复CPU状态的过程,使CPU可以从端点继续执行。

多线程可以使任务执行效率得以提升,但是线程切换一样有代价,开发中要权衡过后再设计方案,例如Redis就为了快速而使用单线程

Thread类的方法

start()

start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。

    
    /* The group of this thread 线程组        ThreadGroup是Java提供的一种对线程进行分组管理的手段,可以对所有线程以组为单位进行操作,为线程服务,用户通过使用线程组的概念批量管理线程,如批量停止或挂起、设置优先级、守护线程等
    */
    private ThreadGroup group;
    
    /**
     * Causes this thread to begin execution; the Java Virtual Machine    线程开始执行
     * calls the <code>run</code> method of this thread.
     * <p>
     * The result is that two threads are running concurrently: the        一个线程使用start方法、另一个使用run方法,的结果是两个线程同时运行
     * current thread (which returns from the call to the
     * <code>start</code> method) and the other thread (which executes its
     * <code>run</code> method).
     * <p>
     * It is never legal to start a thread more than once.    一个线程启动两次是不合法的
     * In particular, a thread may not be restarted once it has completed    一个线程在执行完成后可能无法启动
     * execution.
     *
     * @exception  IllegalThreadStateException  if the thread was already    线程已经存在异常
     * synchronized方法,保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性)
     *               started.
     * @see        #run()
     * @see        #stop()
     */
    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         * 如果线程状态不为NEW,就抛出异常
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started    通知线程组,这个线程将要被启动,以便于添加到线程组列表中,并减少该线程组未开始计数
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            //标记线程已经启动
            started = true;
        } finally {
            try {
                if (!started) {
                    //启动失败,线程组设置线程启动失败
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
    /**
     * native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问
     * 这个关键字表示调用本机的操作系统函数,因为多线程需要底层操作系统的支持
     */
    private native void start0();

run()

run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。

/**
 * If this thread was constructed using a separate    如果这个线程使用单独的构造Runnable运行对象,那么会调用Runnable对象的run方法,否则什么都不会返回
 * <code>Runnable</code> run object, then that
 * <code>Runnable</code> object's <code>run</code> method is called;
 * otherwise, this method does nothing and returns.
 * <p>
 * Subclasses of <code>Thread</code> should override this method.    使用Thread应该重写此方法
 *
 * @see     #start()
 * @see     #stop()
 * @see     #Thread(ThreadGroup, Runnable, String)
 */
@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

sleep()

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    额外的纳秒睡眠
     *         {@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方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象

public class Main {
    private int i = 10;
    private Object object = new Object();

    public static void main(String[] args) {
        Main main = new Main();
        MyThread thread1 = main.new MyThread();
        MyThread thread2 = main.new MyThread();
        thread1.start();
        thread2.start();
    }

    class MyThread extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                i++;
                System.out.println("i:"+i);
                try {
                    System.out.println("线程"+Thread.currentThread().getName()+"进入睡眠状态");
                    Thread.currentThread().sleep(5000);
                } catch (InterruptedException e) {
                    // TODO: handle exception
                }
                System.out.println("线程"+Thread.currentThread().getName()+"睡眠结束");
                i++;
                System.out.println("i:"+i);
            }
        }
    }
}
i:11
线程Thread-0进入睡眠状态
线程Thread-0睡眠结束
i:12
i:13
线程Thread-1进入睡眠状态
线程Thread-1睡眠结束
i:14

  从上面输出结果可以看出,当Thread-0进入睡眠状态之后,Thread-1并没有去执行具体的任务。只有当Thread-0执行完之后,此时Thread-0释放了对象锁,Thread-1才开始执行。

  注意,如果调用了sleep方法,必须捕获InterruptedException异常或者将该异常向上层抛出。当线程睡眠时间满后,不一定会立即得到执行,因为此时可能CPU正在执行其他的任务。所以说调用sleep方法相当于让线程进入阻塞状态

语法方面:
sleep方法的签名里面有InterruptedException 这个checked exception,所以调用方必须捕获或者再次抛出。

线程的中断合作机制方面:
对于java的多线程来说,调用一个可以被阻塞的方法(wait,sleep,join等等),意味着代码到这行会被阻塞,那么中断阻塞的时候应该怎么办?
一般来说,是采用InterruptedException配合Interrupt状态来合作完成的。
如果你调用的方法是个会阻塞的方法(一般会抛出InterruptedException),通用的做法是:
1,如果你的方法签名可以包含InterruptedException,那么直接抛出,让你的方法也变成一个阻塞方法。
2,如果你的方法签名不允许你增加InterruptedException,那么需要你在捕获InterruptedException后及时重置Interrupt状态(一般你调用的阻塞函数在抛出InterruptedException后会清除Interrupt状态)。
以上2种手法的目的都是为了上层调用方能知道本线程被中断了,然后一直将这个Interrupt状态传递到线程的管理者模块那,由他再决定如何处理这个线程。

所以你不一定需要捕获,根据你的方法的签名,决定采用方案1或者2来处理这个异常。

yield()

调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。

    /**
     * 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.
     *
     * <p> Yield is a heuristic attempt to improve relative progression         Yield是一种启发式尝试,旨在提高线程之间的相对进程,否则将过度利用CPU。 应将其使用与详细的性能分析和基准测试结合起来,以确保它实际上具有所需的效果。
     * between threads that would otherwise over-utilise a CPU. Its use
     * should be combined with detailed profiling and benchmarking to
     * ensure that it actually has the desired effect.
     *
     * <p> It is rarely appropriate to use this method. It may be useful    很少适合使用此方法。 它可能对调试或测试有用,因为它可能有助于重现由于竞争条件而产生的错误。 当设计诸如{@link java.util.concurrent.locks}包中的并发
     * for debugging or testing purposes, where it may help to reproduce    控制结构时,它也可能很有用。
     * bugs due to race conditions. It may also be useful when designing
     * concurrency control constructs such as the ones in the
     * {@link java.util.concurrent.locks} package.
     */
    public static native void yield();

join()

假如在main线程中,调用thread.join方法,则main方法会等待thread线程执行完毕或者等待一定的时间。如果调用的是无参join方法,则等待thread执行完毕,如果调用的是指定了时间参数的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    此实现使用以{@code this.isAlive}为条件的{@code this.wait}调用循环。 线程终止时,将调用{@code this.notifyAll}方法。 建议应用程序不要在{@code Thread}
     * conditioned on {@code this.isAlive}. As a thread terminates the    实例上使用{@code wait},{@ code notify}或{@code notifyAll}。
     * {@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()) {
                //线程需要存活状态才能继续等待,否则结束,等到已等待时间大于或等于millis,跳出循环
                long delay = millis - now;
                //如果等待时间结束
                if (delay <= 0) {
                    break;
                }
                //调用wait方法,让线程进入阻塞状态,并释放线程占有的锁,交出CPU执行权限,sleep则不会释放锁
                wait(delay);
                //已等待时间
                now = System.currentTimeMillis() - base;
            }
        }
    }

    /**
     * Waits at most {@code millis} milliseconds plus
     * {@code nanos} nanoseconds for this thread to die.
     *
     * <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
     *
     * @param  nanos
     *         {@code 0-999999} additional nanoseconds to wait
     *
     * @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 final synchronized void join(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++;
        }

        join(millis);
    }
public class JoinMain {
    public static void main(String[] args) throws IOException {
        System.out.println("进入线程" + Thread.currentThread().getName());
        JoinMain joinMain = new JoinMain();
        MyThread thread1 = joinMain.new MyThread();
        thread1.start();
        try {
            System.out.println("线程" + Thread.currentThread().getName() + "等待");
            //等待线程死亡
            thread1.join();
            System.out.println("线程" + Thread.currentThread().getName() + "继续执行");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("进入线程" + Thread.currentThread().getName());
            try {
                Thread.currentThread().sleep(5000);
            } catch (InterruptedException e) {
                // TODO: handle exception
            }
            System.out.println("线程" + Thread.currentThread().getName() + "执行完毕");
        }
    }
}

根据打印内容,我们可以看到,线程thread1执行后,直到执行完毕才开始继续执行main线程,由于有wait方法,所有join同样也会让线程释放对一个对象持有的锁。

进入线程main
线程main等待
进入线程Thread-0
线程Thread-0执行完毕
线程main继续执行

interrupt()

interrupt,顾名思义,即中断的意思。单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程;另外,通过interrupt方法和isInterrupted()方法来停止正在运行的线程

    /**
     * Interrupts this thread.
     *
     * <p> Unless the current thread is interrupting itself, which is    中断此线程。<p>除非当前线程一直在中断自身(总是允许这样做),否则将调用此线程的{@link #checkAccess()checkAccess}方法,这可能会引发{@link 
     * always permitted, the {@link #checkAccess() checkAccess} method    SecurityException}。
     * of this thread is invoked, which may cause a {@link
     * SecurityException} to be thrown.
     *
     * <p> If this thread is blocked in an invocation of the {@link                <p>如果在调用{@link Object#wait()wait()},{@ link Object#wait(long)wait(long)}或{@link Object#wait(long) {@link Object}类或
     * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link    {@link #join()},{@ link #join(long)},{@ link #join(long, int)},{@ link #sleep(long)}或{@link #sleep(long,int)}这类方法,则
     * Object#wait(long, int) wait(long, int)} methods of the {@link Object}    其中断状态将被清除,并且将收到{@link InterruptedException}。
     * class, or of the {@link #join()}, {@link #join(long)}, {@link
     * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
     * methods of this class, then its interrupt status will be cleared and it
     * will receive an {@link InterruptedException}.
     *
     * <p> If this thread is blocked in an I/O operation upon an {@link            <p>如果此线程在{@link java.nio.channels.InterruptibleChannel InterruptibleChannel}上的I / O操作中被阻塞,则该通道将被关闭,该线程的中断状
     * java.nio.channels.InterruptibleChannel InterruptibleChannel}                态将被设置,并且该线程将收到一个{ @link java.nio.channels.ClosedByInterruptException}。
     * then the channel will be closed, the thread's interrupt
     * status will be set, and the thread will receive a {@link
     * java.nio.channels.ClosedByInterruptException}.
     *
     * <p> If this thread is blocked in a {@link java.nio.channels.Selector}    <p>如果此线程在{@link java.nio.channels.Selector}中被阻塞,则将设置该线程的中断状态,并且它将立即从选择操作中返回,可能具有非零值,就像选择器的
     * then the thread's interrupt status will be set and it will return        {@link java.nio.channels.Selector#wakeup唤醒}方法已被调用。
     * immediately from the selection operation, possibly with a non-zero
     * value, just as if the selector's {@link
     * java.nio.channels.Selector#wakeup wakeup} method were invoked.
     *
     * <p> If none of the previous conditions hold then this thread's interrupt        <p>如果没有以上条件,则将设置该线程的中断状态。 </ p>
     * status will be set. </p>
     *
     * <p> Interrupting a thread that is not alive need not have any effect.        <p>中断未运行的线程不会产生任何效果。
     *
     * @throws  SecurityException
     *          if the current thread cannot modify this thread                        如果当前线程无法修改此线程,则@throws SecurityException
     *
     * @revised 6.0
     * @spec JSR-51
     */
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();
        
        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                //native方法
                interrupt0();           // Just to set the interrupt flag
                //设置中断标志位
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

    /**
     * Tests whether the current thread has been interrupted.  The                测试当前线程是否已被中断。 通过此方法可以清除线程的<i>中断状态</ i>。 换句话说,如果要连续两次调用此方法,则第二次调用将返回false(除非当前线程
     * <i>interrupted status</i> of the thread is cleared by this method.  In    在第一次调用清除其中断状态之后且在第二次调用检查其状态之前再次中断)。 p>由于当前中断已被中断<code> false </ @返回<code> true </ code>,该方法
     * other words, if this method were to be called twice in succession, the    将返回false,从而忽略由于线程中断而导致线程中断的忽略。 代码>否则。@ see #isInterrupted()@修订6.0
     * second call would return false (unless the current thread were
     * interrupted again, after the first call had cleared its interrupted
     * status and before the second call had examined it).
     *
     * <p>A thread interruption ignored because a thread was not alive
     * at the time of the interrupt will be reflected by this method
     * returning false.
     *
     * @return  <code>true</code> if the current thread has been interrupted;
     *          <code>false</code> otherwise.
     * @see #isInterrupted()
     * @revised 6.0
     */
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

    /**
     * Tests whether this thread has been interrupted.  The <i>interrupted    测试此线程是否已被中断。 该方法不影响线程的<i>中断状态</ i>。<p>由于该方法返回false将反映线程中断,因为该线程在中断时未处于活动状态。@ return < 
     * status</i> of the thread is unaffected by this method.    code> true </ code>,如果此线程已被中断<code> false </ code>,否则。@ see #interrupted()@revised 6.0
     *
     * <p>A thread interruption ignored because a thread was not alive
     * at the time of the interrupt will be reflected by this method
     * returning false.
     *
     * @return  <code>true</code> if this thread has been interrupted;
     *          <code>false</code> otherwise.
     * @see     #interrupted()
     * @revised 6.0
     */
    public boolean isInterrupted() {
        return isInterrupted(false);
    }
public class InterruptMain {
    public static void main(String[] args) throws IOException {
        InterruptMain test = new InterruptMain();
        MyThread thread = test.new MyThread();
        thread.start();
        try {
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) {

        }
        thread.interrupt();
    }

    class MyThread extends Thread {
        @Override
        public void run() {
            try {
                System.out.println("进入睡眠状态");
                Thread.currentThread().sleep(10000);
                System.out.println("睡眠完毕");
            } catch (InterruptedException e) {
                System.out.println("得到中断异常");
            }
            System.out.println("run方法执行完毕");
        }
    }
}

从这里可以看出,通过interrupt方法可以中断处于阻塞状态的线程。

进入睡眠状态
得到中断异常
run方法执行完毕

接下来看一下通过interrupt方法是否可以中断非阻塞状态的线程。

public class InterruptMain {
    public static void main(String[] args) throws IOException {
        InterruptMain test = new InterruptMain();
        MyThread thread = test.new MyThread();
        thread.start();
        try {
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) {

        }
        thread.interrupt();
    }

    class MyThread extends Thread{
        @Override
        public void run() {
            int i = 0;
            while(i<Integer.MAX_VALUE){
                System.out.println(i+" while循环");
                i++;
            }
        }
    }
}

从结果看出,while循环会一直运行直到变量i的值超出Integer.MAX_VALUE。所以说直接调用interrupt方法不能中断正在运行中的线程。

9766171 while循环
9766172 while循环
9766173 while循环
9766174 while循环
9766175 while循环

虽然直接调用interrupt方法不能中断正在运行中的线程,但是可以使用isInterrupted()判断中断标志是否被置位来中断线程的执行。

public class InterruptMain {
    public static void main(String[] args) throws IOException {
        InterruptMain test = new InterruptMain();
        MyThread thread = test.new MyThread();
        thread.start();
        try {
            //Thread.currentThread().sleep(2000);
            Thread.sleep(2000);
        } catch (InterruptedException e) {

        }
        thread.interrupt();
    }

    class MyThread extends Thread {
        @Override
        public void run() {
            int i = 0;
            while (!isInterrupted() && i < Integer.MAX_VALUE) {
                System.out.println(i + " while循环");
                i++;
            }
        }
    }
}

从结果看出,打印若干个值之后,while循环就停止打印了。

但是一般情况下不建议通过这种方式来中断线程,一般会在MyThread类中增加一个属性 isStop来标志是否结束while循环,然后再在while循环中判断isStop的值

416317 while循环
416318 while循环
416319 while循环
416320 while循环
416321 while循环
416322 while循环

这样就可以在外面通过调用setStop方法来终止while循环

class MyThread extends Thread{
        private volatile boolean isStop = false;
        @Override
        public void run() {
            int i = 0;
            while(!isStop){
                i++;
            }
        }
         
        public void setStop(boolean stop){
            this.isStop = stop;
        }
    }

stop()

stop方法已经是一个废弃的方法,它是一个不安全的方法。因为调用stop方法会直接终止run方法的调用,并且会抛出一个ThreadDeath错误,如果线程持有某个对象锁的话,会完全释放锁,导致对象状态不一致。所以stop方法基本是不会被用到的。

destroy()

已废弃

关于线程属性的方法

1)getId

  用来得到线程ID

    /**
     * Returns the identifier of this Thread.  The thread ID is a positive    返回此线程的标识符。 线程ID是创建该线程时生成的正<tt> long </ tt>号。
     * <tt>long</tt> number generated when this thread was created.            线程ID是唯一的,并且在其生命周期内保持不变。 当线程终止时,可以重新使用该线程ID。
     * The thread ID is unique and remains unchanged during its lifetime.
     * When a thread is terminated, this thread ID may be reused.
     *
     * @return this thread's ID.
     * @since 1.5
     */
    public long getId() {
        return tid;
    }

2)getName和setName

  用来得到或者设置线程名称。

    /**
     * Changes the name of this thread to be equal to the argument    将该线程的名称更改为等于参数<code> name </ code>。<p>
     * <code>name</code>.                                
     * <p>
     * First the <code>checkAccess</code> method of this thread is called    首先,不带任何参数调用此线程的<code> checkAccess </ code>方法。 这可能导致抛出<code> SecurityException </ code>。@ param name这个线程的新名称。 
     * with no arguments. This may result in throwing a                       @exception SecurityException如果当前线程无法修改此异常
     * <code>SecurityException</code>.
     *
     * @param      name   the new name for this thread.
     * @exception  SecurityException  if the current thread cannot modify this
     *               thread.
     * @see        #getName
     * @see        #checkAccess()
     */
    public final synchronized void setName(String name) {
        checkAccess();
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;
        if (threadStatus != 0) {
            setNativeName(name);
        }
    }

    /**
     * Returns this thread's name.    返回线程的名称
     *
     * @return  this thread's name.
     * @see     #setName(String)
     */
    public final String getName() {
        return name;
    }

3)getPriority和setPriority

  用来获取和设置线程优先级。

    /**
     * Changes the priority of this thread.                                更改此线程的优先级。<p>
     * <p>
     * First the <code>checkAccess</code> method of this thread is called    首先,不带任何参数调用此线程的<code> checkAccess </ code>方法。 这可能导致抛出<code> SecurityException </ code>。
     * with no arguments. This may result in throwing a
     * <code>SecurityException</code>.
     * <p>
     * Otherwise, the priority of this thread is set to the smaller of        否则,此线程的优先级将设置为指定的<code> newPriority </ code>和该线程的线程组的最大允许优先级中的较小者。
     * the specified <code>newPriority</code> and the maximum permitted
     * priority of the thread's thread group.
     *
     * @param newPriority priority to set this thread to                    参数 newPriority优先级将此线程设置为@exception IllegalArgumentException如果优先级不在范围<code> MIN_PRIORITY </ code>至
     * @exception  IllegalArgumentException  If the priority is not in the      <code> MAX_PRIORITY </ code>。
     *               range <code>MIN_PRIORITY</code> to
     *               <code>MAX_PRIORITY</code>.
     * @exception  SecurityException  if the current thread cannot modify     如果当前线程无法修改此线程报SecurityException。
     *               this thread.
     * @see        #getPriority
     * @see        #checkAccess()
     * @see        #getThreadGroup()
     * @see        #MAX_PRIORITY
     * @see        #MIN_PRIORITY
     * @see        ThreadGroup#getMaxPriority()
     */
    public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

    /**
     * Returns this thread's priority.    获取属性
     *
     * @return  this thread's priority.
     * @see     #setPriority
     */
    public final int getPriority() {    
        return priority;
    }

4)setDaemon和isDaemon

  用来设置线程是否成为守护线程和判断线程是否是守护线程。

  守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。

    /**
     * Marks this thread as either a {@linkplain #isDaemon daemon} thread    将此线程标记为{@linkplain #isDaemon守护程序}线程或用户线程。 当所有正在运行的线程都是守护程序线程时,Java虚拟机将退出。
     * or a user thread. The Java Virtual Machine exits when the only
     * threads running are all daemon threads.
     *
     * <p> This method must be invoked before the thread is started.    必须在启动线程之前调用此方法。
     *
     * @param  on
     *         if {@code true}, marks this thread as a daemon thread    如果{@code true}将此线程标记为守护线程
     *
     * @throws  IllegalThreadStateException                              如果此线程是{@linkplain #isAlive alive},则@@抛出IllegalThreadStateException
     *          if this thread is {@linkplain #isAlive alive}
     *
     * @throws  SecurityException
     *          if {@link #checkAccess} determines that the current        如果{@link #checkAccess}确定当前线程无法修改该线程,则@throws抛出SecurityException
     *          thread cannot modify this thread
     */
    public final void setDaemon(boolean on) {
        checkAccess();
        if (isAlive()) {
            throw new IllegalThreadStateException();
        }
        daemon = on;
    }

    /**
     * Tests if this thread is a daemon thread.                        测试此线程是否是守护程序线程
     *
     * @return  <code>true</code> if this thread is a daemon thread;
     *          <code>false</code> otherwise.
     * @see     #setDaemon(boolean)
     */
    public final boolean isDaemon() {
        return daemon;
    }

关于用户线程和守护线程

在java多线程开发中,有两类线程,分别是User Thread(用户线程)和Daemon Thread(守护线程) 。

用户线程很好理解,我们日常开发中编写的业务逻辑代码,运行起来都是一个个用户线程。而守护线程相对来说则要特别理解一下。

守护线程,类似于操作系统里面是守护进程。由于Java语言机制是构建在JVM的基础之上,这一机制意味着Java平台是把操作系统的进程给屏蔽了。所以需要在JVM里面构造出对自己有利的机制,于是守护线程应运而生。

所谓的守护线程,指的是程序运行时在后台提供的一种通用服务的线程。比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。

事实上,User Thread(用户线程)和Daemon Thread(守护线程)从本质上来说并没有什么区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。

守护线程并非只有虚拟机内部可以提供,用户也可以手动将一个用户线程设定/转换为守护线程。

在Thread类中提供了一个setDaemon(true)方法来将一个普通的线程(用户线程)设置为守护线程。


zero
49 声望6 粉丝

前进