Подтвердить что ты не робот

Как вы можете гарантировать в java, что блок кода не может быть прерван каким-либо другим потоком

exampl:

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

      *code that must not be interrupted*

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

SomeOtherThread.start();

YetAntherThread.start();

Как вы можете гарантировать, что код, который не должен прерываться, не прерывается?

4b9b3361

Ответ 1

Вы не можете - по крайней мере, не с нормальной Java, работая в нормальной, не в реальном времени операционной системе. Даже если другие потоки не прерывают ваш, другие процессы вполне могут это сделать. В принципе, вы не сможете гарантировать, что вы получите процессор для себя, пока не закончите. Если вам нужна такая гарантия, вы должны использовать что-то вроде Java Real-Time System. Я не знаю достаточно об этом, чтобы знать, будет ли это определенно предоставлять объект, который вы хотите, хотя.

Лучшее, что нужно сделать, это в первую очередь избежать этого требования.

Ответ 2

Собственно, вы можете это сделать, если вы управляете экземпляром потока, на котором вы работаете. Очевидно, что в этом есть тонна предостережений (например, операции висячего io), но по существу вы можете подклассировать Thread и переопределить метод interrupt(). вы можете затем добавить какое-то булево значение, чтобы при переворачивании флага вызовы прерывания() в вашем потоке либо игнорировались, либо лучше сохранялись для дальнейшего использования.

Ответ 3

Использование синхронизированного подхода (в различных формах, размещенных здесь) не помогает вообще.

Этот подход помогает только убедиться, что поток один выполняет критический раздел за раз, но это не то, что вы хотите. Вам нужно предотвратить прерывание потока.

Блокировка чтения/записи, похоже, помогает, но не имеет значения, поскольку ни один другой поток не пытается использовать блокировку записи.

Это только делает приложение немного медленнее, потому что JVM должен выполнить дополнительные проверки для выполнения синхронизированного раздела (используемого только одним потоком, таким образом, тратой процессора)

Собственно так, как у вас есть, поток не "действительно" прерывается. Но похоже, что это так, потому что оно должно давать процессорное время другим потокам. Способ работы потоков; процессор дает каждому потоку шанс немного поработать на очень короткие промежутки времени. Даже один, когда работает один поток, этот поток дает процессорное время другим потокам других приложений (предположив, что однопроцессорная машина поддерживает обсуждение просто).

Вероятно, причина, по которой вам кажется, что это поток, время от времени приостанавливается/прерывается, потому что система позволяет запускать каждый поток в приложении некоторое время.

Итак, что вы можете сделать?

Чтобы увеличить восприятие без прерываний, одно можно сделать, это назначить более высокий приоритет для вашего потока и уменьшить его для остальных.

Если все потоки имеют одинаковый приоритет, одно возможное расписание потоков 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
    }
}

Ответ 4

Вам действительно нужно оставить больше информации.

Вы не можете остановить выполнение других системных процессов, если вы не запускаете ОС реального времени. Это то, что вы имеете в виду?

Вы не можете остановить сбор мусора и т.д., если вы не запускаете java в режиме реального времени. Это то, что вы хотели?

Осталось только одно: если вы просто хотите, чтобы все ваши другие потоки Java не прерывали друг друга, потому что все они имеют доступ к некоторому ресурсу волей-неволей без контроля, вы делаете это неправильно. Правильно спроектируйте его так, чтобы объекты/данные, к которым нужно получить доступ синхронизированным образом, синхронизированы, тогда не беспокойтесь о том, что другие потоки прерывают вас, потому что ваши синхронизированные объекты безопасны.

Я пропустил все возможные случаи?

Ответ 5

Предполагая, что вас беспокоит только конфликт приложений на уровне приложений, и предполагая, что вы готовы суетиться с блокировками, как это было предложено другими (что, IMHO, это действительно плохая идея), тогда вы должны использовать 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();

Ответ 6

Прежде чем поток будет прерван, вызывается метод checkAccess() диспетчера безопасности. Внесите свой собственный менеджер безопасности, вызовите System.setSecurityManager, чтобы установить его и убедиться, что он не позволяет никому другому потоку прерывать вас, когда он находится в критическом разделе.

Ответ 7

Обработка ошибок - пример использования, когда очень важно остановить прерывание потоков. Предположим, у вас большой многопоточный сервер, и возникает какое-то внешнее условие, которое одновременно обнаруживает ошибки для нескольких рабочих потоков. Каждый рабочий поток генерирует уведомление о возникновении ошибки. Скажем далее, желаемый ответ должен привести сервер в безопасное состояние, которое позволит ему перезапуститься после устранения условия ошибки.

Одним из способов реализации этого поведения является наличие конечного автомата для сервера, который обрабатывает изменения состояния в общем порядке. Как только появляется сообщение об ошибке, вы помещаете его в конечный автомат и позволяете машине состояния обрабатывать ее полностью без перерыва. Здесь вы хотите избежать прерываний - вы хотите, чтобы первое уведомление вызывало запуск обработчика ошибок. Дальнейшие уведомления не должны прерывать или перезапускать его. Это звучит просто, но на самом деле это не так - предположим, что конечный автомат поставил сервер онлайн. Вы хотите прервать это, чтобы вместо этого выполнялась обработка ошибок. Поэтому некоторые вещи прерываются, а другие - нет.

Если вы прерываете поток обработки ошибок, он может вывести обработчик ошибок из воды во время обработки синхронизированного метода, оставив объекты в потенциально загрязненном состоянии. В этом суть проблемы - прерывания потоков идут вокруг обычного механизма синхронизации в Java.

Эта ситуация редко встречается в обычных приложениях. Однако, когда он возникает, результатом могут быть византийские неудачи, которые очень трудно предвидеть, не говоря уже о лечении. Ответ заключается в защите таких критических секций от прерываний.

Java не настолько, насколько я могу судить, давая вам механизм, чтобы остановить поток от прерывания. Даже если бы это было так, вы, вероятно, не захотели бы использовать его, потому что прерывание могло легко возникать в низкоуровневых библиотеках (например, обработка сокетов TCP/IP), где эффект отключения прерываний может быть очень непредсказуемым.

Вместо этого кажется, что лучший способ справиться с этим - создать приложение таким образом, чтобы такие прерывания не возникали. Я являюсь автором небольшого пакета государственных машин под названием Tungsten FSM (https://code.google.com/p/tungsten-fsm). FSM реализует простой конечный автомат, который обеспечивает обработку событий в полном порядке. В настоящее время я работаю над исправлением ошибок, которое точно описывает проблему, описанную здесь. FSM предложит один из способов решения этой проблемы, но есть и другие. Я подозреваю, что большинство из них связаны с какой-то государственной машиной и/или очередью событий.

Если вы примете подход предотвращения прерываний, он, конечно, создает еще одну проблему, если по какой-то причине блокируются потоки без прерывания. В этот момент вы просто застреваете и должны перезапустить процесс. Кажется, что все это отличается от тупика между потоками Java, что на самом деле является одним из способов прерывания потоков, которые могут быть заблокированы. На самом деле нет бесплатного обеда на этих типах проблем на Java.

Я потратил много времени на такие проблемы - их очень сложно диагностировать, не говоря уже о решении. Java действительно не справляется с такой проблемой concurrency очень хорошо. Было бы здорово услышать о лучших подходах.

Ответ 8

Лучшим решением на полпути является синхронизация всех потоков на каком-то общем объекте, чтобы никакие другие потоки не выполнялись, пока вы находитесь в критическом разделе.

Кроме этого, я не думаю, что это возможно. И мне очень любопытно, какая проблема требует такого решения?

Ответ 9

Просто запустите свой собственный под-поток и убедитесь, что вызовы прерываний никогда не фильтруются до него.

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();

Ответ 10

Я думаю, вам нужно заблокировать флаг прерывания. Что-то вроде этого (не проверено):

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();

Ответ 11

Обычная программа не случайным образом прерывает потоки. Таким образом, если вы создаете новый Thread и не передаете ссылку на этот Thread, вы можете быть совершенно уверены, что ничто не прервет этот Thread.

Держите ссылку на частную Thread достаточно в большинстве сценариев. Все остальное было бы взломанным.

Обычно рабочие очереди, такие как ExecutorService будут прерывать Thread когда их об этом попросят. В этих случаях вы хотите иметь дело с прерываниями.