Как вы можете гарантировать в Java, что блок кода не может быть прерван любым другим потоком - PullRequest
11 голосов
/ 03 декабря 2008

exampl:

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

      *code that must not be interrupted*

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

SomeOtherThread.start();

YetAntherThread.start();

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

Ответы [ 11 ]

14 голосов
/ 03 декабря 2008

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

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

4 голосов
/ 04 декабря 2008

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

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

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

Это только делает приложение немного медленнее, потому что JVM должна выполнить дополнительные проверки для выполнения синхронизированного раздела (используется только одним потоком, таким образом, тратит впустую 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 это может выглядеть так:

Больше процессора в потоке 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 голосов
/ 03 декабря 2008

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

3 голосов
/ 03 декабря 2008

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

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

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

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

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

3 голосов
/ 03 декабря 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();
2 голосов
/ 04 мая 2013

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

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

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

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

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

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

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

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

2 голосов
/ 03 декабря 2008

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

1 голос
/ 05 ноября 2018

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

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

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

1 голос
/ 07 октября 2014

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

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();
1 голос
/ 12 января 2014

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

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