“核心技术:并发”的版本间差异
跳到导航
跳到搜索
(→线程状态) |
(→线程属性) |
||
第138行: | 第138行: | ||
== 线程属性 == | == 线程属性 == | ||
线程的各种属性,如:线程优先级、守护线程、线程组以及处理未捕获异常的处理器。 | |||
<syntaxhighlight lang="java"> | |||
private int priority; | |||
... | |||
private boolean daemon = false; | |||
... | |||
private ThreadGroup group; | |||
public final static int MIN_PRIORITY = 1; | |||
public final static int NORM_PRIORITY = 5; | |||
public final static int MAX_PRIORITY = 10; | |||
... | |||
// null unless explicitly set | |||
private volatile UncaughtExceptionHandler uncaughtExceptionHandler; | |||
// null unless explicitly set | |||
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler; | |||
</syntaxhighlight> | |||
=== 线程优先级(Priority) === | |||
* 默认情况下,线程会继承它的父线程的优先级;(可以在 init 方法中发现,同时还会继承 “group”、“daemon”) | |||
* 可以用setPriority 方法提高或降低任何一个线程的优先级。 | |||
* 不要将程序构建为功能的正确性依赖于优先级。 | |||
<pre> | |||
如果确实要使用优先级, 应该避免初学者常犯的一个错误。如果有几个高优先级的线程没有进入非活动状态, 低优先级的线程可能永远也不能执行。每当调度器决定运行一个新线程时, 首先会在具有高优先级的线程中进行选择, 尽管这样会使低优先级的线程完全饿死。 | |||
</pre> | |||
【???不用动态优先级吗,还是和底层实现有关?】 | |||
==== 相关方法 ==== | |||
===== java.lang.Thread ===== | |||
* void setPriority(int newPriority) | |||
*: 设置线程的优先级。优先级必须在Thread.MIN_PRIORITY 与Thread.MAX_PRIORITY之间。一般使用Thread.NORMJ»RIORITY 优先级。 | |||
* static int MIN_PRIORITY | |||
*: 线程的最小优先级。最小优先级的值为1。 | |||
* static int N0RM_PRI0RITY | |||
*: 线程的默认优先级。默认优先级为5。 | |||
* static int MAX—PRIORITY | |||
*: 线程的最高优先级。最高优先级的值为10。 | |||
* static void yield( ) | |||
*: 导致当前执行线程处于让步状态。如果有其他的可运行线程具有至少与此线程同样高的优先级,那么这些线程接下来会被调度。注意,这是一个静态方法。【让出CPU】 | |||
=== 守护线程(Daemon) === | |||
* 守护线程的唯一用途是为其他线程提供服务,当只剩下守护线程时,虚拟机就退出了。 | |||
* 可以通过调用“t .setDaemon(true);”将线程转换为守护线程(daemon thread)。 | |||
* 守护线程应该永远不去访问固有资源, 如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。 | |||
==== 相关方法 ==== | |||
===== java.lang.Thread ===== | |||
* void setDaemon( boolean isDaemon ) | |||
*: 标识该线程为守护线程或用户线程。这一方法必须在线程启动之前调用。 | |||
=== 线程组(ThreadGroup) === | |||
<pre> | |||
线程组是一个可以统一管理的线程集合。默认情况下,创建的所有线程属于相同的线程组,但是,也可能会建立其他的组。现在引入了更好的特性用于线程集合的操作,所以建议不要在自己的程序中使用线程组。 | |||
</pre> | |||
==== 相关方法 ==== | |||
===== java.lang.ThreadGroup ===== | |||
* void UncaughtException( Thread t, Throwable e) | |||
*: 如果有父线程组,调用父线程组的这一方法; 或者,如果Thread 类有默认处理器,调用该处理器,否则,输出栈轨迹到标准错误流上(但是, 如果e 是一个ThreadDeath对象,栈轨迹是被禁用的。ThreadDeath 对象由stop 方法产生, 而该方法已经过时)。 | |||
=== 未捕获异常处理器(uncaughtExceptionHandler) === | |||
* 线程的run 方法不能抛出任何受查异常。非受査异常会导致线程终止,在线程死亡之前,异常被传递到一个用于未捕获异常的处理器。 | |||
* 该处理器必须属于一个实现'''Thread.UncaughtExceptionHandler'''接口的类,这个接口只有—个方法“void uncaughtException(Thread t, Throwable e);” | |||
==== 相关方法 ==== | |||
===== java.lang.Thread ===== | |||
* static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler ) | |||
* static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() | |||
*: 设置或获取未捕获异常的默认处理器。 | |||
* void setUncaughtExceptionHandler( Thread.UncaughtExceptionHandlerhandler ) | |||
* Thread.UncaughtExceptionHandler getUncaughtExceptionHandler( ) | |||
*: 设置或获取未捕获异常的处理器。如果没有安装处理器, 则将线程组对象作为处理器。 | |||
===== java.Iang.Thread.UncaughtExceptionHandler ===== | |||
* void UncaughtException(Thread t , Throwable e) | |||
*: 当一个线程因未捕获异常而终止, 按规定要将客户报告记录到日志中。 | |||
*: 参数:t 由于未捕获异常而终止的线程;e 未捕获的异常对象; | |||
== 同步 == | == 同步 == | ||
== 阻塞队列 == | == 阻塞队列 == |
2020年10月19日 (一) 19:48的版本
什么是线程
一个程序同时执行多个任务,每一个任务称为一个线程(thread), 它是线程控制的简称。
- 进程与线程有的区别,在于每个进程拥有自己的一整套变量, 而线程则共享数据。
- 共享变量使线程之间的通信比进程之间的通信更有效、更容易。
使用线程
实现线程的两种方式:
- 实现Runnable接口,利用接口构造Thread对象;(Runnable 是一个函数式接口,可以用lambda 表达式)
Runnable r = () -> { /*task code*/ }; Thread t = new Thread(r);
- 继承Thread类,构造一个子类的对象;
class MyThread extends Thread { public void run() { //task code } }
- 不要调用Thread类或Runnable对象的run方法,而应该调用Thread.start方法。
- 直接调用run 方法,只会执行同一个线程中的任务,而不会启动新线程。
- 如果有很多任务, 要为每个任务创建一个独立的线程所付出的代价太大了。可以使用线程池来解决这个问题
相关方法
java.Iang.Thread
- Thread( Runnable target )
- 构造一个新线程, 用于调用给定目标的nm() 方法。
- void start( )
- 启动这个线程, 将引发调用mn() 方法。这个方法将立即返回, 并且新线程将并发运行。
- void run( )
- 调用关联 Runnable 的run 方法。
- static void sleep(long minis)
- 休眠给定的毫秒数。参数:millis 休眠的毫秒数
java.lang.Runnable
- void run( )
- 必须覆盖这个方法, 并在这个方法中提供所要执行的任务指令。
中断线程
当线程的run 方法执行方法体中最后一条语句后,并经由执行return 语句返冋时,或者出现了在方法中没有捕获的异常时,线程将终止。
- 在Java 的早期版本中,还有一个stop方法,其他线程可以调用它终止线程。
- 因为,stop会瞬间强行停止一个线程,且该线程持有的锁并不能释放;所以,这个方法现在已经被弃用了。
相关方法
java.Iang.Thread
- void interrupt()
- 向线程发送中断请求。线程的中断状态将被设置为true。如果目前该线程被一个sleep调用阻塞,那么, InterruptedException 异常被抛出。
- static boolean interrupted()
- 测试当前线程(即正在执行这一命令的线程)是否被中断。注意,这是一个静态方法。这一调用会产生副作用—它将当前线程的中断状态重置为false。
- boolean islnterrupted()
- 测试线程是否被终止。不像静态的中断方法,这一调用不改变线程的中断状态。
- static Thread currentThread()
- 返回代表当前执行线程的Thread 对象。
线程状态
线程可以有如下6 种状态:
- New ( 新创建)
- Runnable (可运行)
- Blocked ( 被阻塞)
- Waiting ( 等待)
- Timed waiting (计时等待)
- Terminated ( 被终止)
- 可调用“getState”方法获取一个线程的当前状态;
新创建线程
用 new 操作符创建一个新线程,未调用 start 方法,该线程还没有开始运行,即 New 状态
可运行线程
一旦调用 start 方法,则线程处于 runnable 状态。
- (Java 的规范说明没有将它作为一个单独状态:正在运行中的线程仍然处于可运行状态。)
结合线程状态图,理解为:“可运行” = “运行” + “就绪”;
被阻塞线程和等待线程
当线程处于被阻塞或等待状态时,它暂时不活动。它不运行任何代码且消耗最少的资源。直到线程调度器重新激活它。
阻塞(blocked)
阻塞:当一个线程请求锁,而该资源被其他线程持有时,该线程被置为阻塞状态。
- 请求内部的对象锁(非java.util.concurrent库中的锁)。
等待(waiting)
等待:当线程等待另一个线程通知调度器一个条件时,该线程主动进入等待状态。
如:
- Object.wait
- Thread.join
- java.util.concurrent 库中的Lock 或Condition
计时等待(timed waiting)
计时等待状态将一直保持到超时期满或者接收到适当的通知。
如:
- Object.wait(long millis)
- Thread.join(long millis)
- java.util.concurrent 库中的Lock(long millis) 或Condition(long millis)
被终止的线程(Terminated)
线程因如下两个原因之一而被终止:
- 因为run 方法正常退出而自然死亡。
- 因为一个没有捕获的异常终止了nm 方法而意外死亡。
- 不应该使用“stop”方法来终止线程!(虽然能做到)
相关方法
java.iang.Thread
- void join()
- 等待指定线程的终止。【在当前线程调用“s.join();”,则必须等待线程s执行完毕,当前线程才能继续执行】
- void join(long mi11is)
- 等待指定的线程死亡或者经过指定的毫秒数。
- Thread.State getState()
- 得到这一线程的状态;NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING 或 TERMINATED 之一。
- void stop()
- 停止该线程。这一方法已过时。
- void suspend()
- 暂停这一线程的执行。这一方法已过时。
- void resume()
- 恢复线程。这一方法仅仅在调用suspend() 之后调用。这一方法已过时。
线程属性
线程的各种属性,如:线程优先级、守护线程、线程组以及处理未捕获异常的处理器。
private int priority;
...
private boolean daemon = false;
...
private ThreadGroup group;
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
...
// null unless explicitly set
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
// null unless explicitly set
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
线程优先级(Priority)
- 默认情况下,线程会继承它的父线程的优先级;(可以在 init 方法中发现,同时还会继承 “group”、“daemon”)
- 可以用setPriority 方法提高或降低任何一个线程的优先级。
- 不要将程序构建为功能的正确性依赖于优先级。
如果确实要使用优先级, 应该避免初学者常犯的一个错误。如果有几个高优先级的线程没有进入非活动状态, 低优先级的线程可能永远也不能执行。每当调度器决定运行一个新线程时, 首先会在具有高优先级的线程中进行选择, 尽管这样会使低优先级的线程完全饿死。
【???不用动态优先级吗,还是和底层实现有关?】
相关方法
java.lang.Thread
- void setPriority(int newPriority)
- 设置线程的优先级。优先级必须在Thread.MIN_PRIORITY 与Thread.MAX_PRIORITY之间。一般使用Thread.NORMJ»RIORITY 优先级。
- static int MIN_PRIORITY
- 线程的最小优先级。最小优先级的值为1。
- static int N0RM_PRI0RITY
- 线程的默认优先级。默认优先级为5。
- static int MAX—PRIORITY
- 线程的最高优先级。最高优先级的值为10。
- static void yield( )
- 导致当前执行线程处于让步状态。如果有其他的可运行线程具有至少与此线程同样高的优先级,那么这些线程接下来会被调度。注意,这是一个静态方法。【让出CPU】
守护线程(Daemon)
- 守护线程的唯一用途是为其他线程提供服务,当只剩下守护线程时,虚拟机就退出了。
- 可以通过调用“t .setDaemon(true);”将线程转换为守护线程(daemon thread)。
- 守护线程应该永远不去访问固有资源, 如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。
相关方法
java.lang.Thread
- void setDaemon( boolean isDaemon )
- 标识该线程为守护线程或用户线程。这一方法必须在线程启动之前调用。
线程组(ThreadGroup)
线程组是一个可以统一管理的线程集合。默认情况下,创建的所有线程属于相同的线程组,但是,也可能会建立其他的组。现在引入了更好的特性用于线程集合的操作,所以建议不要在自己的程序中使用线程组。
相关方法
java.lang.ThreadGroup
- void UncaughtException( Thread t, Throwable e)
- 如果有父线程组,调用父线程组的这一方法; 或者,如果Thread 类有默认处理器,调用该处理器,否则,输出栈轨迹到标准错误流上(但是, 如果e 是一个ThreadDeath对象,栈轨迹是被禁用的。ThreadDeath 对象由stop 方法产生, 而该方法已经过时)。
未捕获异常处理器(uncaughtExceptionHandler)
- 线程的run 方法不能抛出任何受查异常。非受査异常会导致线程终止,在线程死亡之前,异常被传递到一个用于未捕获异常的处理器。
- 该处理器必须属于一个实现Thread.UncaughtExceptionHandler接口的类,这个接口只有—个方法“void uncaughtException(Thread t, Throwable e);”
相关方法
java.lang.Thread
- static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler )
- static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler()
- 设置或获取未捕获异常的默认处理器。
- void setUncaughtExceptionHandler( Thread.UncaughtExceptionHandlerhandler )
- Thread.UncaughtExceptionHandler getUncaughtExceptionHandler( )
- 设置或获取未捕获异常的处理器。如果没有安装处理器, 则将线程组对象作为处理器。
java.Iang.Thread.UncaughtExceptionHandler
- void UncaughtException(Thread t , Throwable e)
- 当一个线程因未捕获异常而终止, 按规定要将客户报告记录到日志中。
- 参数:t 由于未捕获异常而终止的线程;e 未捕获的异常对象;