Synchronized之三:Synchronized与线程中断、线程wait
线程中断
正如中断二字所表达的意义,在线程运行(run方法)中间打断它,在Java中,提供了以下3个有关线程中断的方法
//中断线程(实例方法)
public void Thread.interrupt(); //判断线程是否被中断(实例方法)
public boolean Thread.isInterrupted(); //判断是否被中断并清除当前中断状态(静态方法)
public static boolean Thread.interrupted();
当一个线程处于被阻塞状态或者试图执行一个阻塞操作时,使用Thread.interrupt()
方式中断该线程,注意此时将会抛出一个InterruptedException的异常,同时中断状态将会被复位(由中断状态改为非中断状态),如下代码将演示该过程:
package com.dxz.synchronize; import java.util.concurrent.TimeUnit; public class InterruputSleepThread3 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
// while在try中,通过异常中断就可以退出run循环
try {
while (true) {
// 当前线程处于阻塞状态,异常必须捕捉处理,无法往外抛出
TimeUnit.SECONDS.sleep(2);
}
} catch (InterruptedException e) {
System.out.println("Interruted When Sleep");
boolean interrupt = this.isInterrupted();
// 中断状态被复位
System.out.println("interrupt:" + interrupt);
}
}
};
t1.start();
TimeUnit.SECONDS.sleep(2);
// 中断处于阻塞状态的线程
t1.interrupt();
}
}
结果:
Interruted When Sleep
interrupt:false
如上述代码所示,我们创建一个线程,并在线程中调用了sleep方法从而使用线程进入阻塞状态,启动线程后,调用线程实例对象的interrupt方法中断阻塞状态,并抛出InterruptedException异常,此时中断状态也将被复位。这里有些人可能会诧异,为什么不用Thread.sleep(2000);
而是用TimeUnit.SECONDS.sleep(2);
其实原因很简单,前者使用时并没有明确的单位说明,而后者非常明确表达秒的单位,事实上后者的内部实现最终还是调用了Thread.sleep(2000);
,但为了编写的代码语义更清晰,建议使用TimeUnit.SECONDS.sleep(2);
的方式,注意TimeUnit是个枚举类型。
除了阻塞中断的情景,我们还可能会遇到处于运行期且非阻塞的状态的线程,这种情况下,直接调用Thread.interrupt()
中断线程是不会得到任响应的,如下代码,将无法中断非阻塞状态下的线程:
package com.dxz.synchronize; import java.util.concurrent.TimeUnit; public class InterruputThread {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
while (true) {
System.out.println("未被中断");
}
}
};
t1.start();
TimeUnit.SECONDS.sleep(2);
t1.interrupt(); }
}
结果:
未被中断
未被中断
未被中断
...
虽然我们调用了interrupt方法,但线程t1并未被中断,因为处于非阻塞状态的线程需要我们手动进行中断检测并结束程序,改进后代码如下:
package com.dxz.synchronize; import java.util.concurrent.TimeUnit; public class InterruputThread {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
while (true) {
// 判断当前线程是否被中断
if (this.isInterrupted()) {
System.out.println("线程中断");
break;
}
} System.out.println("已跳出循环,线程中断!");
}
};
t1.start();
TimeUnit.SECONDS.sleep(2);
t1.interrupt();
}
}
结果:
线程中断
已跳出循环,线程中断!
是的,我们在代码中使用了实例方法isInterrupted判断线程是否已被中断,如果被中断将跳出循环以此结束线程。综合所述,可以简单总结一下中断两种情况:
- 一种是当线程处于阻塞状态或者试图执行一个阻塞操作时,我们可以使用实例方法interrupt()进行线程中断,执行中断操作后将会抛出interruptException异常(该异常必须捕捉无法向外抛出)并将中断状态复位;
- 另外一种是当线程处于运行状态时,我们也可调用实例方法interrupt()进行线程中断,但同时必须手动判断中断状态,并编写中断线程的代码(其实就是结束run方法体的代码)。有时我们在编码时可能需要兼顾以上两种情况,那么就可以如下编写:
package com.dxz.synchronize; import java.util.concurrent.TimeUnit; public class InterruputThread3 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
try {
// 判断当前线程是否已中断,注意interrupted方法是静态的,执行后会对中断状态进行复位
while (!Thread.interrupted()) {
System.out.println("服务中...");
TimeUnit.SECONDS.sleep(2);
}
} catch (InterruptedException e) {
System.out.println("中断");
// 中断状态被复位
System.out.println("interrupt:" + this.isInterrupted());
}
}
};
t1.start();
TimeUnit.SECONDS.sleep(2);
t1.interrupt();
}
}
结果:
服务中...
服务中...
中断
interrupt:false
中断与synchronized
事实上线程的中断操作对于正在等待获取的锁对象的synchronized方法或者代码块并不起作用,也就是对于synchronized来说,如果一个线程在等待锁,那么结果只有两种,要么它获得这把锁继续执行,要么它就保存等待,即使调用中断线程的方法,也不会生效。演示代码如下
package com.dxz.synchronize; import java.util.concurrent.TimeUnit; public class SynchronizedBlocked implements Runnable { public synchronized void f() {
System.out.println("Trying to call f()");
while (true) // Never releases lock
Thread.yield();
} /**
* 在构造器中创建新线程并启动获取对象锁
*/
public SynchronizedBlocked() {
// 该线程已持有当前实例锁
new Thread() {
public void run() {
f(); // Lock acquired by this thread
}
}.start();
} public void run() {
// 中断判断
while (true) {
if (Thread.interrupted()) {
System.out.println("中断线程!!");
break;
} else {
f();
}
}
} public static void main(String[] args) throws InterruptedException {
SynchronizedBlocked sync = new SynchronizedBlocked();
Thread t = new Thread(sync);
// 启动后调用f()方法,无法获取当前实例锁处于等待状态
t.start();
TimeUnit.SECONDS.sleep(1);
// 中断线程,无法生效
t.interrupt();
}
}
结果:
Trying to call f()
我们在SynchronizedBlocked构造函数中创建一个新线程并启动获取调用f()获取到当前实例锁,由于SynchronizedBlocked自身也是线程,启动后在其run方法中也调用了f(),但由于对象锁被其他线程占用,导致t线程只能等到锁,此时我们调用了t.interrupt();
但并不能中断线程。
等待唤醒机制与synchronized
所谓等待唤醒机制本篇主要指的是notify/notifyAll和wait方法,在使用这3个方法时,必须处于synchronized代码块或者synchronized方法中,否则就会抛出IllegalMonitorStateException异常,这是因为调用这几个方法前必须拿到当前对象的监视器monitor对象,也就是说notify/notifyAll和wait方法依赖于monitor对象,在前面的分析中,我们知道monitor 存在于对象头的Mark Word 中(存储monitor引用指针),而synchronized关键字可以获取 monitor ,这也就是为什么notify/notifyAll和wait方法必须在synchronized代码块或者synchronized方法调用的原因。
synchronized (obj) {
obj.wait();
obj.notify();
obj.notifyAll();
}
需要特别理解的一点是,与sleep方法不同的是wait方法调用完成后,线程将被暂停,但wait方法将会释放当前持有的监视器锁(monitor),直到有线程调用notify/notifyAll方法后方能继续执行,而sleep方法只让线程休眠并不释放锁。同时notify/notifyAll方法调用后,并不会马上释放监视器锁,而是在相应的synchronized(){}/synchronized方法执行结束后才自动释放锁。
转自:
http://blog.csdn.net/javazejian/article/details/72828483?locationNum=5&fps=1
Synchronized之三:Synchronized与线程中断、线程wait的更多相关文章
- java并发编程(一)线程状态 & 线程中断 & 线程间的协作
参考文章: Java线程的5种状态及切换:http://blog.csdn.net/pange1991/article/details/53860651 线程的5种状态: 1. 新建(NEW):新创建 ...
- 注意Thread.interrupt()方法的真正作用并不是用来中断线程
程序是很简易的.然而,在编程人员面前,多线程呈现出了一组新的难题,如果没有被恰当的解决,将导致意外的行为以及细微的.难以发现的错误. 在本篇文章中,我们针对这些难题之一:如何中断一个正在 ...
- 中断线程Interrupt()
以下是参考<<Java多线程模式>>的 1. sleep() & interrupt() 线程A正在使用sleep()暂停着: Thread.sleep(100000) ...
- java线程中断的办法
目录 中断线程相关的方法 中断线程 for循环标记退出 阻塞的退出线程 使用stop()方法停止线程 中断线程相关的方法 中断线程有一些相应的方法,这里列出来一下. 注意,如果是Thread.meth ...
- C#线程 使用线程
第三部分 使用线程 基于事件的异步模式 基于事件的异步模式(EAP)提供了一种简单的方法,通过这些方法,类可以提供多线程功能,而使用者无需显式启动或管理线程.它还提供以下功能: 合作取消模型 工作人员 ...
- java 线程的终止与线程中断
关于线程终止: 1.一般来讲线程在执行完毕后就会进入死亡状态,那该线程自然就终止了. 2.一些服务端的程序,可能在业务上需要,常驻系统.它本身是一个无穷的循环,用于提供服务.那对于这种线程我们该如何结 ...
- lesson6:java线程中断
正常的情况下,业务系统都不会去中断它的线程,但是由于一些特殊情况的发生,线程已经不能正常结束了,并且此类线程已经影响到业务系统提供服务的能力,如果系统设计的健壮,便会通过监控线程去主动的中断此类线程. ...
- Java 可中断线程
PART.1 无法中断的线程 一个无法中断的线程的例子. public class UninterruptableThread { @SuppressWarnings("deprecatio ...
- Java 并发 中断线程
Java 并发 中断线程 @author ixenos 对Runnable.run()方法的三种处置情况 1.在Runnable.run()方法的中间中断它 2.等待该方法到达对cancel标志的测试 ...
随机推荐
- 利用BURPSUITE检测CSRF漏洞
CSRF漏洞的手动判定:修改referer头或直接删除referer头,看在提交表单时,网站是否还是正常响应. 下面演示用Burpsuite对CSRF进行鉴定. 抓包. 成功修改密码完成漏洞的利用.
- 在springboot项目中获取pom.xml中的信息
最近做了一个新项目,用到了springboot.在搭建框架的过程中,需要读取pom.xml中version的值,本来想着是用自己用java解析xml来着.没想到maven提供了这么一个包,可以直接获取 ...
- sqlplus登陆scott用户,以及退出连接
进入sqlplus界面 即登陆成功,PLsql也一样 退出连接:
- 前端和后端采用接口访问时的调用验证机制(基于JWT的前后端验证)(思路探讨)
说明:基于前后端,尤其是使用Ajax请求的接口,现在市面上网页上调用的Ajax基本都是没有验证的,如果单独提取之后可以无线的刷数据. 继上一篇http://www.cnblogs.com/EasonJ ...
- .net core mvc启动顺序以及主要部件1
原文:.net core mvc启动顺序以及主要部件1 首先我是新人一个写这些东西也是为了增加记忆,有不对的地方请多多指教. 说回正题,打开Program.cs文件,看到在有个CrateWebHost ...
- Python奇技
本文目录 1. 显示有限的接口到外部 2. with的魔力 3. filter的用法 4. 一行作判断 5. 装饰器之单例 6. staticmethod装饰器 7. property装饰器 8. i ...
- 在运行hadoop是出现Master的9000端口拒绝访问的情况
出现9000端口拒绝访问的情况有可能是防火墙没有开放9000端口,可以选择关闭防火墙或者卸载防火墙,如果还是无法解决这种情况可能是因为hadoop的启动顺序不对. 应该按照如下得顺序启动 Step2: ...
- ElasticDownload
https://github.com/eltld/ElasticDownload
- HDU 6155 Subsequence Count 线段树维护矩阵
Subsequence Count Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 256000/256000 K (Java/Oth ...
- 减肥 day1
今天是我减肥第一天,现在体重是147斤, 早晨吃了一碗面,喝了一碗奶,中午吃了一个apple. 6点钟去打篮球,晚上去食堂稍微吃一点东西.