为什么java中信号量类的AcquireUnterruptibly()方法不能按预期工作?
我有两个Java文件: Check.java为什么java中信号量类的AcquireUnterruptibly()方法不能按预期工作?,java,multithreading,semaphore,java.util.concurrent,Java,Multithreading,Semaphore,Java.util.concurrent,我有两个Java文件: Check.java import java.util.concurrent.Semaphore; class Check { public static void main(String[] args) { Semaphore s = new Semaphore(1); MyThread t1 = new MyThread("t1",s); MyThread t2 = new MyThread("t2",s); t1.start(); t2.st
import java.util.concurrent.Semaphore;
class Check
{
public static void main(String[] args)
{
Semaphore s = new Semaphore(1);
MyThread t1 = new MyThread("t1",s);
MyThread t2 = new MyThread("t2",s);
t1.start();
t2.start();
t2.interrupt();
}
}
MyThread.java
import java.util.concurrent.Semaphore;
class MyThread extends Thread
{
Semaphore s;
MyThread(String name, Semaphore s)
{
super(name);
this.s=s;
}
public void run()
{
try
{
s.acquireUninterruptibly();
for(int i=0; i<5; i++)
{
Thread.sleep(500);
System.out.println(Thread.currentThread().getName()+"-"+i);
}
s.release();
}
catch(InterruptedException e){}
}
}
如果我注释掉语句t2.interrupt,那么两个线程都可以正常执行。但是如果我不注释该语句,那么线程t2根本就没有执行。根据我对AcquireUnterruptibly方法的理解,线程t2应该继续等待许可证,即使在得到中断之后。那么为什么线程t2在收到一个中断后停止工作?第二个线程实际上开始工作,但由于您忽略了该线程的InterruptedException空catch块,因此无法检测到在线程2处于睡眠状态时执行了中断调用。如果中断,该方法将抛出InterruptdeException。由于这个异常,线程2退出循环而不打印任何内容 这是我在catch块中添加e.printStackTrace后看到的 t1-0 t1-1 t1-2 t1-3 t1-4 InterruptedException:睡眠中断 在java.lang.Thread.sleepNative方法中 在MyThread.runParser.java:31
首先,您不应该捕获异常并忽略它们。此外,您不应该以这样的方式放置异常处理程序:在异常情况下,代码将错过关键的发布调用。通常,最小化受保护的代码区域有助于跟踪问题 当您将代码更改为
Semaphore s = new Semaphore(1);
Runnable r = () -> {
System.out.println(Thread.currentThread().getName()+" - before acquire");
s.acquireUninterruptibly();
System.out.println(Thread.currentThread().getName()+" - acquired");
for(int i=0; i<5; i++) {
try {
Thread.sleep(500);
}
catch(InterruptedException e){
System.out.println(Thread.currentThread().getName()+" - "+e);
}
System.out.println(Thread.currentThread().getName()+" - "+i);
}
s.release();
};
Thread t1 = new Thread(r, "t1");
Thread t2 = new Thread(r, "t2");
t1.start();
t2.start();
t2.interrupt();
在大多数情况下。这是不能保证的,因为t2可能首先获取信号量
对于典型的定时,t1首先获取,t2在不间断地执行acquired之前或执行acquired时被中断。但是t2是在调用AcquireUnterruptibly之前、之间还是之后被中断并不重要,因为此方法忽略了中断。因此,⟨打断⟩ 状态一直持续,直到线程执行第一个与它相关的操作为止
在本例中,这是第一个睡眠呼叫。在代码中,这会终止线程,因为try…catch语句覆盖了整个代码。在我的示例中,它只覆盖一个睡眠呼叫,因为此呼叫将重置⟨打断⟩ 状态通过InterruptedException报告时,后续调用将在捕获异常后按预期休眠
您也可以自己检查和清除状态,例如
Semaphore s = new Semaphore(1);
Runnable r = () -> {
System.out.println(Thread.currentThread().getName()+" - before acquire");
s.acquireUninterruptibly();
System.out.println(Thread.currentThread().getName()+" - acquired");
if(Thread.interrupted())
System.out.println(Thread.currentThread().getName()
+" - interruption before or during acquire");
for(int i=0; i<5; i++) {
try {
Thread.sleep(500);
}
catch(InterruptedException e){
System.out.println(Thread.currentThread().getName()+" - "+e);
}
System.out.println(Thread.currentThread().getName()+" - "+i);
}
s.release();
};
Thread t1 = new Thread(r, "t1");
Thread t2 = new Thread(r, "t2");
t1.start();
t2.start();
t2.interrupt();
如前所述,这不是保证。有时,它会打印
t2-获取前
t1-获取前
t2获得性
t2-采集之前或期间的中断
t2-0
t2-1
t2-2
t2-3
t2-4
t1-获得性
t1-0
t1-1
t1-2
t1-3
t1-4
相反
与
如果当前线程在等待许可证时被中断,那么它将继续等待,但是线程被分配许可证的时间可能与未发生中断时收到许可证的时间相比有所变化。当线程确实从此方法返回时,将设置其中断状态
最后一句话是关键。当方法被中断时,它将继续等待并正常返回,但线程的“中断状态将被设置”,这将使下一个中断敏感操作对其作出反应
:
抛出:
…
InterruptedException-如果任何线程中断了当前线程。引发此异常时,当前线程的中断状态将被清除
@如果不是,那么两个线程都应该执行。为什么只有线程t1在使用语句t2.interrupt时才执行?@ScaryWombat不管t2是在调用s.acquireUnterrupty之前、之间还是之后被中断,只要它是在main调用t2.start之后和第一次睡眠调用完成之前被中断的,直到目前为止,它没有执行任何可感知的操作,因此,产生的行为将始终是相同的。我知道中断工作正常。但根据我的理解,AcquireUnterruptibly方法应该在得到中断后继续等待许可证。是的,事实是,在t2获得许可证并且许可证处于睡眠状态后,您调用中断。不幸的是,控制台中的输出顺序在多线程程序中可能不准确。@mynameisGYAN JDK类经过了非常彻底的测试,因此Java 1.5中添加的一个相对较旧的功能和信号量不太可能按所述工作。t2获得许可证后,不需要调用中断。重要的是,睡眠呼叫是t2的第一个动作,当它不间断地忽略它时,t2会对它做出反应。明确地说,线程的中断状态将一直保持,直到一个操作显式地重置它为止,而不间断地重置它。@Ivan您可以很容易地检查这一点。这适用于所有对中断敏感的操作。作为这些活动之间的普通代码 ons不受影响,并且使中断依赖于执行时间是没有意义的。