Markdown版本笔记 | 个人GitHub首页 | 个人博客 | 个人微信 | 个人邮箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
线程 Thread Runnable 守护线程【API】html
目录
Runnable
Thread
简介
构造方法
常量
静态方法
公共方法
不经常使用方法
已过期方法
对 join 方法的测试案例
守护线程
守护线程使用注意
守护线程的意义
守护线程的使用案例
ThreadGroup 线程组
Runnable
JDK文档java
全部已知实现类:
Thread
, TimerTask
, AsyncBoxView.ChildState, FutureTask, RenderableImageProducer, SwingWorkergit
Runnable 接口应该由那些打算经过某一线程
执行其实例的类来实现。
设计该接口的目的是为但愿在活动时执行代码的对象提供一个公共协议。例如,Thread 类实现了 Runnable。激活的意思是说某个线程已启动而且还没有中止。程序员
此外,Runnable 为非 Thread 子类的类提供了一种激活方式。经过实例化某个 Thread 实例并将自身做为运行目标,就能够运行实现 Runnable 的类而无需建立 Thread 的子类。大多数状况下,若是只想重写 run() 方法,而不重写其余 Thread 方法,那么应使用 Runnable 接口。这很重要,由于除非程序员打算修改或加强类的基本行为,不然不该为该类建立子类。github
使用实现接口 Runnable 的对象建立一个线程时,启动该线程将致使在独立执行的线程中调用对象的 run 方法。数据库
方法 run 的常规协定是,它可能执行任何所需的动做。api
Thread
JDK文档数组
简介
public class Thread extends Object implements Runnable
Thread 是程序中的执行线程。Java 虚拟机容许应用程序并发地运行多个执行线程。安全
每一个线程都有一个优先级
,高优先级线程的执行优先于低优先级线程。每一个线程均可以或不能够标记为一个守护程序
。当某个线程中运行的代码建立一个新 Thread 对象时,该新线程的初始优先级被设定为建立线程的优先级,而且当且仅当建立线程是守护线程时,新线程才是守护程序。服务器
当 Java 虚拟机启动时,一般都会有单个非守护线程(它一般会调用某个指定类的 main 方法)。Java 虚拟机会继续执行线程,直到下列任一状况出现时为止:
- 调用了 Runtime 类的 exit 方法,而且安全管理器容许退出操做发生。
- 非守护线程的全部线程都已中止运行,不管是经过从对 run 方法的调用中返回,仍是经过抛出一个传播到 run 方法以外的异常。
建立新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来能够分配并启动该子类的实例。
建立线程的另外一种方法是声明实现 Runnable 接口的类。该类而后实现 run 方法。而后能够分配该类的实例,在建立 Thread 时做为一个参数来传递并启动。
每一个线程都有一个标识名,多个线程能够同名
。若是线程建立时没有指定标识名,就会为其生成一个新名称。
构造方法
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
分配新的 Thread 对象,将target做为其运行对象,将name做为其名称,做为group所引用的线程组的一员,并具备指定的堆栈大小
- group - 线程组。线程组能够批量的管理线程或线程组对象,有效的对线程或线程组进行组织。
- target - 其 run 方法被调用的对象。
- 若是 target 参数不是 null,则 target 的 run 方法在启动该线程时调用。若是 target 参数为 null,则该线程的 run 方法在该线程启动时调用。
- 新建立线程的优先级被设定为建立该线程的线程的优先级,即当前正在运行的线程的优先级。方法 setPriority 可用于将优先级更改成一个新值。
- 当且仅当建立新线程的线程当前被标记为守护线程时,新建立的线程才被标记为守护线程。方法 setDaemon 可用于改变线程是否为守护线程。
- name - 新线程的名称。自动生成的名称的形式为
Thread- n
,其中的 n 为整数。 - stackSize - 新线程的
预期堆栈大小
,为零时表示忽略该参数。
堆栈大小是虚拟机要为该线程堆栈分配的地址空间的近似字节数。
stackSize 参数的做用具备高度的平台依赖性,在某些平台上,stackSize 参数的值不管如何不会起任何做用。
做为建议,可让虚拟机自由处理 stackSize 参数。
其余构造方法
- Thread()
- Thread(String name)
- Thread(Runnable target)
- Thread(Runnable target, String name)
- Thread(ThreadGroup group, String name)
- Thread(ThreadGroup group, Runnable target)
- Thread(ThreadGroup group, Runnable target, String name)
常量
public final static int MAX_PRIORITY
= 10; //The maximum priority that a thread can have.
public final static int NORM_PRIORITY
= 5; //The default priority that is assigned to a thread.
public final static int MIN_PRIORITY
= 1; //The minimum priority that a thread can have.
静态方法
- static int activeCount() 返回当前线程的
线程组中活动线程的数目
。 - static Thread currentThread() 返回对当前
正在执行的线程对象
的引用。 - static void dumpStack() 将当前线程的
堆栈跟踪
打印至标准错误流。效果相似于new Throwable().printStackTrace()
。该方法仅用于调试。
// 调用 Thread.dumpStack() 后打印的内容 java.lang.Exception: Stack trace at java.lang.Thread.dumpStack(Thread.java:1329) at Test.main(Test.java:6) new Throwable().printStackTrace(); java.lang.Throwable at Test.main(Test.java:6)
- static int enumerate(Thread[] tarray) 将当前线程的线程组及其子组中的活动线程复制到数组中。该方法只调用当前线程的线程组的 enumerate 方法,且带有数组参数。返回放入该数组的线程数。
- static Map getAllStackTraces() 返回全部活动线程的堆栈跟踪的一个映射。
- 映射的键是
线程
,而每一个映射值都是一个StackTraceElement 数组
,该数组表示相应 Thread 的堆栈转储。 返回的堆栈跟踪的格式都是针对 getStackTrace 方法指定的。 - 在调用该方法的同时,线程可能也在执行。每一个线程的堆栈跟踪仅表明一个
快照
,而且每一个堆栈跟踪均可以在不一样时间得到。若是虚拟机没有线程的堆栈跟踪信息,则映射值中将返回一个零长度数组。
- 映射的键是
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces(); for (Thread thread : map.keySet()) { System.out.println(thread.toString() + " " + Arrays.toString(map.get(thread))); } Thread[Finalizer,8,system] [java.lang.Object.wait(Native Method), java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143), java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164), java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)] Thread[Attach Listener,5,system] [] Thread[Signal Dispatcher,9,system] [] Thread[Reference Handler,10,system] [java.lang.Object.wait(Native Method), java.lang.Object.wait(Object.java:502), java.lang.ref.Reference.tryHandlePending(Reference.java:191), java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)] Thread[main,5,main] [java.lang.Thread.dumpThreads(Native Method), java.lang.Thread.getAllStackTraces(Thread.java:1603), Test.main(Test.java:8)]
- static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() 返回线程因为未捕获到异常而忽然终止时调用的默认处理程序。若是返回值为 null,则没有默认处理程序。
- static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) 设置当线程因为未捕获到异常而忽然终止,而且没有为该线程定义其余处理程序时所调用的默认处理程序
- static boolean holdsLock(Object obj) 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。该方法旨在使程序可以断言当前线程已经保持一个指定的锁:assert Thread.holdsLock(obj);
- static boolean interrupted() 测试当前线程是否已经中断。
- static void sleep(long millis) 在指定的毫秒数内让【当前】正在执行的线程休眠。
- static void sleep(long millis, int nanos) 在指定的毫秒数+纳秒数内让当前正在执行的线程休眠。nanos 的值必须在 0-999999 之间。
- 此操做受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权。
- 若是任何线程中断了当前线程,则抛出 InterruptedException,当抛出该异常时,当前线程的 中断状态 被清除。
- static void yield() 暂停当前正在执行的线程对象,并执行其余线程。
公共方法
- long getId() 返回该线程的标识符。线程 ID 是一个正的 long 数,在建立该线程时生成。线程 ID 是惟一的,并终生不变。线程终止时,该线程 ID 能够被从新使用。
- String getName() 返回该线程的名称。
- void setName(String name) 改变线程名称,使之与参数 name 相同。
- int getPriority() 返回线程的优先级。
- void setPriority(int newPriority) 更改线程的优先级。
- StackTraceElement[] getStackTrace() 返回一个表示该线程堆栈转储的堆栈跟踪元素数组。
- 若是该线程还没有启动或已经终止,则该方法将返回一个零长度数组。
- 若是返回的数组不是零长度的,则其第一个元素表明堆栈顶,它是该序列中最新的方法调用。最后一个元素表明堆栈底,是该序列中最旧的方法调用。
Thread.State getState() 返回该线程的状态。该方法用于监视系统状态,不用于同步控制。
- Thread.State.NEW 至今还没有启动的线程处于这种状态。
- Thread.State.RUNNABLE 正在 Java 虚拟机中执行的线程处于这种状态。
- Thread.State.BLOCKED 受阻塞并等待某个监视器锁的线程处于这种状态。
- Thread.State.WAITING 无限期地等待另外一个线程来执行某一特定操做的线程处于这种状态。
- Thread.State.TIMED_WAITING 等待另外一个线程来执行取决于指定等待时间的操做的线程处于这种状态。
- Thread.State.TERMINATED 已退出的线程处于这种状态。
- 在给定时间点上,一个线程只能处于一种状态。这些状态是虚拟机状态,它们并无反映全部操做系统线程状态。
Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() 返回该线程因为未捕获到异常而忽然终止时调用的处理程序。
- void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) 设置该线程因为未捕获到异常而忽然终止时调用的处理程序。
- void interrupt() 中断线程。若是线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程当中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。
- boolean isAlive() 测试线程是否处于活动状态。若是线程已经启动且还没有终止,则为活动状态。
- boolean isInterrupted() 测试线程是否已经中断。线程的 中断状态 不受该方法的影响。
- void run() 若是该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;不然,该方法不执行任何操做并返回。
- boolean isDaemon() 测试该线程是否为守护线程。
- void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程是,Java虚拟机退出。该方法必须在启动线程前使用。
- void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
- 结果是两个线程并发地运行;当前线程(从调用返回给 start 方法)和另外一个线程(执行其 run 方法)。
- 屡次启动一个线程是非法的(会抛出IllegalThreadStateException)。特别是当线程已经结束执行后,不能再从新启动。
- String toString() 返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
Thread[Thread-0,5,main]
,Thread[main,5,main]
不经常使用方法
- void checkAccess() 断定当前运行的线程是否有权修改该线程。若是有安全管理器,则调用其 checkAccess 方法,并将该线程做为其参数。若是不容许当前线程访问该线程,抛出 SecurityException。
- ClassLoader getContextClassLoader() 返回该线程的上下文 ClassLoader。
- 上下文 ClassLoader 由线程建立者提供,供运行于该线程中的代码在加载类和资源时使用。
- 若是未设定,则默认为父线程的 ClassLoader 上下文。原始线程的上下文 ClassLoader 一般设定为用于加载应用程序的类加载器。
- void setContextClassLoader(ClassLoader cl) 设置该线程的上下文 ClassLoader。上下文 ClassLoader 能够在建立线程设置,并容许建立者在加载类和资源时向该线程中运行的代码提供适当的类加载器。
- ThreadGroup getThreadGroup() 返回该线程所属的线程组。若是该线程已经终止(中止运行),该方法则返回 null。
System.out.println(new Thread().getThreadGroup()); java.lang.ThreadGroup[name=main,maxpri=10]
void join() 让父线程等待子线程结束以后才能继续运行。
- When we call this method using a thread object, it suspends the execution of the calling thread until the object called finishes its execution.当咱们调用某个线程的这个方法时,这个方法会挂起调用线程,直到被调用线程结束执行,调用线程才会继续执行。
- 若是任何线程中断了当前线程则抛出 InterruptedException。当抛出该异常时,当前线程的 中断状态 被清除。
void join(long millis) 等待该线程终止的时间最长为 millis 毫秒。
- void join(long millis, int nanos) 等待该线程终止的时间最长为 millis 毫秒+nanos 纳秒。nanos 的值必须在 0-999999 之间。
已过期方法
- int countStackFrames() 已过期
- void destroy() 已过期
- void resume() 已过期
- void stop() 已过期
- void stop(Throwable obj) 已过期
- void suspend() 已过期
对 join 方法的测试案例
Thread 类中的 join 方法的主要做用就是同步,它可使得线程之间的并行执行变为串行执行
。
例以下面代码的意思就是:
Thread t1 = new TestThread("子线程1"); Thread t2 = new TestThread("子线程2"); t1.start();//子线程1开始执行 t1.join();//main 线程会放弃 cpu 控制权,直到线程 t1 执行完毕才获取 cpu 控制权 t2.start();//因此直到线程 t1 执行完毕才会执行子线程2
程序在 main 线程(父线程)中调用 t1 线程的 join 方法,则main 线程放弃 cpu 控制权
,并返回 t1 线程继续执行直到线程 t1 执行完毕。
因此结果是 t1 线程执行完后,才回到主线程执行,至关于在 main 线程中同步执行 t1 线程
,t1 执行完了,main 线程才有执行的机会。
public static void main(String[] args) { Thread thread1 = new Thread(() -> { System.out.println("线程1开始执行"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程1执行完毕"); }); Thread thread2 = new Thread(() -> { System.out.println("线程2开始执行"); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程2执行完毕"); }); thread1.start(); try { thread1.join(); } catch (InterruptedException e) { e.printStackTrace(); } thread2.start(); }
结果为:
线程1开始执行 线程1执行完毕 线程2开始执行 线程2执行完毕
若果没有 thread1.join() 结果为:
线程1开始执行 线程2开始执行 线程2执行完毕 线程1执行完毕
守护线程
在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程) 。
守护线程也称“服务线程”,守护线程会在没有用户线程可服务时自动离开
。
守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。
Daemon的做用是为其余线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。
User和Daemon二者几乎没有区别,惟一的不一样之处就在于虚拟机的离开:若是 User Thread已经所有退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了
。 由于没有了被守护者,Daemon也就没有工做可作了,也就没有继续运行程序的必要了。
守护线程并不是只有虚拟机内部提供,用户在编写程序时也能够本身设置守护线程。
Thread daemonTread = new Thread(); daemonThread.setDaemon(true); // 设定 daemonThread 为 守护线程 daemonThread.isDaemon(); // 验证当前线程是否为守护线程,返回 true 则为守护线程
守护线程使用注意
setDaemon(true)
必须在 start() 以前设置,不然会抛出一个 IllegalThreadStateException 异常,你不能把正在运行的常规线程设置为守护线程。
在Daemon线程中产生的新线程也是Daemon的。
Thread thread = new Thread(() -> System.out.println(new Thread().isDaemon()));//true thread.setDaemon(true); thread.start();
守护线程应该永远不去访问固有资源,如文件、数据库,由于它会在任什么时候候甚至在一个操做的中间发生中断。
public class Test { public static void main(String[] args) { Thread thread = new Thread(() -> { try { System.out.println("start"); Thread.sleep(1000);//阻塞1秒后运行 FileWriter writer = new FileWriter(new File("d://daemon.txt"), true); writer.write("daemon"); writer.close(); System.out.println("end"); } catch (Exception e) { e.printStackTrace(); } }); thread.setDaemon(true); //设置守护线程时不会有任何内容写入文件 thread.start(); //开始执行分进程 } }
结果,字符串并无写入指定文件。缘由很简单,直到主线程完成,守护线程仍处于1秒的阻塞状态,主线程很快就运行完了,而后虚拟机就退出了,Daemon中止服务,输出操做天然失败了。
守护线程的意义
用个比较通俗的比喻,任何一个守护线程都是整个JVM中全部非守护线程的保姆:只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就所有工做;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工做。
好比你正在用 Java 写成的编辑器写 Word 文档,你处理敲键盘事件的线程是个非守护线程, 除此以外,后台还有一个进行拼写检查的线程,它是个守护线程
,他尽可能不打扰你写稿子,他们能够同时进行,当守护线程发现有拼写错误时在状态条显示错误,可是你能够忽略。
好比城堡门前有个卫兵(守护线程),里面有诸侯(非守护线程),他们是能够同时干着各自的活儿,可是若是城堡里面的人都搬走了, 那么卫兵也就没有存在的意义了。
好比在使用长链接的comet服务端推送技术中,将消息推送线程设置为守护线程,服务于ChatServlet的servlet用户线程,在servlet的init启动消息线程,servlet一旦初始化后,一直存在服务器,servlet摧毁后,消息线程自动退出。
守护线程的使用案例
public class Test { static String account; public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println("用户输入的帐号为:" + account); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }); thread.setDaemon(true); //设定为守护线程,当main线程结束后,此线程也结束了 thread.start(); Scanner scanner = new Scanner(System.in); while (true) { System.out.println("请输入你的帐号:"); account = scanner.nextLine(); System.out.println(account + " 欢迎光临"); scanner.close(); break; } } }
ThreadGroup 线程组
public class ThreadGroup extends Object implements Thread.UncaughtExceptionHandler
线程组表示一个线程的集合,能够用来管理一组线程,实际没有什么大的用处。
线程组也能够包含其余线程组,线程组构成一棵树,在树中,除了初始线程组外,每一个线程组都有一个父线程组。
容许线程访问有关本身的线程组的信息,可是不容许它访问有关其线程组的父线程组或其余任何线程组的信息。
main方法执行后,将自动建立system线程组
和main线程组
,main方法所在线程存放在main线程组中。
public class Test { public static void main(String[] args) { ThreadGroup group = Thread.currentThread().getThreadGroup(); System.out.println(group);//java.lang.ThreadGroup[name=main,maxpri=10] Thread[] threads = new Thread[group.activeCount()]; group.enumerate(threads); for (Thread thread : threads) { System.out.println(thread); //Thread[main,5,main] } System.out.println("---------"); group.list(); } }
构造方法
- public ThreadGroup(String name):新线程组的父线程组是目前正在运行线程的线程组。
- public ThreadGroup(ThreadGroup parent,String name):新线程组的父线程组是指定的线程组。
得到线程组中的信息
ThreadGroup类中有几个方法可用来得到线程组中有关线程和子线程的信息
,这些信息包括线程组名字、线程组中可运行线程的数目、线程组中线程的最大优先级、线程组中各线程的名字等:
- int
activeCount()
; //返回此线程组中活动线程的估计数。结果并不能反映并发活动,而且可能受某些系统线程的存在状态的影响。 - int
activeGroupCount()
; //返回此线程组中活动线程组的估计数。结果并不能反映并发活动。 - int
enumerate(Thread list[])
; //把此线程组及其子组中的全部活动线程复制到指定数组中。 - int
enumerate(Thread[] list, boolean recurse)
; //若是 recurse 标志为 true,则还包括对此线程的子组中的全部活动线程的引用。若是数组过小而没法保持全部线程,则忽略额外的线程。 - int
enumerate(ThreadGroup list[])
; //把对此线程组中的全部活动子组的引用复制到指定数组中。 - int
enumerate(ThreadGroup list[], boolean recurse)
; //若是 recurse 标志为 true,则还包括对子组的全部活动子组的引用,等等。 - final int
getMaxPriority()
; //得到当前线程组中最大优先级 - final String
getName()
; //得到当前线程组的名字 - final ThreadGroup
getParent()
; //得到当前线程组的父线程组,顶级线程组仅为那些父线程组为 null 的线程组。 - boolean
parentOf(ThreadGroup g)
; //测试此线程组是否为线程组参数或其祖先线程组之一。 - boolean
isDaemon()
; //若是此线程组是一个后台程序线程组,则返回 true;不然返回 false。在中止后台程序线程组的最后一个线程或销毁其最后一个线程组时,自动销毁这个后台程序线程组。 - boolean
isDestroyed()
; //若是此对象被销毁,则返回 true - void
list()
; //将有关此线程组的信息打印到标准输出。此方法只对调试有用。 - void
uncaughtException(Thread t, Throwable e)
;//当此线程组中的线程由于一个未捕获的异常而中止,而且线程没有安装特定 Thread.UncaughtExceptionHandler 时,由 Java Virtual Machine 调用此方法。
对线程组操做
ThreadGroup类中的方法都是以线程组为操做目标的,其中包括设置线程组中线程的最大优先级、将线程组中全部线程挂起或恢复到可运行状态、终止线程组中全部线程等:
- final void
resume()
; //已过期。使被挂起的当前组内的线程恢复到可运行状态 - final void
setDaemon(boolean daemon)
; //更改此线程组的后台程序状态。 - final void
setMaxPriority(int pri)
; //设置线程组的最高优先级。线程组中已有较高优先级的线程不受影响。 - final void
stop()
;//已过期。终止当前线程组中全部线程 - final void
destroy()
; //销毁此线程组及其全部子组。此线程组必须为空,指示此线程组中的全部线程都已中止执行。 - final void
interrupt()
; //中断此线程组中的全部线程。 - final void
suspend()
; //已过期。挂起当前线程组中全部线程 - final void
checkAccess()
; //肯定当前运行的线程是否有权修改此线程组。 - String
toStrinng()
; //将当前线程组转换为String类的对象
2018-5-31