วิธีการยกเลิกในอนาคต (boolean b) จะไม่ทำงานเมื่อใช้ในแอปพลิเคชัน GUI

ฉันมีรหัสต่อไปนี้:

public class Cancelling {
public static void main(String args[]) {
    ToBeCancelled tbc = new ToBeCancelled();
    ForkJoinPool pool = new ForkJoinPool(1);
    Future<?> future = pool.submit(tbc);

    try {
        Thread.sleep(3000);
    } catch (InterruptedException ie) {}

    future.cancel(true);
    if (future.isCancelled())
        System.out.println("The task has been cancelled");
}
}

ด้วยคลาส ToBeCancelled ที่เป็น:

public class ToBeCancelled implements Runnable {
public void run() {
    try {
        Thread.sleep(5000); // should throw exception here
    } catch (Exception e) {
        return; // should exit
    }

    System.out.println("I should never be able to print this");
}
}

เธรดหลักควรเริ่มต้น รอ 3 วินาที จากนั้นยกเลิกงาน ToBeCancelled โดยใช้ future.cancel(true) จากนั้นควรพิมพ์ The task has been cancelled ในขณะที่งานไม่เคยพิมพ์ข้อความเลย อย่างน้อยนี่คือสิ่งที่เกิดขึ้นเมื่อฉันเริ่มจากคอนโซล

เมื่อฉันเริ่มต้นจากแอปพลิเคชัน GUI ด้วย TextArea ที่เอาต์พุตถูกเปลี่ยนเส้นทางไป นั่นไม่เป็นเช่นนั้น วิธีการหลักจะพิมพ์ The task has been cancelled แต่งานก็พิมพ์ I should never be able to print this ด้วย

นี่กำลังทำให้ฉันเป็นบ้า จากสิ่งที่ฉันเข้าใจ งานควรได้รับคำสั่ง cancel ในขณะที่ใช้วิธี Thread.sleep(5000) ซึ่งจะทำให้เกิดข้อยกเว้นที่ตามมา และทำให้เธรดส่งคืน แต่มันไม่เกิดขึ้นแต่หลักคิดว่าถูกยกเลิกแล้ว เหมือนกับว่าเมธอด cancel ถูกละเลยโดยงานโดยสิ้นเชิง

ฉันได้ลองทุกอย่างที่คิดได้แล้ว โดยตรวจสอบค่าที่ส่งคืนเป็น cancel ทำให้งานรอนานขึ้นโดยใช้ Thread.currentThread().isInterrupted() แต่ก็ไม่มีอะไรทำงาน

ฉันรู้สึกเหมือนขาดบางสิ่งที่เรียบง่ายจริงๆ แต่ฉันไม่สามารถค้นหาว่ามันคืออะไร มีความคิดอะไรบ้าง?

ในกรณีที่ใครคิดว่าอาจเป็นอะไรบางอย่างในแอปพลิเคชัน GUI นี่คือวิธีที่เริ่มโปรแกรม:

public static void StartProgram(String name) {
    try {
        Method m = Class.forName(name).getDeclaredMethod("main",String[].class);
        Object[] args = new Object[1];
        String s[] = new String[2];
        s[0] = tf1.getText();
        s[1] = tf2.getText();
        args[0] = s;
        t = new Thread(new ProgramStarter(args, m));
        t.start();
    } catch (Exception e) {
        e.printStackTrace();
    }       
}

โดยที่ ProgramStarter เป็น:

public class ProgramStarter implements Runnable {
private Object[] args;
private Method m;

public ProgramStarter(Object args[], Method m) {
    this.args = args;
    this.m = m;
}

public void run() {
    try {
        m.invoke(null, args);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
}

person user3648026    schedule 18.05.2014    source แหล่งที่มา
comment
เป็นไปได้ไหมที่เวลาผ่านไปเพียงพอแล้วและเธรดไม่อยู่ในโหมดสลีปก่อนการขัดจังหวะที่ทำให้เกิดข้อยกเว้น ลองเพิ่มเวลานอน   -  person Sotirios Delimanolis    schedule 18.05.2014
comment
ฉันได้ลองแล้ว แต่มันไม่เปลี่ยนแปลงอะไรเลย ฉันแน่ใจด้วยว่าไม่เป็นเช่นนั้นเพราะเมื่อโปรแกรมหลักพิมพ์ The Task ถูกขัดจังหวะ หมายความว่าโปรแกรมได้เรียกใช้วิธีการ cancel แล้ว และ 2 วินาทีหลังจากนั้นงานก็บอกว่าฉันไม่ควรพิมพ์สิ่งนี้ได้เลย   -  person user3648026    schedule 18.05.2014


คำตอบ (1)


ปัญหาคือการยืนยันของคุณไม่ถูกต้อง คุณคิดว่าโค้ดของคุณใช้งานได้เมื่อเรียกใช้จากคอนโซล แต่จริงๆ แล้ว มันล้มเหลวในทุกกรณี เมื่อรันจากคอนโซล เธรดหลักของคุณสิ้นสุดหลังจากความพยายามที่จะยกเลิกในอนาคต และ JVM จะหยุดทำงานเนื่องจากมีเธรด daemon เหลืออยู่ใน JVM เท่านั้น เนื่องจากการยกเลิก JVM คุณไม่สังเกตว่าการยกเลิกไม่ได้ผล

เมื่อเพิ่ม sleep ที่ส่วนท้ายของวิธีการ main ของคุณเพื่อชะลอการยกเลิก JVM คุณจะสังเกตเห็นว่า "I should never be able to print this" ถูกพิมพ์เมื่อเรียกใช้จากคอนโซลเช่นกัน ดังนั้นข้อแตกต่างระหว่าง GUI และเวอร์ชันคอนโซลก็คือ Event Dispatch Thread ที่ทำงานอยู่จะป้องกันไม่ให้ JVM ยุติการทำงาน ดังนั้นคุณจะเห็นว่ามันไม่ทำงาน


สิ่งสำคัญที่สุดคือ: อย่าใช้ ForkJoinPool เว้นแต่คุณจะมีเหตุผลในเรื่องนี้

เนื่องจากคุณเพียงต้องการให้ submit เป็นโปรแกรมเรียกทำงานเธรดพื้นหลังเดียวแบบธรรมดา คุณจึงสามารถสร้างโปรแกรมเรียกทำงานโดยใช้ Executors.newFixedThreadPool(1) ได้ สิ่งนี้มีพฤติกรรมที่ไม่คาดคิดน้อยกว่า: เธรดไม่ใช่ daemon ตามค่าเริ่มต้น และ Future จะ cancel โดยมีการหยุดชะงักตามที่คาดไว้

person Holger    schedule 19.05.2014
comment
ใช่แล้ว นั่นคือมัน การเพิ่มโหมดสลีปทำให้พิมพ์สิ่งนั้นในคอนโซลได้เช่นกัน และผู้บริหารก็แก้ไขปัญหาของฉันได้ ขอบคุณ. - person user3648026; 19.05.2014