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

Запрет использования процессора/памяти в потоке в Java?

Я пишу приложение, которое будет работать с несколькими потоками, и хочу дросселировать использование ЦП/памяти этих потоков.

Существует аналогичный вопрос для С++, но я хочу попытаться избежать использования С++ и JNI, если это возможно. Я понимаю, что это может быть невозможно с использованием языка более высокого уровня, но мне любопытно узнать, есть ли у кого-нибудь идеи.

EDIT: Добавлена ​​щедрость; Мне бы хотелось, чтобы на самом деле были очень хорошие, хорошо продуманные идеи.

РЕДАКТИРОВАТЬ 2: Ситуация, в которой я нуждаюсь, это выполнение кода других людей на моем сервере. В принципе это полностью произвольный код, при этом единственной гарантией является то, что в файле класса будет основной метод. В настоящее время несколько полностью разрозненных классов, которые загружаются во время выполнения, выполняются одновременно как отдельные потоки.

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

РЕДАКТИРОВАТЬ 3:. Простой способ приблизить размер объекта с помощью java Instrumentation классов; в частности, метод getObjectSize. Обратите внимание, что для использования этого инструмента требуется специальная настройка.

4b9b3361

Ответ 1

Если я понимаю вашу проблему, одним из способов было бы сглаживание потоков, подобно тому, как воспроизведение видео выполняется на Java. Если вы знаете, что хотите использовать 50% ядер, ваш алгоритм должен спать приблизительно 0,5 секунды - потенциально распределяется в течение секунды (например, 0,25 с, 0,25 с сна, e.t.c.). Ниже приведен пример моего видеопроигрывателя.

long starttime = 0; // variable declared
//...
// for the first time, remember the timestamp
if (frameCount == 0) {
    starttime = System.currentTimeMillis();
}
// the next timestamp we want to wake up
starttime += (1000.0 / fps);
// Wait until the desired next time arrives using nanosecond
// accuracy timer (wait(time) isn't accurate enough on most platforms) 
LockSupport.parkNanos((long)(Math.max(0, 
    starttime - System.currentTimeMillis()) * 1000000));

Этот код будет зависеть от значения frame/second.

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

package concur;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class MemoryLimited {
    private static Semaphore semaphore = new Semaphore(1024 * 1024, true);
    // acquire method to get a size length array
    public static byte[] createArray(int size) throws InterruptedException {
        // ask the semaphore for the amount of memory
        semaphore.acquire(size);
        // if we get here we got the requested memory reserved
        return new byte[size];
    }
    public static void releaseArray(byte[] array) {
        // we don't need the memory of array, release
        semaphore.release(array.length);
    }
    // allocation size, if N > 1M then there will be mutual exclusion
    static final int N = 600000;
    // the test program
    public static void main(String[] args) {
        // create 2 threaded executor for the demonstration
        ExecutorService exec = Executors.newFixedThreadPool(2);
        // what we want to run for allocation testion
        Runnable run = new Runnable() {
            @Override
            public void run() {
                Random rnd = new Random();
                // do it 10 times to be sure we get the desired effect
                for (int i = 0; i < 10; i++) {
                    try {
                        // sleep randomly to achieve thread interleaving
                        TimeUnit.MILLISECONDS.sleep(rnd.nextInt(100) * 10);
                        // ask for N bytes of memory
                        byte[] array = createArray(N);
                        // print current memory occupation log
                        System.out.printf("%s %d: %s (%d)%n",
                            Thread.currentThread().getName(),
                            System.currentTimeMillis(), array,
                            semaphore.availablePermits());
                        // wait some more for the next thread interleaving
                        TimeUnit.MILLISECONDS.sleep(rnd.nextInt(100) * 10);
                        // release memory, no longer needed
                        releaseArray(array);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        // run first task
        exec.submit(run);
        // run second task
        exec.submit(run);
        // let the executor exit when it has finished processing the runnables
        exec.shutdown();
    }
}

Ответ 2

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

Чтобы в некоторой степени контролировать использование ЦП, вы можете использовать Thread.setPriority().

Что касается памяти, то нет такой вещи, как память в потоке. Сама концепция потоков Java означает разделяемую память. Единственный способ контролировать использование памяти - это параметры командной строки, такие как -Xmx, но нет способа управлять настройками во время выполнения.

Ответ 3

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

import java.lang.management.*;

ThreadMXBean TMB = ManagementFactory.getThreadMXBean();
long time = new Date().getTime() * 1000000;
long cput = 0;
double cpuperc = -1;

while(true){

if( TMB.isThreadCpuTimeSupported() ){
    if(new Date().getTime() * 1000000 - time > 1000000000){ //Reset once per second
        time = new Date().getTime() * 1000000;
        cput = TMB.getCurrentThreadCpuTime();
    }

    if(!TMB.isThreadCpuTimeEnabled()){
        TMB.setThreadCpuTimeEnabled(true);
    }

    if(new Date().getTime() * 1000000 - time != 0)
        cpuperc = (TMB.getCurrentThreadCpuTime() - cput) / (new Date().getTime() *  1000000.0 - time) * 100.0;                  
    }
//If cpu usage is greater then 50%
if(cpuperc > 50.0){
     //sleep for a little bit.
     continue;
}
//Do cpu intensive stuff
}

Ответ 4

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

Посмотрите на этот ответ, чтобы узнать, помогает ли это.

Когда все работающие потоки имеют одинаковый приоритет, они могут выполняться следующим образом:

t1, t2, t3,     t1, t2, t3,   t1, t2, t3

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

t1, t1, t1, t1,    t2,    t1, t1, t1 t3.

То есть, первый поток работает "чаще", а остальные.

Ответ 5

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

Однако все, что вы делаете, скорее всего, добавит накладные расходы и сложность, которые часто контрпродуктивны.

Если вы не можете объяснить, почему вы хотели бы это сделать (например, у вас плохо написанная библиотека, которой вы не доверяете и не можете получить поддержку), я бы предложил вам не нужно.

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

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

Ответ 6

Почему бы не вместо того, чтобы "threading" делать совместную многозадачность, было бы интересно посмотреть, можете ли вы манипулировать http://www.janino.net/ для запуска программы на определенное количество времени/набора инструкций, затем остановите и запустите следующую программу. По крайней мере, так оно справедливо, дайте каждому одно и то же время...

Ответ 7

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

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

Еще один теоретический путь к реализации - использовать Isolates. К сожалению, вам будет трудно найти универсальную JVM, которая реализует изоляты. Кроме того, стандартные API-интерфейсы позволяют вам управлять изоляцией, а не потоками изоляции.

Ответ 8

Единственный способ ограничить использование процессора Thread - либо блоком на ресурсе, либо часто вызывать yield().

Это не ограничивает использование ЦП ниже 100%, но дает другим потокам и процессам больше времени.

Ответ 9

Чтобы уменьшить процессор, вы хотите спать ваши потоки внутри обычных if и while.

while(whatever) {
    //do something
    //Note the capitol 'T' here, this sleeps the current thread.
    Thread.sleep(someNumberOfMilliSeconds);
}

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

Что касается памяти, я бы запускал профилировщик в отдельных потоках и выполнял некоторую настройку производительности. Если вы подавляете объем доступной памяти для потока, я думаю, что исключение из памяти или голодный поток вероятны. Я бы доверял JVM, чтобы обеспечить столько же памяти, сколько требуемый поток, и работать над уменьшением использования памяти, сохраняя только существенные объекты в области видимости в любой момент времени.