Java中,建立线程的话,通常有两种方式java
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
先来看看Java API中对于该方法的介绍:使该线程开始执行;Java 虚拟机调用该线程的 run
方法。结果是两个线程并发地运行;当前线程(从调用返回给 start
方法)和另外一个线程(执行其 run
方法)。屡次启动一个线程是非法的。特别是当线程已经结束执行后,不能再从新启动,用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体中的代码执行完毕而直接继续执行后续的代码。经过调用Thread类的 start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并无运行,一旦获得cpu时间片,就开始执行run()方法,这里的run()方法 称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。安全
一样先看看Java API中对该方法的介绍:若是该线程是使用独立的 Runnable
运行对象构造的,则调用该 Runnable
对象的 run
方法;不然,该方法不执行任何操做并返回。 Thread
的子类应该重写该方法。run()方法只是类的一个普通方法而已,若是直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径仍是只有一条,仍是要顺序执行,仍是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。多线程
根据以上信息能够得出结论并发
thread1和thread2的线程ID不一样,thread2和主线程ID相同,说明调用start方法方可启动新的线程,而run方法只是thread类中的一个普通方法调用,仍是在主线程里执行,并不会建立新的线程app
虽然thread1的start方法调用在thread2的run方法前面调用,可是先输出的是thread2的run方法调用的相关信息,说明新线程建立的过程不会阻塞主线程的后续执行less
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()); } }
结果ide
主线程ID:1 子线程ID:12
经过实现Runnable接口,咱们定义了一个子任务,而后将子任务交给Thread去执行,这种方式须要将Runnable做为Thread类的参数,而后经过Thread的start方法来建立一个新的线程来执行子任务,若是调用Runnable的run方法就不会建立线程,和普通方法没有区别,实际上Thread类就是实现了Runnable接口。函数
public class Thread implements Runnable {
在Java中,两种方式均可以用来建立线程去执行子任务,可是Java只容许单继承,因此若是自定义类须要继承其余类,就只能实现Runnable接口。oop
首先咱们来介绍下线程的状态
线程状态从大的方面来讲,可归结为:初始/新建状态(new)、可运行/就绪状态(runnable)、运行状态(running)、阻塞状态(blocked、time waiting、waiting)、消亡状态(dead)五个状态:
当用new操做符建立一个线程时, 例如new Thread(r),线程尚未开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序尚未开始运行线程中的代码
一个新建立的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法建立线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
处于就绪状态的线程并不必定当即运行run()方法,线程还必须同其余线程竞争CPU时间,只有得到CPU时间才能够运行线程。由于在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。所以此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。
当线程得到*CPU*时间后,它才进入运行状态,真正开始执行run()方法.
线程运行过程当中,可能因为各类缘由进入阻塞状态:
有两个缘由会致使线程死亡:
1) run方法正常退出而天然死亡,
2) 一个未捕获的异常终止了run方法而使线程猝死。
为了肯定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),须要使用isAlive方法。若是是可运行或被阻塞,这个方法返回true; 若是线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.
线程上下文是指某一时间点 CPU 寄存器和程序计数器的内容,CPU经过时间片分配算法来循环执行任务(线程),由于时间片很是短,因此CPU经过不停地切换线程执行。
CPU在一个时刻只能运行一个线程,当在运行一个线程的过程当中转去运行另一个线程,这个叫作线程上下文切换,这样就须要记录不一样线程的运行状态,以便下次从新切换回来时可以继续切换到以前的状态运行,上下文的切换实际上就是存储和恢复CPU状态的过程,使CPU能够从端点继续执行。
多线程可使任务执行效率得以提高,可是线程切换同样有代价,开发中要权衡事后再设计方案,例如Redis就为了快速而使用单线程
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()方法是不须要用户来调用的,当经过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方法有两个重载版本:
/** * 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方法会让当前线程交出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();
假如在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方法和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方法会直接终止run方法的调用,而且会抛出一个ThreadDeath错误,若是线程持有某个对象锁的话,会彻底释放锁,致使对象状态不一致。因此stop方法基本是不会被用到的。
已废弃
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)方法来将一个普通的线程(用户线程)设置为守护线程。