java核心技术卷I-线程

线程

多线程程序在较低的层次上扩展了多任务的概念:一个程序同时执行多个任务。通常,每一个任务称为一个线程( thread), 它是线程控制的简称。可以同时运行一个以上线程的程序称为多线程程序(multithreaded)。

多进程与多线程区别

本质的区别在于每个进程拥有自己的一整套变量, 而线程则共享数据(同时也带来了风险)。
共享变量使线程之间的通信比进程之间的通信更有效、 更容易。
与进程相比较, 线程更“ 轻量级”, 创建、 撤销一个线程比启动新进程的开销要小得多。

创建线程

构造线程的基本方法

public interface Runnable
{
	void run();
}
Runnable r = () -> { task code };
Thread t = new Thread(r);
t.start();

或者继承Thread类:

class MyThread extends Thread
{
	public void run()
	{
		task code
	}
}
MyThread m=new MyThread();
m.start();

不要调用 Thread 类或 Runnable 对象的 run 方法。 直接调用 run 方法, 只会执行同一个线程中的任务, 而不会启动新线程。应该调用 Thread.start 方法。这个方法将创建一个执行 run 方法的新线程

中断线程

当线程的 run 方法执行方法体中最后一条语句后, 并经由执行 return 语句返冋时,或者出现了在方法中没有捕获的异常时,线程将终止。 在 Java 的早期版本中, 还有一个stop方法, 其他线程可以调用它终止线程。但是, 这个方法现在已经被弃用了。
没有可以强制线程终止的方法。然而,interrupt 方法可以用来请求终止线程。
当对一个线程调用 interrupt 方法时,线程的中断状态将被置位。这是每一个线程都具有的 boolean 标志。每个线程都应该不时地检査这个标志, 以判断线程是否被中断。
要想弄清中断状态是否被置位,首先调用静态的 Thread.currentThread 方法获得当前线程, 然后调用 islnterrupted 方法:

while (!Thread.currentThread().islnterrupted() && more work to do)
{
	do more work
}

但是, 如果线程被阻塞, 就无法检测中断状态。这是产生 InterruptedException 异常的地方。当在一个被阻塞的线程(调用 sleep 或 wait) 上调用 interrupt 方法时,阻塞调用将会被Interrupted Exception 异常中断。
没有任何语言方面的需求要求一个被中断的线程应该终止。中断一个线程不过是引起它的注意。被中断的线程可以决定如何响应中断。某些线程是如此重要以至于应该处理完异常后, 继续执行,而不理会中断。但是,更普遍的情况是,线程将简单地将中断作为一个终止的请求。

Runnable r = () -> {
	try
	{
		while (!Thread.currentThread().islnterrupted0 && more work to do)
		{
			do more work
		}
	}catch(InterruptedException e){
		// thread was interr叩ted during sleep or wait
	}finally{
		clean up,if required
	}
	// exiting the run method terminates the thread
};

如果在每次工作迭代之后都调用 sleep 方法(或者其他的可中断方法,) islnterrupted 检测既没有必要也没有用处。如果在中断状态被置位时调用 sleep 方法,它不会休眠。相反,它将清除这一状态(丨)并拋出 IntemiptedException。因此, 如果你的循环调用 sleep,不会检测中断状态。
有两个非常类似的方法,interrupted 和 islnterrupted。Interrupted 方法是一个静态方法, 它检测当前的线程是否被中断。 而且, 调用 interrupted 方法会清除该线程的中断状态。另一方面,islnterrupted 方法是一个实例方法,可用来检验是否有线程被中断。调用这个方法不会改变中断状态。
java.Iang.Thread 1.0部分方法

void interrupts()

向线程发送中断请求。线程的中断状态将被设置为 true。如果目前该线程被一个 sleep调用阻塞,那么,InterruptedException 异常被抛出。

static boolean interrupted()

测试当前线程(即正在执行这一命令的线程)是否被中断。注意,这是一个静态方法。这一调用会产生副作用—它将当前线程的中断状态重置为 false。

boolean islnterrupted()

测试线程是否被终止。不像静态的中断方法,这一调用不改变线程的中断状态。

static Thread currentThread()

返回代表当前执行线程的 Thread 对象

线程状态

线程可以有如下 6 种状态:

New (新创建)
Runnable (可运行)
Blocked (被阻塞)
Waiting (等待)
Timed waiting (计时等待)
Terminated (被终止)

在这里插入图片描述
要确定一个线程的当前状态, 可调用 getState 方法

新创建线程

当用 new 操作符创建一个新线程时,如 newThread(rs), 该线程还没有开始运行。这意味着它的状态是 new。当一个线程处于新创建状态时,程序还没有开始运行线程中的代码。在线程运行之前还有一些基础工作要做

可运行线程

一旦调用 start 方法,线程处于 runnable 状态。一个可运行的线桿可能正在运行也可能没有运行, 这取决于操作系统给线程提供运行的时间。
一旦一个线程开始运行,它不必始终保持运行。事实上,运行中的线程被中断,目的是为了让其他线程获得运行机会。线程调度的细节依赖于操作系统提供的服务。抢占式调度系统给每一个可运行线程一个时间片来执行任务。当时间片用完,操作系统剥夺该线程的运行权, 并给另一个线程运行机会。当选择下一个线程时, 操作系统考虑线程的优先级。
在具有多个处理器的机器上,每一个处理器运行一个线程, 可以有多个线程并行运行。当然,如果线程的数目多于处理器的数目, 调度器依然采用时间片机制。

在任何给定时刻,一个可运行的线程可能正在运行也可能没有运行

被阻塞线程和等待线程

当线程处于被阻塞或等待状态时,它暂时不活动。它不运行任何代码且消耗最少的资源。直到线程调度器重新激活它。

当一个线程试图获取一个内部的对象锁, 而该锁被其他线程持有(而不是 javiutiUoncurrent 库中的锁), 则该线程进人阻塞状态。当所有其他线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程将变成非阻塞状态。
当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态。在调用Object.wait 方法或 Thread.join 方法, 或者是等待 java,util.concurrent 库中的 Lock 或 Condition 时, 就会出现这种情况。实际上,被阻塞状态与等待状态是有很大不同的。
有几个方法有一个超时参数。调用它们导致线程进人计时等待( timed waiting ) 状态。这一状态将一直保持到超时期满或者接收到适当的通知。带有超时参数的方法有Thread.sleep 和 Object.wait、Thread.join、 Lock,tryLock 以及 Condition.await 的计时版。

当一个线程被阻塞或等待时(或终止时,) 另一个线程被调度为运行状态。当一个线程被重新激活(例如, 因为超时期满或成功地获得了一个锁,) 调度器检查它是否具有比当前运行线程更高的优先级。如果是这样,调度器从当前运行线程中挑选一个, 剥夺其运行权,选择一个新的线程运行

被终止的线程

线程因如下两个原因之一而被终止:

因为 run 方法正常退出而自然死亡
因为一个没有捕获的异常终止了 run 方法而意外死亡。

线程属性

包括:线程优先级、守护线程、 线程组以及处理未捕获异常的处理器

线程优先级

在 Java 程序设计语言中,每一个线程有一个优先级。默认情况下, 一个线程继承它的父线程的优先级。可以用 setPriority 方法提高或降低任何一个线程的优先级。可以将优先级设置为在 MIN_PRIORITY (在 Thread 类中定义为 1 ) 与 MAX_PRIORITY (定义为 10 ) 之间的任何值。NORM_PRIORITY 被定义为 5

守护线程

可以通过调用

t.setDaemon(true);

将线程转换为守护线程(daemon thread。) 这样一个线程没有什么神奇。守护线程的唯一用途是为其他线程提供服务。

未捕获异常处理器

线程的 run方法不能抛出任何受查异常, 但是,非受査异常会导致线程终止。在这种情况下,线程就死亡了。
但是,不需要任何 catch子句来处理可以被传播的异常。相反,就在线程死亡之前, 异常被传递到一个用于未捕获异常的处理器。
该处理器必须属于一个实现 Thread.UncaughtExceptionHandler 接口的类。这个接口只有—个方法。

void uncaughtException(Thread t, Throwable e)

可以用 setUncaughtExceptionHandler 方法为任何线程安装一个处理器。也可以用Thread类的静态方法 setDefaultUncaughtExceptionHandler 为所有线程安装一个默认的处理器。替换处理器可以使用日志 API 发送未捕获异常的报告到日志文件。
ThreadGroup 类实现 Thread.UncaughtExceptionHandler 接口。它的 uncaughtException 方法做如下操作:

1 ) 如果该线程组有父线程组, 那么父线程组的 uncaughtException 方法被调用。
2 ) 否则, 如果Thread.getDefaultExceptionHandler 方法返回一个非空的处理器, 则调用该处理器。
3 ) 否则,如果Throwable 是 ThreadDeath 的一个实例, 什么都不做。
4 ) 否则,线程的名字以及 Throwable的栈轨迹被输出到 System.err 上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

局外人一枚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值