java 线程不足_Java 线程基础知识

wait() 和notify()、notifyAll()

这三个方法用于协调多个线程对共享数据的存取,所以必须在 Synchronized 语句块内使用这三个方法,否则会抛出错 IllegalMonitorStateException。前面说过 Synchronized 这个关键字用于保护共享数据,阻止其他线程对共享数据的存取。但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出 Synchronized 数据块时让其他线程也有机会访问共享数据呢?此时就用这三个方法来灵活控制。

wait() 方法使当前线程被阻塞挂起暂停执行并释放对象锁标志,让其他线程可以进入 Synchronized 数据块,当前线程被放入对象等待池中。当调用共享对象的 notify() 或者 notifyAll() 方法才会返回,此时返回的线程会加入到锁标志等待池中,只有锁标志等待池中的线程能够获取锁标志,如果锁标志等待池中没有线程,则 notify() 不起作用。

notifyAll() 则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。

需要注意的是,当线程调用共享对象的 wait() 方法时,当前线程只会释放当前共享对象的锁,当前线程持有的其他共享对象的监视器锁并不会释放。

线程中断

为啥需要中断呢?下面简单的举例情况:

比如我们会启动多个线程做同一件事,比如抢 12306 的火车票,我们可能开启多个线程从多个渠道买火车票,只要有一个渠道买到了,我们会通知取消其他渠道。这个时候需要关闭其他线程;

很多线程的运行模式是死循环,比如在生产者/消费者模式中,消费者主体就是一个死循环,它不停的从队列中接受任务,执行任务,在停止程序时,我们需要一种”优雅”的方法以关闭该线程;

在一些场景中,比如从第三方服务器查询一个结果,我们希望在限定的时间内得到结果,如果得不到,我们会希望取消该任务;

上面这几个例子线程已经在运行了,并不好去干涉,但是可以通过中断,告诉这个线程,你应该中断了。比如上面的例子中的线程再收到中断后,可以通过中断标志来结束线程的运行。当然,你也可以收到后,不做任何处理,这也是可以的。

在 Java 中,停止一个线程的主要机制是中断,中断并不是强迫终止一个线程,它是一种协作机制,是给线程传递一个取消信号,但是由线程来决定如何以及何时退出。

需要注意的是:在停止线程的时候,不要调用 stop 方法,该方法已经被废弃了,并且会带来不可预测的影响。

线程对中断的反应

RUNNABLE:线程在运行或具备运行条件只是在等待操作系统调度

WAITING/TIMED_WAITING:线程在等待某个条件或超时

BLOCKED:线程在等待锁,试图进入同步块

NEW/TERMINATED:线程还未启动或已结束

线程中断常用的方法

interrupt() :中断线程,将会设置该线程的中断状态位,即设置为true,中断的结果线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序本身。线程会不时地检测这个中断标示位,以判断线程是否应该被中断(中断标示值是否为true)。它并不像stop方法那样会中断一个正在运行的线程。

interrupted():第一次使用返回true,并清除中断标志位,在此之后查询中断状态isInterrupt()都会返回false,刚刚第一个例子也看到了,利用    第一次返回的true可以跳出循环。第二次以及以后都是返回false。

isInterrupted():仅仅查询中断标志位来判断是否发生中断并返回true或者false。

RUNNABLE 状态

如果线程在运行中,interrupt() 只是会设置线程的中断标志位,没有任何其它作用。线程应该在运行过程中合适的位置检查中断标志位,比如说,如果主体代码是一个循环,可以在循环开始处进行检查,如下所示:

public class InterruptRunnableDemo extendsThread {

@Overridepublic voidrun() {while (!Thread.currentThread().isInterrupted()) { // 也可以使用 !Thread.currentThread().interrupted() 来判断有没有中断//... 单次循环代码

}

System.out.println("done ");

}public static void main(String[] args) throwsInterruptedException {

Thread thread= newInterruptRunnableDemo();

thread.start();

Thread.sleep(1000);

thread.interrupt();

}

}

WAITING/TIMED_WAITING

线程执行如下方法会进入WAITING状态:

public final void join() throwsInterruptedExceptionpublic final void wait() throws InterruptedException

执行如下方法会进入TIMED_WAITING状态:

public final native void wait(long timeout) throwsInterruptedException;public static native void sleep(long millis) throwsInterruptedException;public final synchronized void join(long millis) throws InterruptedException

在这些状态时,对线程对象调用 interrupt() 会使得该线程抛出 InterruptedException,需要注意的是,抛出异常后,中断标志位会被清空(线程的中断标志位会由 true 重置为false,因为线程为了处理异常已经重新处于就绪状态),而不是被设置。比如说,执行如下代码:

Thread t = newThread (){

@Overridepublic voidrun() {try{

Thread.sleep(1000);

}catch(InterruptedException e) {//exception被捕获,但是为输出为false 因为标志位会被清空

System.out.println(isInterrupted());

}

}

};

t.start();try{

Thread.sleep(100);

}catch(InterruptedException e) {

}

t.interrupt();//置为true

InterruptedException 是一个受检异常,线程必须进行处理。我们在异常处理中介绍过,处理异常的基本思路是,如果你知道怎么处理,就进行处理,如果不知道,就应该向上传递,通常情况下,你不应该做的是,捕获异常然后忽略。

捕获到 InterruptedException,通常表示希望结束该线程,线程大概有两种处理方式:

向上传递该异常,这使得该方法也变成了一个可中断的方法,需要调用者进行处理

有些情况,不能向上传递异常,比如Thread的run方法,它的声明是固定的,不能抛出任何受检异常,这时,应该捕获异常,进行合适的清理操作,清理后,一般应该调用Thread的interrupt方法设置中断标志位,使得其他代码有办法知道它发生了中断

第一种方式的示例代码如下:

//抛出中断异常,由调用者捕获

public void interruptibleMethod() throwsInterruptedException{//... 包含wait, join 或 sleep 方法

Thread.sleep(1000);

}

第二种方式的示例代码如下:

public class InterruptWaitingDemo extendsThread {

@Overridepublic voidrun() {while (!Thread.currentThread().isInterrupted()) {try{//模拟任务代码

Thread.sleep(2000);

}catch(InterruptedException e) {//... 清理操作

System.out.println(isInterrupted());//false//重设中断标志位为true

Thread.currentThread().interrupt();

}

}

System.out.println(isInterrupted());//true

}public static voidmain(String[] args) {

InterruptWaitingDemo thread= newInterruptWaitingDemo();

thread.start();try{

Thread.sleep(100);

}catch(InterruptedException e) {

}

thread.interrupt();

}

}

BLOCKED

如果线程在等待锁,对线程对象调用interrupt()只是会设置线程的中断标志位,线程依然会处于BLOCKED状态,也就是说,interrupt()并不能使一个在等待锁的线程真正”中断”。我们看段代码:

public class InterruptWaitingDemo extendsThread {

@Overridepublic voidrun() {while (!Thread.currentThread().isInterrupted()) {try{//模拟任务代码

Thread.sleep(2000);

}catch(InterruptedException e) {//... 清理操作//重设中断标志位

Thread.currentThread().interrupt();

}

}

System.out.println(isInterrupted());

}public static voidmain(String[] args) {

InterruptWaitingDemo thread= newInterruptWaitingDemo();

thread.start();try{

Thread.sleep(100);

}catch(InterruptedException e) {

}

thread.interrupt();

}

}

BLOCKED 如果线程在等待锁,对线程对象调用 interrupt() 只是会设置线程的中断标志位,线程依然会处于 BLOCKED 状态,也就是说,interrupt() 并不能使一个在等待锁的线程真正”中断”。我们看段代码:

public classInterruptSynchronizedDemo {private static Object lock = new Object();//monitor

private static class A extendsThread {

@Overridepublic voidrun() {//等待lock锁

synchronized(lock) {//等待标志位被置为true

while (!Thread.currentThread().isInterrupted()) {

}

}

System.out.println("exit");

}

}public static void test() throwsInterruptedException {synchronized (lock) {//获取锁

A a = newA();

a.start();

Thread.sleep(1000);//a在等待lock锁,interrupt 无法中断

a.interrupt();//a线程加入当前线程,等待执行完毕

a.join();

}

}public static void main(String[] args) throwsInterruptedException {

test();

}

}

test 方法在持有锁 lock 的情况下启动线程 a,而线程 a 也去尝试获得锁 lock,所以会进入锁等待队列,随后 test 调用线程 a 的 interrupt 方法并等待线程线程 a 结束,线程 a 会结束吗?不会,interrupt 方法只会设置线程的中断标志,而并不会使它从锁等待队列中出来。线程a 会一直尝试获取锁,但是主线程也在等待 a 结束才会释放锁,所以相互之间互为等待,不能结束。

我们稍微修改下代码,去掉 test方法中的最后一行 a.join(),即变为:

public static void test() throwsInterruptedException {synchronized(lock) {

A a= newA();

a.start();

Thread.sleep(1000);

a.interrupt();

}//lock锁释放后 A线程重队列中出来

}

这时,程序就会退出。为什么呢?因为主线程不再等待线程 a 结束,释放锁 lock 后,线程 a 会获得锁,然后检测到发生了中断,所以会退出。

在使用 synchronized 关键字获取锁的过程中不响应中断请求,这是 synchronized 的局限性。如果这对程序是一个问题,应该使用显式锁,java 中的 Lock 接口,它支持以响应中断的方式获取锁。对于 Lock.lock(),可以改用 Lock.lockInterruptibly(),可被中断的加锁操作,它可以抛出中断异常。等同于等待时间无限长的 Lock.tryLock(long time, TimeUnit unit)。

NEW/TERMINATE

如果线程尚未启动 (NEW),或者已经结束 (TERMINATED),则调用 interrupt() 对它没有任何效果,中断标志位也不会被设置。比如说,以下代码的输出都是 false。

public classInterruptNotAliveDemo {private static class A extendsThread {

@Overridepublic voidrun() {

}

}public static void test() throwsInterruptedException {

A a= newA();

a.interrupt();

System.out.println(a.isInterrupted());

a.start();

Thread.sleep(100);

a.interrupt();

System.out.println(a.isInterrupted());

}public static void main(String[] args) throwsInterruptedException {

test();

}

}

IO操作

如果线程在等待 IO 操作,尤其是网络 IO,则会有一些特殊的处理,我们没有介绍过网络,这里只是简单介绍下。

实现此 InterruptibleChannel 接口的通道是可中断的:如果某个线程在可中断通道上因调用某个阻塞的 I/O 操作(常见的操作一般有这些:serverSocketChannel. accept()、socketChannel.connect、socketChannel.open、socketChannel.read、socketChannel.write、fileChannel.read、fileChannel.write)而进入阻塞状态,而另一个线程又调用了该阻塞线程的 interrupt 方法,这将导致该通道被关闭,并且已阻塞线程接将会收到 ClosedByInterruptException,并且设置已阻塞线程的中断状态。另外,如果已设置某个线程的中断状态并且它在通道上调用某个阻塞的 I/O 操作,则该通道将关闭并且该线程立即接收到 ClosedByInterruptException;并仍然设置其中断状态。

如果线程阻塞于 Selector 调用,则线程的中断标志位会被设置,同时,阻塞的调用会立即返回。

我们重点介绍另一种情况,InputStream 的 read 调用,该操作是不可中断的,如果流中没有数据,read 会阻塞 (但线程状态依然是 RUNNABLE ),且不响应 interrupt(),与 synchronized 类似,调用 interrupt() 只会设置线程的中断标志,而不会真正”中断”它,我们看段代码

public classInterruptReadDemo {private static class A extendsThread {

@Overridepublic voidrun() {while(!Thread.currentThread().isInterrupted()){try{

System.out.println(System.in.read())//wait input

} catch(IOException e) {

e.printStackTrace();

}

}

System.out.println("exit");

}

}public static void main(String[] args) throwsInterruptedException {

A t= newA();

t.start();

Thread.sleep(100);

t.interrupt();

}

}

线程t启动后调用 System.in.read() 从标准输入读入一个字符,不要输入任何字符,我们会看到,调用 interrupt() 不会中断 read(),线程会一直运行。

不过,有一个办法可以中断 read() 调用,那就是调用流的 close 方法,我们将代码改为:

public classInterruptReadDemo {private static class A extendsThread {

@Overridepublic voidrun() {while (!Thread.currentThread().isInterrupted()) {try{

System.out.println(System.in.read());

}catch(IOException e) {

e.printStackTrace();

}

}

System.out.println("exit");

}public voidcancel() {try{

System.in.close();

}catch(IOException e) {

}

interrupt();

}

}public static void main(String[] args) throwsInterruptedException {

A t= newA();

t.start();

Thread.sleep(100);

t.cancel();

}

}

我们给线程定义了一个 cancel 方法,在该方法中,调用了流的 close 方法,同时调用了 interrupt 方法,这次,程序会输出:

-1exit

也就是说,调用close方法后,read方法会返回,返回值为-1,表示流结束。

如何正确地取消/关闭线程

1. 以上,我们可以看出,interrupt 方法不一定会真正”中断”线程,它只是一种协作机制,如果 不明白线程在做什么,不应该贸然的调用线程的 interrupt 方法,以为这样就能取消线程。

2. 对于以线程提供服务的程序模块而言,它应该封装取消/关闭操作,提供单独的取消/关闭方法给调用者,类似于 InterruptReadDemo 中演示的 cancel 方法,外部调用者应该调用这些方法而不是直接调用 interrupt。

3. Java并发库的一些代码就提供了单独的取消/关闭方法,比如说,Future接口提供了如下方法以取消任务:boolean cancel(boolean mayInterruptIfRunning);

4. 再比如,ExecutorService提供了如下两个关闭方法:

voidshutdown();

List shutdownNow();

5. Future 和 ExecutorService 的 API 文档对这些方法都进行了详细说明,这是我们应该学习的方式。

参考文章:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/410999.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

16.Python略有小成(内置模块Ⅱ)

Python(内置模块Ⅱ) 一、规范化开发 一个py文件中 文件加载问题代码可读性,查询差要将一个py文件分开,合理的分成多个py文件 配置文件放在一起 : 一些轻易不改变的变量,一般命名 settings.py主逻辑函数放在一起 : 一般命名 src.py辅助功能函数放在一起 : 公共组件部分,装饰器,l…

java8 入门脚本之家_Java 8中的Lambda表达式

我想使用lambda表达式而不是for循环生成数字列表.所以让我们想要生成一个100以下的所有三角形数字的列表.三角数字是以下公式的数字:(n * n n)/ 2这样做最好的方法是什么?目前我有这个:Stream.iterate(1,n -> n 1).limit(100).map(n ->…

[原]nasm语法

工具:nasm 汇编gcc 编译cld 进行链接kscope 查看源代码make 工程管理khexedit 分析二进制文件一:nasm源文件布局:像其他汇编器一样, nasm源文件包含四个域的组合。(除了宏, 或者预编译器指示, 或者汇编…

matlab的算法java_matlab环境下的回归算法分析

嗨,我想对回归技术进行全面分析,因此将继续编辑这个问题 . 我正在尝试使用Matlab中提供的技术来解决回归问题 . 理想情况下,我想看看诸如此类的技巧线性回归Logistic回归贝叶斯回归支持向量回归回归的高斯过程问题陈述给定数据 X 和 Y &#…

C#仿QQ皮肤-常用用户控件EnterFrom1和窗体EntryForm的实现

导读部分 ------------------------------------------------------------------------------------------------------------- C#仿QQ皮肤-实现原理系列文章导航 最新版源码下载 http://www.cckan.net/thread-2-1-1.html 本次是我们窗体实现的最后一节&#xff0c…

idea中删除java类并提交_IDEA 删除java类的3种提示

1. 点击 delete 删除文件时,会弹出如下选项:Safe delete (with usage search)Search in comments and strings“Safe delete ( with usage search)”, 字面意思是使用搜索安全删除. 是否选中结果分别为:没有选中状态,直接删除.选中状态,删除前IDEA会搜寻…

S3C2440、S3C2450和S3C6410之间区别

S3C2440:  主频400MHz;  SDRam内存控制器;  支持Nor Flash和Nand Flash(SLC)  支持2种Flash启动;  内部LCD、I2C、Camera等控制器。 S3C2450:  主频533MHz;  双总线架…

mysql指定时间_MySQL查询指定时间的数据

MySQL查询指定时间的数据。亲测可用。

助教小结13

本周总结: 主要为完成团队任务五的批改 整体完成的还可以 第1组、第9组:未点击提交按钮总成绩打八折 优秀作业: 第5组:https://www.cnblogs.com/lishuya/p/11088462.html 千帆竟发图: 转载于:https://www.cnblogs.com/…

Oracle多表连接查询

多个表之间关系:一对多|(多对一) 多对多 一对一 3种 关系的完整性约束:实体完整性、参照完整性、用于定义的完整性。 必须满足实体完整性和参照完整性. 实体完整性:规定了字段|属性的约束 参照完整性:关系与关系之间的引用 某个字…

工作393-注册小程序

每个小程序都需要在 app.js 中调用 App 方法注册小程序实例,绑定生命周期回调函数、错误监听和页面不存在监听函数等。详细的参数含义和使用请参考 App 参考文档 。// app.js App({onLaunch (options) {// Do something initial when launch.},onShow (options) {//…

java工作台无法显示_【Eclipse】使用指南(18)搜索工作台

Searching the workbench搜索工作台In this section, you will search the workbench for Java elements.本小节,你将在工作台中搜索Java元素。In the Search dialog, you can perform file, text or Java searches. Java searches operate on the structure of the…

Gridview 鼠标悬浮光棒效果

代码 1 protectedvoidGridView1_RowDataBound(objectsender, GridViewRowEventArgs e)2 {3 4 //如果是绑定数据行 5 6 if(e.Row.RowType DataControlRowType.DataRow)7 {8 9 //鼠标经过时,行背景色变 10 e.Row.Attributes.Add("onmouseover",11 "cu…

神策埋点

首先进入后台管理点击生成导入代码: 选好选项后点生成: 以下是生成的代码: (function(para) {var p para.sdk_url, n para.name, w window, d document, s script,x null,y null;if(typeof(w[sensorsDataAnalytic201505]) ! undefined…

java 抽象类语法_JAVA基础语法8--多态/抽象类/抽象方法

多态继承、封装、多态、抽象是面向对象编程的四大基本特征。封装隐藏了类的内部实现机制,从而可以在不影响使用者的前提条件下,改变类的内部结构,同时保护了数据。继承是为了重用父类代码,同时为多态做准备。那么,什么…