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

пример:

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

      *code that must not be interrupted*

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

SomeOtherThread.start();

YetAntherThread.start();

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


person Community    schedule 03.12.2008    source источник
comment
Вам лучше быть чертовски уверенным, что код завершается, иначе у вас большие проблемы.   -  person Chris Conway    schedule 03.12.2008
comment
Прервал как? Доступ к конфликтующему ресурсу? Единственный поток, которому разрешено работать в этот момент времени?   -  person Andrew Rollings    schedule 03.12.2008
comment
Это зависит от того, что вы подразумеваете под прерванным... Вы имеете в виду: (a) вы не хотите, чтобы поток перехватывал InterruptedException, (b) вы не хотите, чтобы поток был заменен планировщиком JVM или (c) вы не хотите, чтобы процесс java был заменен подчиненной ОС (например, вы хотите очистить аппаратные прерывания)?   -  person Chris Conway    schedule 03.12.2008
comment
Всем спасибо за ответы. Я имел в виду, что он действительно не может быть прерван другими потоками в java-процессе, в котором находится этот поток. Я пытаюсь реализовать некоторые игровые механики rpg в апплете.   -  person    schedule 03.12.2008
comment
определить прерываемый!!! Вы имеете в виду, что хотите запретить другим потокам вызывать метод прерывания в этом потоке?? Вы расплывчаты   -  person TheSoftwareJedi    schedule 04.12.2008
comment
Я ищу ответ на связанный с этим вопрос - stackoverflow.com/questions/54560757/   -  person Alexander Mills    schedule 07.02.2019


Ответы (11)


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

Лучше всего лучше вообще избегать этого требования.

person Jon Skeet    schedule 03.12.2008
comment
Хороший комментарий!. Вы знаете, как это работает для многопроцессорных машин? Я знаю об одном процессоре, но я не знаю, есть ли способ выделить, скажем, 1 процессор для одного процесса. - person OscarRyz; 04.12.2008
comment
Это действительно зависит от операционной системы. Я считаю, что в Windows есть битовая маска, чтобы сказать, на каких процессорах процесс готов работать, но я не уверен, можете ли вы указать и не запускать что-либо еще на этом ядре! - person Jon Skeet; 04.12.2008

Предполагая, что вас интересует только конфликт потоков на уровне приложения, и предполагая, что вы готовы возиться с блокировками, как предлагают другие (что, ИМХО, является действительно плохой идеей), тогда вам следует использовать 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();
person Chris Conway    schedule 03.12.2008
comment
Вы можете объяснить, что не так с простым синхронизированным блоком? - person Johannes Schaub - litb; 03.12.2008
comment
Это решение не справляется со многими потоками, запускаемыми самой средой выполнения. Создание бесперебойного кода даже на уровне JVM требует поддержки в реальном времени. - person erickson; 03.12.2008
comment
@litb Если все потоки синхронизируются в одной и той же блокировке, то никакие два потока не могут работать одновременно. С помощью ReadWriteLock устройства чтения могут выполняться одновременно, но модуль записи работает в одиночку. - person Chris Conway; 03.12.2008
comment
@erickson Я согласен. Это лучшее решение (что-то вроде фальшивой проблемы), чем некоторые предлагали, но не очень хорошее решение. - person Chris Conway; 03.12.2008
comment
Крис, разве не это мы хотели ему сказать? то есть, что только один поток попадает в критическую секцию и ни один другой поток одновременно? насколько я могу судить, в вашем коде у вас есть точно такие же вещи, как если бы вы сделали это с синхронизацией. - person Johannes Schaub - litb; 03.12.2008
comment
@litb Если основной поток не находится в критических разделах, то сколько других потоков могут быть запущены? Посмотрите документы для ReadWriteLock. - person Chris Conway; 03.12.2008

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

person james    schedule 03.12.2008
comment
Планирование обычного потока в Java совершенно отличается от прерывания потока. Этот подход не поможет. - person Mocky; 03.12.2008
comment
Я читаю вопрос как о Thread.interrupt(), а не о планировании потоков. По крайней мере, у меня сейчас такая ситуация. Если это то, что имелось в виду, то будет ли это хорошим ответом? - person Tim Cooper; 26.06.2013

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

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

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

Единственное, что осталось: если вы просто хотите, чтобы все ВАШИ другие потоки Java не прерывали друг друга, потому что все они имеют тенденцию обращаться к какому-либо ресурсу волей-неволей без контроля, вы делаете это неправильно. Спроектируйте его правильно, чтобы объекты/данные, к которым НЕОБХОДИМО обращаться синхронизированным образом, были синхронизированы, а затем не беспокойтесь о том, что другие потоки прерывают вас, потому что ваши синхронизированные объекты безопасны.

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

person Bill K    schedule 03.12.2008
comment
Да вы сделали. Предполагать, что кто-то делает что-то неправильно только потому, что вы не можете придумать случаев, в которых это было бы желательно, неконструктивно. - person ; 04.12.2008
comment
Я полагаю, что другой случай заключается в том, что вы не задали правильный вопрос. Если ваш вопрос заключался в том, как я могу заставить этот раздел кода работать как можно лучше, то вам не следует спрашивать о прерываниях, вы должны смотреть на приоритеты потоков. - person Bill K; 04.12.2008
comment
Согласны ли вы с тем, что если бы я продемонстрировал больше знаний о деталях реализации Java, вы бы ответили по-другому? Или вообще не может? - person ; 06.12.2008
comment
Нет, честно говоря, я не вижу другой причины делать то, о чем вы просите. Вы до сих пор не уточнили. Это для производительности или безопасности (обоих я говорил), или есть какая-то третья возможность, которую вы держите в секрете? Вы сказали, что есть и третья возможность, но не уточнили, мне очень любопытно. - person Bill K; 08.12.2008

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

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

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

Это только делает приложение немного медленнее, потому что 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 это может быть так:

Больше ЦП для потока 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
    }
}
person OscarRyz    schedule 03.12.2008

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

person Yoni Roit    schedule 03.12.2008
comment
Это звучит интересно. Вы действительно когда-нибудь делали это? - person ; 04.12.2008

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

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

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

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

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

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

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

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

person R Hodges    schedule 04.05.2013

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

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();
person Jesse Barnum    schedule 11.01.2014
comment
этот поток, вероятно, все еще может быть прерван, если вы не контролируете весь код в нем - person ; 06.02.2019

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

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();
person Perdi Estaquel    schedule 06.10.2014

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

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

person krosenvold    schedule 03.12.2008
comment
Синхронизация на одном объекте сделает все потоки непрерывными везде. Вам нужен ReadWriteLock. - person Chris Conway; 03.12.2008
comment
да, но если вы не контролируете все потоки - например, сбор мусора или что-то еще - person ; 06.02.2019
comment
Причина, по которой я ищу эту функцию, состоит в том, чтобы предотвратить Zalgo, который может произойти в шаблонах асинхронного кодирования. Вам нужен некоторый код дальше по блоку для регистрации перед обратным вызовом и т. д. Поэтому вам нужно убедиться, что код дальше по блоку выполняется в течение некоторого детерминированного промежутка времени и т. д. - person ; 06.02.2019

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

В большинстве случаев достаточно оставить ссылку на Thread приватной. Все остальное было бы хакерским.

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

person benez    schedule 05.11.2018
comment
@MrCholo взгляните на эту статью: ibm.com/developerworks/library /j-jtp05236/index.html — показывает несколько примеров и сценариев - person benez; 11.02.2019