如何在java中确保代码块不会被任何其他线程中断

时间:2008-12-03 17:02:27

标签: java multithreading synchronization thread-priority

为例:

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

      *code that must not be interrupted*

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

SomeOtherThread.start();

YetAntherThread.start();

如何确保不可中断的代码不会被中断?

11 个答案:

答案 0 :(得分:14)

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

最好的要做的就是首先避免这个要求。

答案 1 :(得分:4)

实际上,如果您控制正在运行的线程实例,可以执行此操作。显然,这有很多警告(比如悬挂io操作),但实质上你可以继承Thread并覆盖interrupt()方法。然后你可以放置某种布尔值,这样当你翻转一个标志时,你的线程上的interrupt()调用会被忽略或者更好地存储起来供以后使用。

答案 2 :(得分:4)

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

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

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

它只会使应用程序变慢,因为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

将max设置为1,将min设置为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
    }
}

答案 3 :(得分:3)

假设你只关心应用程序级别的线程争用,并假设你愿意像其他人所建议的那样大惊小怪(恕我直言,一个非常糟糕的主意),那么你应该使用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();

答案 4 :(得分:3)

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

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

除非你运行实时java,否则你不能停止垃圾收集等。那是你想要的吗?

唯一剩下的就是:如果你只是希望你的所有其他java线程都不会互相打断,因为它们都倾向于在没有控制权的情况下无缘无故地访问某些资源,你做错了。正确设计,以便同步访问的对象/数据同步,然后不要担心其他线程因为同步对象是安全的而中断你。

我是否错过任何可能的案件?

答案 5 :(得分:2)

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

答案 6 :(得分:2)

错误处理是一个用例的例子,它可以阻止线程被中断。假设您有一个大型多线程服务器,并且出现一些外部条件,导致同时在多个工作线程上检测到错误。每个工作线程都会生成一个错误发生的通知。让我们进一步说明所需的响应是将服务器置于安全状态,以便在错误条件被清除后重新启动。

实现此行为的一种方法是让服务器的状态机按总顺序处理状态更改。一旦错误通知到达,您将其放入状态机并让状态机直接处理它而不会中断。这是您希望避免中断的地方 - 您希望第一个通知导致错误处理程序运行。进一步的通知不应该中断或重启。这听起来很容易,但实际上并非如此 - 假设状态机正在将服务器放在网上。您 想要中断它以让错误处理运行。所以有些东西是可以中断的,但有些则不是。

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

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

Java并没有尽我所能给你一个机制来阻止线程被中断。即使它确实如此,你可能也不想使用它,因为中断很容易发生在低级库中(例如,TCP / IP套接字处理),其中关闭中断的效果可能是非常不可预测的。

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

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

我花了很多时间来研究这样的问题 - 他们很难诊断,更不用解决了。 Java根本没有真正处理这种并发问题。听到更好的方法会很棒。

答案 7 :(得分:1)

最好的中途解决方案是同步一些公共对象上的所有线程,这样当你处于关键部分时,没有其他线程可以运行。

除此之外我不认为这是可能的。我很好奇需要这类解决方案的问题是什么?

答案 8 :(得分: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();

答案 9 :(得分: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();

答案 10 :(得分:1)

通常的程序不会随机中断线程。因此,如果您开始一个新的Thread并且没有传递对此Thread的引用,则可以确定没有任何东西会中断该Thread

在大多数情况下,对Thread私有的引用就足够了。其他所有东西都会变黑。

通常,像ExecutorService这样的工作队列会在被要求中断Thread时中断。在这种情况下,您想处理中断。