1. 程式人生 > >Java指令碼API執行指令碼程式防止指令碼死迴圈

Java指令碼API執行指令碼程式防止指令碼死迴圈

Java指令碼API執行指令碼程式防止死迴圈

前提概要

當我們使用java指令碼API執行指令碼的時候,在一些我們並不知道指令碼的程式邏輯並且無法修改指令碼的特殊的場景下,如果指令碼中存在死迴圈(endless loop)或者高資源消耗的耗時迴圈語句,程式執行將會佔用大量的系統資源,比如說CPU、磁碟IO等。如果指令碼程式是死迴圈並且程式同步地執行指令碼的話,那麼程式將會一直阻塞下去。

解決辦法

由於在這些場景下,我們無法控制指令碼的程式邏輯,無法改動指令碼的程式碼,所以有必要對指令碼的執行進行控制。在這裡我們可以通過非同步呼叫的方式,防止指令碼執行阻塞對主程式帶來的負面影響。並且通過新增超時機制,對指令碼執行超時的執行緒進行強制關閉,避免有死迴圈嫌疑的惡意指令碼對系統資源的惡意消耗。

程式示例

1.編寫指令碼執行執行緒

/**
 * 指令碼執行執行緒
 */
private static abstract class ScriptThread extends Thread {
    private boolean done = false;

    boolean isDone() {
        return done;
    }

    @Override
    public void run() {
        execute();
        this.done = true;
    }

    public abstract
void execute(); }

說明:

  • 執行緒中新增變數 done , 用來標誌指令碼執行正常結束的情況。
  • run方法中呼叫execute()方法,execute執行完成將done標誌位置為true。
  • 通過isDOne()方法判斷執行緒是否正常結束。
  • 建立ScriptThread物件需要實現execute()方法,方法內部新增執行指令碼的邏輯程式碼。

2.定義指令碼執行超時異常類

/**
 * 指令碼執行超時異常
 */
public static class ScriptTimeoutException extends Exception {
    private static
final long serialVersionUID = 1L; private int timeout; public ScriptTimeoutException() { super("Script execute timeout."); } public ScriptTimeoutException(int timeout) { super("Script execute timeout."); this.timeout = timeout; } public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout = timeout; } }

說明:

  • 指令碼執行超時後丟擲ScriptTimeoutException物件,用來區分異常型別。

3.編寫指令碼執行阻塞方法

/**
 * 阻塞等待指令碼執行結束,或者到達超時時間
 * 
 * <pre>
 *     指令碼執行超過等待時間,強制停止指令碼執行緒
 * </pre>
 *
 * @param task 指令碼執行執行緒
 * @return 1:指令碼正常執行結束 2:指令碼強制退出執行 0:其他
 */
@SuppressWarnings("deprecation")
private static int waitScriptRunning(ScriptThread task) {
    int result = 0;
    long start = System.currentTimeMillis();
    while (true) {
        if (task.isDone()) {//如果指令碼執行已經結束
            result = 1;
            break;
        }
        long current = System.currentTimeMillis();
        if (current - start >= waitTime) {//超過指令碼執行等待時間還未結束,取消執行,強制關閉執行緒
            if (!task.isDone()) {
                result = 2;
                task.stop();
            }
            break;
        }
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            logger.warn(e.getMessage(), e);
        }
    }
    return result;
}

說明:

  • 呼叫該方法將阻塞等待,直到執行緒執行完畢或者執行緒超時,如果超時,則強制關閉執行緒。(JDK Thread#stop()方法不推薦使用,在當前的特殊場景下,我們無法修改指令碼邏輯,無法在指令碼內部控制執行緒的中斷,因此需要使用stop方法對執行緒進行強制退出。)

4.編寫指令碼執行方法

/**
 * 執行指令碼中的方法
 *
 * @param scriptLang   指令碼語言
 * @param script       需要執行的指令碼文字
 * @param functionName 執行的方法名
 * @param args         執行指令碼方法傳入的引數
 * @return 指令碼返回值
 * @throws Exception exception
 */
public static Object invokeScriptFunction(String scriptLang, String script, String functionName, Object... args)
        throws Exception {
    final Map<String, Object> map = new HashMap<>();

    ScriptThread scriptThread = new ScriptThread() {
        @Override
        public void execute() {
            try {
                ScriptEngine engine = getEngine(scriptLang);
                if (engine == null)
                    throw new Exception(String.format("Script engine not get! No support for script [%s].", scriptLang));
                engine.eval(script);
                map.put("value", ((Invocable) engine).invokeFunction(functionName, args));
            } catch (Exception e) {
                map.put("exception", e);
            }
        }
    };
    scriptThread.start();

    int result = waitScriptRunning(scriptThread);
    if (result == 2) {
        throw new ScriptTimeoutException(waitTime);
    }

    Object o = map.get("exception");
    if (o != null) {
        throw (Exception) o;
    }
    return map.get("value");
}

說明:

  • 方法邏輯中首先新建指令碼執行執行緒並提交執行緒的執行,接著呼叫等待方法阻塞等待指令碼的執行,如果返回結果說明指令碼超時,則丟擲超時異常。
  • 指令碼執行執行緒execute方法體中定義執行指令碼的邏輯,將執行指令碼正常情況下的返回值、異常情況下的異常物件儲存到map中。
  • 如果指令碼執行正常結束沒有超時,則拿到map中的內容,若異常物件不為空則丟擲異常物件;若異常物件為空則將指令碼返回值返回。

附上Java指令碼執行工具專案地址

相關推薦

Java指令碼API執行指令碼程式防止指令碼迴圈

Java指令碼API執行指令碼程式防止死迴圈 前提概要 當我們使用java指令碼API執行指令碼的時候,在一些我們並不知道指令碼的程式邏輯並且無法修改指令碼的特殊的場景下,如果指令碼中存在死迴圈(endless loop)或者高資源消耗的耗時迴圈語句,程

Java 利用指令碼API執行Groovy指令碼的方式

Java執行指令碼語言的方式(以Groovy為例) 介紹 運用java Script API可以非常方便的執行能夠在JVM執行的指令碼程式,並通過其指令碼引擎進行引數傳遞等。 Java Scripting API 包含一組類和介面,在 javax.sc

樹莓派自動執行應用程式指令碼及圖形介面)

/etc/rc.local :加入自己的執行命令。 /etc/inittab :初始狀態的設定。 /etc/init.d/ :編寫一個指令碼,放著裡面,需設為執行許可權。 以svn為例: 1.製作開機啟動指令碼svn_serve #!/bin/sh ### BEGIN INIT INF

shell指令碼定時執行php程式

其實很簡單的,在做開發的過程中可能會遇到一些情況需要定時得去跑一些程式,這時呢我們就可以用到這crontab這個定時器來幫助我們完成這些任務。 首先寫好你需要執行的php程式 我這裡呢就是一個PD

呼叫Java系統API獲取當前程式佔用記憶體

以下三行程式碼貼上去就可以了。 MemoryMXBean bean = ManagementFactory.getMemoryMXBean(); MemoryUsage memoryUsage = bean.getHeapMemoryUsage(); System.out

telnet 讓不能後臺執行程式後臺執行程式加入假檢測功能

        Telnet協議是TCP/IP協議族中的一員,是Internet遠端登陸服務的標準協議和主要方式。它為使用者提供了在本地計算機上完成遠端主機工作的能力。在終端使用者的電腦上使用teln

執行緒下HashMap的迴圈

多執行緒下HashMap的死迴圈 Java的HashMap是非執行緒安全的。多執行緒下應該用ConcurrentHashMap。 多執行緒下[HashMap]的問題(這裡主要說死迴圈問題): 1、多執行緒put操作後,get操作導致死迴圈。 2、多執行緒

HashMap多執行緒環境下的迴圈問題解釋

hashMap在多執行緒環境下,呼叫put方法出現的死迴圈是由於擴容時候resize方法導致的連結串列出現迴圈。 void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity =

為什麼在主執行緒的Looper.looper迴圈不會卡

public static void main(String[] args) { .... //建立Looper和MessageQueue物件,用於處理主執行緒的訊息 Looper.prepareMainLooper(); //建立ActivityThre

HashMap1.8中多執行緒擴容引起的迴圈問題

最近在學習併發,看到書上寫到hashmap在併發執行put操作時會引起死迴圈,因為在put中會引起擴容操作,使連結串列形成環形的資料結構,不是很明白,然後在網上看了一些部落格,但是部落格都是jdk1.7版本的,而1.8版本中的擴容操作已經和1.7版本中大不一樣了

HashMap原始碼解析、jdk7和8之後的區別、相關問題分析(多執行緒擴容帶來的迴圈

# 一、概覽 ```java HashMap map = new HashMap(); ``` 這個語句執行起來,在 jdk1.8 之前,會建立一個長度是 16 的 `Entry[]` 陣列,叫 `table`,用來儲存鍵值對。 在 jdk 1.8 後,不在這裡建立陣列了,而是在第一次 `pu

JAVA web呼叫執行python指令碼程式的四種方式,迴避java.lang.OutOfMemoryError:PermGen space記憶體溢位問題

我在網上搜到的JAVA呼叫python程式的三種方式: 方式一:呼叫python函式。可以傳入引數,獲取返回值。  public static void PythonFunctionTest(){         

nginx優化-利用nginx限制HTTP的請求方法--防止指令碼被上傳至伺服器執行指令碼對系統的破壞

利用nginx限制HTTP的請求方法 $request_method --防止指令碼被上傳至伺服器執行該指令碼對系統的破壞 可以上傳檔案,但是不能讓指令碼檔案執行成功 例如:站點目錄下有一個/image目錄,這個目錄是使用者上傳的一些圖片,不能阻止使用者上傳圖片,但要阻止使用者用特殊的方法執行裡面的檔

HtmlUnit執行自定javascript指令碼並回調java方法

import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebDriver; import org.openqa.selenium.htmlunit.HtmlUnitDr

java執行linux和windows指令碼工具類

我們有時候會 在java程式碼中,去執行一個linux shell指令碼或者windows觸發執行一個.bat指令碼 本文章,會寫一個通用的指令碼工具類,通過這個工具類,可以在java程式碼中,呼叫linux shell指令碼,或者window .bat指令碼。 比

python Windows計劃任務執行程式指令碼

up主第一次設定計劃任務,著實摸索了一會……才找到正確的方法,特來分享給大家: 第一步:開始選單開啟計劃任務 第二步:建立基本任務 第三步:填寫引數 建議最好把描述寫寫清除,不然過個把月,很容易就忘記這個是幹啥的了。  設定時間,後一直點選下一步

動態呼叫動態語言之Java指令碼API

我們不需要將動態語言編譯為 Java位元組碼就可以在 Java 應用程式中使用它們。使用 Java Platform, Standard Edition 6 (Java SE)中新增的指令碼包(並且向後相容 Java SE 5),Java 程式碼可以在執行時以一種簡單的、統一的方式呼叫多種動態語言。

java呼叫Linux執行Python爬蟲,並將資料儲存到elasticsearch--(一、環境指令碼搭建)

java呼叫Linux執行Python爬蟲,並將資料儲存到elasticsearch中 一、以下部落格程式碼使用的開發工具及環境如下: 1、idea: 2、jdk:1.8 3、elasticsearch:5.2.0 4、Linux 5、Python 6、maven 二、maven座標: <!--jav

rex 防止指令碼還沒執行完成,就立刻發起

[[email protected] rex]#cat Rexfile use Rex -feature => ['1.0']; use Rex::Misc::ShellBlock; use Rex::Misc::PerlBlock; use Rex::Misc::GetIpList

java類中執行指令碼或命令(比如Linux下的指令碼命令等)

可以使用java中的ProcessBuilder執行本地命令或指令碼等工作: 以下是一個簡單的使用java呼叫本地python指令碼的例子。從某工程程式碼中整理出來的,未封裝,僅供參考。 JAVA 程式碼: List<String> commands=