12

exampl:

new Thread(new Runnable() {
  public void run() {
    while(condition) {

      *code that must not be interrupted*

      *some more code*
    }
  }
}).start();

SomeOtherThread.start();

YetAntherThread.start();

How can you ensure that code that must not be interrupted won't be interrupted?

4

11 回答 11

14

你不能——至少不能使用普通的 Java,在普通的非实时操作系统上运行。即使其他线程不会中断您的线程,其他进程也可能会这样做。基本上,在您完成之前,您将无法保证自己拥有一个 CPU。如果你想要这种保证,你应该使用 Java Real-Time System 之类的东西。我对此知之甚少,不知道这是否肯定会提供您想要的设施。

最好的办法是首先避免该要求。

于 2008-12-03T17:17:34.933 回答
4

假设您只关心应用程序级线程争用,并假设您愿意按照其他人的建议对锁大惊小怪(恕我直言,这是一个非常糟糕的主意),那么您应该使用ReadWriteLock而不是简单的对象同步:

import java.java.util.concurrent.locks.*;

// create a fair read/write lock
final ReadWriteLock rwLock = new ReentrantReadWriteLock(true);

// the main thread grabs the write lock to exclude other threads
final Lock writeLock = rwLock.writeLock();

// All other threads hold the read lock whenever they do 
// *anything* to make sure the writer is exclusive when 
// it is running. NOTE: the other threads must also 
// occasionally *drop* the lock so the writer has a chance 
// to run!
final Lock readLock = rwLock.readLock();

new Thread(new Runnable() {
  public void run() {
    while(condition) {

      writeLock.lock();
      try {
        *code that must not be interrupted*
      } finally {
        writeLock.unlock();
      }

      *some more code*
    }
  }
}).start();

new SomeOtherThread(readLock).start();
new YetAntherThread(readLock).start();
于 2008-12-03T17:32:16.107 回答
3

实际上,如果您控制正在运行的线程实例,则可以执行此操作。显然,对此有很多警告(例如挂起 io 操作),但本质上您可以子类化 Thread 并覆盖 interrupt() 方法。然后,您可以放置​​某种布尔值,这样当您翻转标志时,线程上的 interrupt() 调用要么被忽略,要么更好地存储以供以后使用。

于 2008-12-03T17:34:52.030 回答
3

你真的需要留下更多的信息。

除非您在实时操作系统上运行,否则您无法阻止其他系统进程的执行。你是这个意思吗?

除非您运行实时 Java,否则您无法停止垃圾收集等。那是你想要的吗?

剩下的唯一一件事是:如果您只是希望所有其他 java 线程不要相互中断,因为它们都倾向于在不受控制的情况下随意访问某些资源,那么您做错了。正确设计它,以便同步需要以同步方式访问的对象/数据,然后不要担心其他线程会中断您,因为您的同步对象是安全的。

我错过了任何可能的情况吗?

于 2008-12-03T18:11:39.990 回答
3

使用同步方法(在此处发布的各种形式)根本没有帮助。

这种方法仅有助于确保一个线程一次执行关键部分,但这不是您想要的。您需要防止线程被中断。

读/写锁似乎有帮助,但没有任何区别,因为没有其他线程正在尝试使用写锁。

它只会使应用程序变慢一点,因为 JVM 必须执行额外的验证来执行同步部分(仅由一个线程使用,因此浪费 CPU)

实际上,按照您的方式,线程并没有“真正”被中断。但它似乎确实如此,因为它必须为其他线程提供 CPU 时间。线程的工作方式是;CPU 让每个线程有机会在很短的时间内运行一段时间。即使是在单个线程运行时,该线程也会与其他应用程序的其他线程产生 CPU 时间(假设单处理器机器以保持讨论简单)。

这可能是您认为线程不时暂停/中断的原因,因为系统让应用程序中的每个线程运行一段时间。

所以,你可以做什么?

为了增加没有中断的感觉,您可以做的一件事是为您的线程分配更高的优先级,并在其余部分降低它。

如果所有线程具有相同的优先级,则线程 1、2、3 的一个可能调度可能是这样的:

平均分配

1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3

虽然将最大值设置为 1,将最小值设置为 2,3,但它可能是这样的:

更多 CPU 到线程 1

1,1,1,2,1,1,3,1,1,1,2,1,1,1,3,1,2,1,1,1

对于要被另一个线程中断的线程,它必须处于可中断状态,通过调用 Object.wait、Thread.join 或 Thread.sleep 来实现

下面是一些有趣的代码进行实验。


代码1:测试如何改变线程的优先级。查看输出上的模式。

public class Test {
    public static void main( String [] args ) throws InterruptedException {
        Thread one = new Thread(){
            public void run(){
                while ( true ) {
                    System.out.println("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
                }
            }
        };
        Thread two = new Thread(){
            public void run(){
                while ( true ) {
                    System.out.println(".............................................");
                }
            }
        };
        Thread three = new Thread(){
            public void run(){
                while ( true ) {
                    System.out.println("------------------------------------------");
                }
            }
        };

        // Try uncommenting this one by one and see the difference.

        //one.setPriority( Thread.MAX_PRIORITY );
        //two.setPriority( Thread.MIN_PRIORITY );
        //three.setPriority( Thread.MIN_PRIORITY );
        one.start();
        two.start();
        three.start();

        // The code below makes no difference
        // because "one" is not interruptable
        Thread.sleep( 10000 ); // This is the "main" thread, letting the others thread run for aprox 10 secs.
        one.interrupt();  // Nice try though.
    }
}

代码 2. 一个线程如何被实际中断的示例(在这种情况下是在休眠时)

public class X{
    public static void main( String [] args ) throws InterruptedException  {
        Thread a = new Thread(){ 

            public void run(){ 

                int i = 1 ; 
                while ( true ){ 
                    if ( i++ % 100 == 0 ) try {
                        System.out.println("Sleeping...");
                        Thread.sleep(500);
                    } catch ( InterruptedException ie ) {
                        System.out.println( "I was interrpted from my sleep. We all shall die!! " );
                        System.exit(0);
                    }
                    System.out.print("E,"); 
                }
            }

         };
        a.start();


        Thread.sleep( 3000 ); // Main thread letting run "a" for 3 secs. 
        a.interrupt(); // It will succeed only if the thread is in an interruptable state
    }
}
于 2008-12-03T21:09:59.787 回答
2

在线程被中断之前,会调用安全管理器的 checkAccess() 方法。实现你自己的安全管理器,调用 System.setSecurityManager 来安装它,并确保它在临界区时不会让任何其他线程打断你。

于 2008-12-03T18:14:27.567 回答
2

错误处理是一个用例示例,在该用例中,阻止线程被中断非常有用。假设您有一个大型多线程服务器,并且出现了一些外部条件,导致在多个工作线程上同时检测到错误。每个工作线程都会生成一个发生错误的通知。让我们进一步说,期望的响应是使服务器进入安全状态,允许它在错误条件清除后重新启动。

实现此行为的一种方法是为服务器设置一个状态机,以总顺序处理状态更改。一旦错误通知到达,您将其放入状态机中,并让状态机在不中断的情况下对其进行处理。这是您希望避免中断的地方——您希望第一个通知导致错误处理程序运行。进一步的通知不应中断或重新启动它。这听起来很简单,但实际上并非如此——假设状态机将服务器置于联机状态。您可能希望中断它以让错误处理运行。所以有些事情是可以中断的,但有些事情不是。

如果您中断错误处理线程,它可能会在同步方法处理期间将错误处理程序从水中吹走,从而使对象处于潜在的脏状态。这是问题的症结所在——线程中断绕过 Java 中的正常同步机制。

这种情况在正常应用中很少见。然而,当它确实出现时,结果可能是难以预料的拜占庭式失败,更不用说治愈了。答案是保护这些关键部分免受中断。

据我所知,Java 并没有为您提供阻止线程被中断的机制。即使这样做了,您也可能不想使用它,因为中断很容易发生在低级库中(例如,TCP/IP 套接字处理),其中关闭中断的影响可能非常不可预测。

相反,处理这个问题的最佳方法似乎是以不发生此类中断的方式设计您的应用程序。我是一个名为 Tungsten FSM ( https://code.google.com/p/tungsten-fsm ) 的小型状态机包的作者。FSM 实现了一个简单的有限状态机,可确保按总顺序处理事件。我目前正在修复错误,以解决此处描述的问题。FSM 将提供一种解决此问题的方法,但还有许多其他方法。我怀疑它们中的大多数都涉及某种状态机和/或事件队列。

如果您采取防止中断的方法,如果不可中断线程由于某种原因被阻塞,它当然会产生另一个问题。那时,您只是被卡住了,必须重新启动该过程。这似乎与 Java 线程之间的死锁没有什么不同,这实际上是不可中断线程被阻塞的一种方式。对于 Java 中的这类问题,真的没有免费的午餐。

我花了很多时间研究这样的问题——它们很难诊断,更不用说解决了。Java 并没有真正很好地处理这种并发问题。很高兴听到更好的方法。

于 2013-05-04T16:56:35.157 回答
1

我认为你需要锁定一个中断标志。像这样的东西怎么样(未经测试):

new Thread() {
    boolean[] allowInterrupts = { true };

    @Override
    public void run() {
        while(condition) {
            allowInterrupts[0] = false;
            *code that must not be interrupted*
            allowInterrupts[0] = true;
            *some more code*
        }
    }

    @Override
    public void interrupt() {
        synchronized (allowInterrupts) {
            if (allowInterrupts[0]) {
                super.interrupt();
            }
        }
    }
}.start();

SomeOtherThread.start();

YetAntherThread.start();
于 2014-10-06T22:51:27.133 回答
1

只需启动您自己的子线程,并确保中断调用永远不会过滤到它。

new Thread(new Runnable() {
  public void run() {
    Thread t = new Thread() {
      public void run() {
        *code that must not be interrupted*
      }
    }
    t.start(); //Nothing else holds a reference to t, so nothing call call interrupt() on it, except for your own code inside t, or malicious code that gets a list of every live thread and interrupts it.

      while( t.isAlive() ) {
        try {
          t.join();
        } catch( InterruptedException e ) {
          //Nope, I'm busy.
        }
      }

      *some more code*
    }
  }
}).start();

SomeOtherThread.start();

YetAntherThread.start();
于 2014-01-11T21:44:19.317 回答
0

Best halfway solution would be to synchronize all threads on some common object so that no other threads are runnable while you're in the critical section.

Other than that I do not think it's possible. And I'm quite curious as to what kind of problem that requires this type of solution ?

于 2008-12-03T17:09:17.870 回答
-1

通常的程序不会随机中断线程。所以如果你开始一个新的Thread并且你没有传递对 this 的引用Thread,你可以很确定没有什么会打断它Thread

在大多数情况下,保持对私有的引用就Thread足够了。其他一切都会很糟糕。

通常,当被要求这样做时,像这样的工作队列ExecutorService会打断他们的工作队列。Thread在这些情况下,您需要处理中断。

于 2018-11-05T20:17:22.833 回答