我有两个Java文件:
检查.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.start();
t2.interrupt();
}
}
我的线程.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 根本不执行。根据我对 acquireUninterruptibly(( 方法的理解,线程 t2 即使在中断后也应该继续等待许可。那为什么线程t2在得到中断后停止工作呢?
首先,您不应该捕获异常并忽略它们。此外,不应以某种方式放置异常处理程序,以免代码在异常情况下错过关键release()
调用。通常,最小化受保护的代码区域有助于跟踪问题。
当您将代码更改为
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();
你会得到
t1 - before acquire
t2 - before acquire
t1 - acquired
t1 - 0
t1 - 1
t1 - 2
t1 - 3
t1 - 4
t2 - acquired
t2 - java.lang.InterruptedException: sleep interrupted
t2 - 0
t2 - 1
t2 - 2
t2 - 3
t2 - 4
在大多数情况下。这不能保证,因为t2
可能首先获取信号量。
对于典型的计时,t1
首先获取,t2
在执行acquireUninterruptibly()
之前或期间被中断。但是t2
是在调用acquireUninterruptibly()
之前、中间还是之后中断并不重要,因为此方法会忽略中断。因此,⟨中断⟩状态将一直持续到线程执行第一个操作为止。
在此示例中,这是第一个sleep
调用。在您的代码中,这会终止线程,因为您的try … catch
语句涵盖了整个代码。在我的示例中,它仅涵盖一个sleep
调用,并且由于此调用将在通过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();
这可能导致
t1 - before acquire
t2 - before acquire
t1 - acquired
t1 - 0
t1 - 1
t1 - 2
t1 - 3
t1 - 4
t2 - acquired
t2 - interruption before or during acquire
t2 - 0
t2 - 1
t2 - 2
t2 - 3
t2 - 4
如前所述,不能保证。有时,它会打印
t2 - before acquire
t1 - before acquire
t2 - acquired
t2 - interruption before or during acquire
t2 - 0
t2 - 1
t2 - 2
t2 - 3
t2 - 4
t1 - acquired
t1 - 0
t1 - 1
t1 - 2
t1 - 3
t1 - 4
相反。
与Semaphore.acquireUninterruptibly()
的文档进行比较
中断,则它将继续等待,但与未发生中断时为线程分配许可证的时间相比,线程获得许可证的时间可能会发生变化。当线程从此方法返回时,将设置其中断状态。
最后一句话是关键点。当方法被中断时,它会继续等待并正常返回,但线程的"中断状态将被设置",这将使下一个中断敏感操作对其做出反应。
相比之下,Thread.sleep
抛出:
...
InterruptedException
- 如果任何线程中断了当前线程。引发此异常时,将清除当前线程的中断状态。
您的第二个线程实际上开始工作,但由于您忽略了InterruptedException
(它的空捕获块(,因此您无法检测到您的interrupt()
调用是在线程 2 处于睡眠方法时执行的。如果中断,该方法会抛出InterruptdeException
。由于该异常,您的线程 2 退出 for 循环而不打印任何内容。
这是我在 catch 块中添加e.printStackTrace()
后看到的。
T1-0
T1-1
T1-2
T1-3
T1-4
java.lang.InterruptedException: sleep Interrupted at java.lang.Thread.sleep(Native Method( at MyThread.run(Parser.java:31(