如何创立线程
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:1name:thread2 子线程ID:1name: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.
上下文切换
线程上下文是指某一时间点 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) */@Overridepublic 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:12i: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)办法来将一个一般的线程(用户线程)设置为守护线程。