سؤال

قدم زميله في الفريق المطالبة التالية:

"Thread.interrupt() مكسورة بطبيعته، وينبغي أن تستخدم (تقريبا) أبدا ".

أحاول أن أفهم لماذا هذا هو الحال.

هل هي أفضل ممارسة معروفة أبدا للاستخدام Thread.interrupt()ب هل يمكنك تقديم أدلة لماذا يتم كسرها / عربات التي تجرها الدواب، ولا ينبغي استخدامها لكتابة كود قوي متعدد الاستخدامات؟

ملحوظة - أنا غير مهتم بهذا السؤال إذا كانت "جميلة" من حافظة تصميم. سؤالي هو - هل هو عربات التي تجرها الدواب؟

هل كانت مفيدة؟

المحلول

نسخة مختصرة:

هل هي أفضل ممارسات معروفة أبدا لاستخدام الخيط.

لا.

هل يمكنك تقديم أدلة لماذا يتم كسرها / Buggie، ويجب عدم استخدامها لكتابة كود قوي متعدد الاستخدامات؟

العكس هو الصحيح: إنه أمر بالغ الأهمية للحصول على رمز متعدد المراحل.

انظر القائمة 7.7 في جافا تزامن في الممارسة مثال.

نسخة أطول:

هنا، نستخدم هذه الطريقة في مكان واحد محدد: المناولة InterruptedExceptions. وبعد قد يبدو ذلك غريبا بعض الشيء ولكن إليك ما يبدو عليه في الكود:

try {
    // Some code that might throw an InterruptedException.  
    // Using sleep as an example
    Thread.sleep(10000);
} catch (InterruptedException ie) {
    System.err.println("Interrupted in our long run.  Stopping.");
    Thread.currentThread().interrupt();
}

هذا يفعل شيئين بالنسبة لنا:

  1. يتجنب تناول استثناء المقاطعة. توفر لك معالجات الاستثناء التلقائي IDE دائما شيئا مثل ie.printStackTrace(); و Jaunty "ToDo: شيء مفيد يحتاج إلى الذهاب هنا!" تعليق.
  2. إنه يستعيد حالة المقاطعة دون إجبار استثناء تم التحقق منه على هذه الطريقة. إذا كان توقيع الطريقة التي تنفذها لا تملك throws InterruptedException جملة، هذا هو خيارك الآخر لنشر الوضع الذي توقف.

اقترح المعلق أنه يجب أن أستخدم استثناء غير محدد "لإجبار الخيط للموت." هذا يفترض أن لدي معرفة مسبقة أن قتل الخيط مفاجئ هو الشيء الصحيح الذي يجب القيام به. أنا لا.

اقتبس Brian Goetz من JCIP على الصفحة قبل الإدراج المذكور أعلاه:

لا ينبغي أن تتحمل المهمة أي شيء عن سياسة عدم الانقطاع لمؤشر ترابطها المنفذ إلا إذا كان مصمم بشكل صريح لتشغيله داخل الخدمة التي لديها سياسة قانونية محددة.

على سبيل المثال، تخيل أنني فعلت هذا:

} catch (InterruptedException ie) {
    System.err.println("Interrupted in our long run.  Stopping.");
    // The following is very rude.
    throw new RuntimeException("I think the thread should die immediately", ie);
}

سأعلن أنه، بغض النظر عن التزامات أخرى لبقية مكدس المكالمات والولاية المرتبطة، يحتاج هذا الموضوع إلى الموت الآن. سأحاول التسلل بعد جميع كتل الصيد الأخرى وشمول تنظيف الدولة للحصول على وفاة مباشرة. الأسوأ من ذلك، كنت قد استهلكت حالة مقاطعة الخيط. سيضطر الآن منطق المنبع الآن إلى تفكيك استثناءي لمحاولة اللغز ما إذا كان هناك خطأ في المنطق البرنامجي أو ما إذا كنت أحاول إخفاء استثناء تم التحقق منه داخل غلاف غامض.

على سبيل المثال، إليك ما يفعله أي شخص آخر في الفريق على الفور:

try {
    callBobsCode();
} catch (RuntimeException e) { // Because Bob is a jerk
    if (e.getCause() instanceOf InterruptedException) {
        // Man, what is that guy's problem?
        interruptCleanlyAndPreserveState();
        // Restoring the interrupt status
        Thread.currentThread().interrupt();
    }
}

الدولة المتوقفة هي أكثر أهمية من أي محددة InterruptException. وبعد مثال محدد لماذا، انظر Javadoc الخيط. الثانية ():

إذا تم حظر هذا الموضوع في استدعاء الانتظار ()، أو الانتظار (طويل)، أو الانتظار (طويل، int) من فئة الكائن، أو الانضمام ()، الانضمام (طويل)، الانضمام (الطويل، الدولي) ، النوم (طويل)، أو النوم (طويل، int)، طرق هذه الفئة، ثم سيتم تطهير حالة المقاطعة وسوف تتلقى مقاطعة orpluptedException.

كما ترون، فإن أكثر من مقاطعة واحدة قد يتم إنشاؤها ومعالجتها كطلبات المقاطعة تتم معالجتها ولكن فقط إذا تم الحفاظ على حالة المقاطعة هذه.

نصائح أخرى

الطريقة الوحيدة التي أدرك فيها Thread.interrupt() مكسور هو أنه لا يفعل في الواقع ما يبدو عليه كما قد يكون - لا يمكن إلا في الواقع قاطع الرمز الذي يستمع به.

ومع ذلك، يستخدم بشكل صحيح، يبدو لي مثل آلية مدمجة جيدة لإدارة المهام والإلغاء.

أوصي جافا تزامن في الممارسة لمزيد من القراءة على الاستخدام المناسب والآمن لذلك.

المشكلة الرئيسية مع Thread.interrupt() هو أن معظم المبرمجين لا يعرفون عن المزالق المخفية واستخدامها بطريقة خاطئة. على سبيل المثال، عند التعامل مع المقاطعة، هناك طرق صافي العلم (لذلك تضيع الوضع).

أيضا، لن تكون المكالمة دائما مقاطعة الموضوع على الفور. على سبيل المثال، عندما تتوقف في بعض روتين النظام، لن يحدث شيء. في الواقع، إذا كان الخيط لا يتحقق من العلم ولا تستدعي أبدا طريقة Java التي يلقي InterruptException, ، ثم مقاطعة لن يكون لها أي تأثير على الإطلاق.

لا، انها ليست عربات التي تجرها الدواب. في الواقع هو أساس كيفية إيقاف المواضيع في جافا. يتم استخدامه في إطار المنفذ من java.util.concurrent - انظر تنفيذ java.util.concurrent.FutureTask.Sync.innerCancel.

أما بالنسبة للفشل، فلن أراها أبدا، وقد استخدمتها على نطاق واسع.

سبب واحد غير مذكورة هو أن إشارة المقاطعة يمكن أن تضيع مما يجعل استدعاء طريقة الخيط. لذلك ما لم يكن التعليمات البرمجية الخاصة بك في طريقة Thread.run () تدور في حين حلقة من نتائج استدعاء Lissing.Interrupt () غير مؤكد.

لقد لاحظت أنه عندما تكون في الخيط ONE أنا أعمل DriverManager.getConnection() عندما لا يكون هناك اتصال قاعدة بيانات المتاحة (يقول الخادم معطلا، فهذا أخيرا يلقي هذا الخط SQLException) ومن الخيط TWO أنا استدعاء صراحة ONE.interrupt(), ، ثم كلاهما ONE.interrupted() و ONE.isInterrupted() إرجاع false حتى لو وضعت كطريق أول في catch{} كتلة حيث SQLException تتم معالجة.

بالطبع عهدت بهذه القضية التي تنفذ الإشارة الإضافية، لكنها مزعجة للغاية، لأنها أول مشكلة من هذه القضايا في تطوير Java لمدة 15 عاما.

أفترض أنه بسبب الأخطاء في com.microsoft.sqlserver.jdbc.SQLServerDriver. وبعد وأنا حققت أكثر لتأكيد أن الدعوة إلى native تستهلك الدالة هذا الاستثناء في جميع الحالات التي يضربها الخاصة بها، ولكنها تحافظ عليها عندما نجحت.

توميك

ملاحظة وجدت مشكلة مماثلة.

أرفق PPS مثالا قصيرا جدا لما أراه أعلاه. يمكن العثور على الطبقة المسجلة في sqljdbc42.jar. وبعد لقد وجدت هذا الخطأ في الفصول الدراسية المدمج في 2015-08-20 ثم قمت بتحديث أحدث إصدار متوفر (من 2017-01-12) وعلة لا يزال موجودا.

import java.sql.*;

public class TEST implements Runnable{
    static{
        try{
//register the proper driver
           Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
        }
        catch(ClassNotFoundException e){
            System.err.println("Cannot load JDBC driver (not found in CLASSPATH).");
        }
    }

    public void run() {
        Thread.currentThread().interrupt();
        System.out.println(Thread.currentThread().isInterrupted());
//prints true
        try{
            Connection conn = DriverManager.getConnection("jdbc:sqlserver://xxxx\\xxxx;databaseName=xxxx;integratedSecurity=true");
        }
        catch (SQLException e){
            System.out.println(e.getMessage());
        }
        System.out.println(Thread.currentThread().isInterrupted());
//prints false
        System.exit(0);
    }

    public static void main(String[] args){
        (new Thread(new TEST())).start();
    }
}

إذا قمت بتمرير شيء غير صحيح تماما، كما "foo", ، إلى DriverManager.getConnection(), ، ستحصل على الرسالة "لم يتم العثور على برنامج تشغيل مناسب FOO"، وستظل النسخة المطبوعة الثانية صحيحة كما يتوقع المرء. ولكن إذا قمت بتمرير السلسلة المبنية بشكل صحيح، فقل، فقل الخادم الخاص بك إلى أسفل أو فقدت اتصال الشبكة الخاصة بك (يمكن أن يحدث عموما في بيئة الإنتاج)، سترى java.net مقبس مهلة الخطأ المطبوعة والخيط interrupted() تضيع الدولة.

المشكلة ليست أن التنفيذ ليس عربات التي تجرها الدواب ولكن، بل هو مؤشر ترابطك في حالة غير معروفة عندما ينقطع، وهذا يمكن أن يؤدي إلى سلوك لا يمكن التنبؤ به.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top