كيفية إجبار مؤشر ترابط Java لإغلاق اتصال قاعدة بيانات موضوع محلي

StackOverflow https://stackoverflow.com/questions/1700212

سؤال

عند استخدام اتصال قاعدة بيانات مؤشر الترابط، مطلوب إغلاق الاتصال عند وجود الخيط.

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

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

نظرت إلى مصادر Java 1.5 ووجدت أن خيط الخيط المحلي يتم تعيينه إلى NULL، مما سيؤدي في النهاية إلى استدعاء مجموعة القمامة وضع اللمسات الأخيرة (), لكنني لا أريد الاعتماد على جامع القمامة.

يبدو التجاوز التالي أمر لا مفر منه للتأكد من إغلاق اتصال قاعدة البيانات:

@Override 
public void remove() {
    get().release(); 
    super.remove(); 
}

أين إفراج() يغلق اتصال قاعدة البيانات، إذا تم فتحه. لكننا لا نعرف ما إذا كان الموضوع قد استخدم هذا الموضوع محليا. إذا كان الحصول على () لم يتم استدعاؤه من قبل هذا الموضوع، فهناك مضيعة تماما هنا: threadlocal.initialvalue () سيتم استدعاء، سيتم إنشاء خريطة على هذا الموضوع، إلخ.

-

مزيد من التوضيح والمثال وفقا لتعليق Thorbjørn:

java.lang.threadlocal. هو نوع من المصنع لكائن مرتبط بموضوع. يحتوي هذا النوع على Getter for The الكائن وطريقة المصنع (عادة ما يكون من قبل المستخدم). عندما يسمى Getter يدعو إلى استدعاء طريقة المصنع فقط إذا لم يتم استدعاؤها من قبل هذا الموضوع.

استخدام threadlocal. يسمح للمطور بربط مورد إلى مؤشر ترابط حتى إذا كتب رمز الخيط من قبل طرف ثالث.

مثال: قل لدينا نوع من الموارد يسمى نوعي ونريد أن يكون واحدا واحدا فقط منه لكل موضوع.

حدد في الفئة الواجبة: STATCLOCAL SHOTALACTORNORSTORNORSTORTORY = New ThreadLocal () {override المحمي MyType InitialValue () {RETURN NEW MYTYPE ()؛ }}

استخدم في السياق المحلي في هذه الفئة: Public Void SomeMethod () {MyType Resource = TellingFactory.get ()؛ Resource.useresource ()؛ }

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

مثال الاستخدام الكلاسيكي هو متى نوعي هو بعض النصوص / التاريخ / التاريخ غير آمن للخيط.

ولكن عادة ما لا تحتاج هذه الأنفيات إلى إصدارها أو إغلاقها، فإن اتصالات قاعدة البيانات تفعل وأنا أستخدم java.lang.threadlocal. للحصول على اتصال قاعدة بيانات واحد لكل موضوع.

الطريقةالتي اراه بها، java.lang.threadlocal. هو الكمال تقريبا لذلك. تقريبا لأنه لا توجد طريقة لضمان إغلاق المورد إذا كان موضوع الاتصال ينتمي إلى تطبيق طرف ثالث.

أحتاج إلى العقول الخاصة بك squires: عن طريق التمديد java.lang.threadlocal. تمكنت من ربط اتصال قاعدة بيانات واحدة لكل مؤشر ترابط، لأن استخدامه الحصري - بما في ذلك الخيوط التي لا أستطيع تعديلها أو تجاوزها. تمكنت من التأكد من إغلاق الاتصالات في حالة وفاة مؤشر الترابط على استثناء غير مصراعي.

في حالة خروج الخيط العادي، يغلق جامع القمامة الاتصال (لأن نوعي يتجاوز وضع اللمسات الأخيرة ()). في الحقيقة الفعلية يحدث بسرعة كبيرة، ولكن هذا ليس مثاليا.

إذا كان لدي طريقي، كان هناك طريقة أخرى على java.lang.threadlocal.:

protected void release() throws Throwable {}

إذا كانت هذه الطريقة موجودة على java.lang.threadlocal., ، تم استدعاؤه بواسطة JVM عند أي خروج الموضوع / الموت، ثم في التجاوز الخاص بي، يمكنني إغلاق اتصالي (وسيأتي المخلص إلى صهيون).

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

شكرا

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

المحلول

إذا كنت من التصرف الحساس، فابحث الآن.

لن أتوقع هذا على نطاق واسع؛ يضاعف فعليا عدد المواضيع في النظام. قد يكون هناك بعض حالات الاستخدام حيث يكون مقبولا.

public class Estragon {
  public static class Vladimir {
    Vladimir() { System.out.println("Open"); }
    public void close() { System.out.println("Close");}
  }

  private static ThreadLocal<Vladimir> HOLDER = new ThreadLocal<Vladimir>() {
    @Override protected Vladimir initialValue() {
      return createResource();
    }
  };

  private static Vladimir createResource() {
    final Vladimir resource = new Vladimir();
    final Thread godot = Thread.currentThread();
    new Thread() {
      @Override public void run() {
        try {
          godot.join();
        } catch (InterruptedException e) {
          // thread dying; ignore
        } finally {
          resource.close();
        }
      }
    }.start();
    return resource;
  }

  public static Vladimir getResource() {
    return HOLDER.get();
  }
}

أفضل معالجة خطأ وهلم جرا يتم تركه كممارحة للمنفذ.

يمكنك أيضا إلقاء نظرة على تتبع المواضيع / الموارد في ConcurrentHashMap مع اقتراع موضوع آخر حي. وبعد لكن هذا الحل هو آخر منتجع من الكائنات اليائسة ربما ينتهي الأمر بالتحقق من ذلك في كثير من الأحيان أو نادرا ما يكون.

لا أستطيع التفكير في أي شيء آخر لا ينطوي على الأجهزة. قد تعمل AOP.

سيكون تجمع الاتصال هو خياري المفضل.

نصائح أخرى

لف runnable الخاص بك مع روعة جديدة مع

try {
  wrappedRunnable.run();
} finally {
  doMandatoryStuff();
}

البناء، ودع ذلك ينفذ بدلا من ذلك.

يمكنك حتى أن تصنعها في طريقة، مثل السابقين:

  Runnable closingRunnable(Runnable wrappedRunnable) {
    return new Runnable() {
      @Override
      public void run() {
        try {
          wrappedRunnable.run();
        } finally {
          doMandatoryStuff();
        }
      }
    };
  }

واتصل بهذه الطريقة تمر في runnable كنت تبحث عنها.

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

إذا كنت تستخدم تطبيق تنفيذي، فيمكنك استخدامه مثل executor.submit(closingRunnable(normalRunnable))

إذا كنت تعرف أنه سيتم إيقاف تشغيل ExecutorService بالكامل وأريد الاتصالات بإغلاقها عند هذه النقطة، فيمكنك تعيين مصنع مؤشر ترابط يقوم أيضا بإغلاق "بعد الانتهاء من جميع المهام وإغلاق التنفيذ"، مثال:

  ExecutorService autoClosingThreadPool(int numThreads) {
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(numThreads, numThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); // same as Executors.newFixedThreadPool
    threadPool.setThreadFactory(new ThreadFactory() {
      @Override
      public Thread newThread(Runnable r) {
        return new Thread(closingRunnable(r)); // closes it when executor is shutdown
      }
    });
    return threadPool;
  }

فيما يتعلق بما إذا كان يمكن أن يعرف DomanStatorySuff أم لا، فقد تم فتح الاتصال أم لا من قبل أم لا، في السابق، هناك شيء واحد يتبادر إلى الذهن هو وجود ThreadLocal الثاني الذي يتتبع فقط إذا تم فتحه أم لا (EX: عند فتح الاتصال، احصل عليه ثم قم بتعيين AtomicInteger على 2، عند وقت التنظيف، تحقق لمعرفة ما إذا كان لا يزال في الافتراضي، على سبيل المثال، 1 ...)

ممارسة JDBC العادية هي أنك تغلق ConnectionStatement و ResultSet) في كتلة الطريقة نفسها كما حصلت عليه.

في الرمز:

Connection connection = null;

try {
    connection = getConnectionSomehow();
    // Do stuff.
} finally {
    if (connection != null) {
        try {
            connection.close();
        } catch (SQLException e) {
            ignoreOrLogItButDontThrowIt(e);
        }
    }
}

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

إذا كان نيتك الأصلي هو تحسين أداء الاتصال، فأنت بحاجة إلى إلقاء نظرة عليه تجمع الاتصال. وبعد يمكنك استخدامها على سبيل المثال C3P0. API لهذا. أو إذا كان webapplication، استخدم مرافق تجمع الاتصالات المدمجة في AppServer في نكهة DataSource. وبعد استشر وثائق AppServer الخاصة بالتفاصيل.

لا أفهم حقا لماذا لا تستخدم تجمع الاتصال التقليدي. لكن يجب أن أفترض أن لديك أسبابك.

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

بعد ذلك مقدار التحكم الذي لديك إما على إنشاء متغير ThreadLocal أو إنشاء مؤشرات الترابط؟ أظن أن لديك سيطرة كاملة على ThreadLocal لكنها تقتصر على عدم إبداء خلاصة المواضيع؟

هل يمكنك استخدام البرمجة الموجهة نحو جانب الجسيم لمراقبة Runnable أو المواضيع الجديدة التي توسيع طريقة التشغيل () لتضمين التنظيف؟ ستحتاج أيضا إلى تمديد ThreadLocal بحيث يمكن تسجيل نفسه.

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

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

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

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

تجاوز طريقة GET () في TREAEEDLOCAL بحيث يضبط خاصية قائمة على الفئة الفرعية. يمكن بسهولة الاستعلام عن هذه الخاصية لتحديد ما إذا كانت طريقة GET () قد تم استدعاؤها لمؤشر ترابط معين. يمكنك بعد ذلك الوصول إلى ThreadLocal لتنظيفه في هذه الحالة.

تحديث استجابة للتعليق

ما فعلناه كان

@Override
public void run() {
  try {
    // ...
  } finally {
    resource.close();
  }
}

أساسا فقط (ربما فتح ثم) أغلقه لجميع المسارات من خلال الخيط. في حالة تساعد أي شخص هناك :)

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

على سبيل المثال، إذا قمت بإنشاء المنفذ عن طريق تمديد ThreadPoolExecutor، فيمكنك تجاوز الطرق الواجب () والطرق المنتهية () لإنجازها. السابق لخيط الخروج بشكل غير طبيعي بينما الأخير عادة.

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