سيرفلتات جافا: الكائنات، الجلسات، وتعدد الخيوط — تحليل شامل

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

لماذا لا تزال السيرفلتات مخيفة، وكيف تتعايش مع ذلك؟

\n\n

إذا سبق لك أن ألقيت نظرة على Stack Overflow بحثًا عن إجابة لسؤال "كيف تعمل السيرفلتات؟"، فمن المحتمل أنك صادفت الموضوع الأسطوري الذي حظي بآلاف المشاهدات. السؤال الذي يُطرح مرارًا وتكرارًا: "كم عدد نسخ السيرفلت التي يتم إنشاؤها؟ كيف أشارك البيانات بين الطلبات؟ ولماذا يتصرف متغيري وكأنه مجنون؟"

\n\n

لنكن صادقين: السيرفلتات ليست علم الصواريخ، لكنها تحتوي على الكثير من المزالق. خاصة عندما يتعلق الأمر بـ تعدد الخيوط والمتغيرات المشتركة. في هذه المقالة، لن نعيد سرد الوثائق فقط. سنأخذ مثالًا حقيقيًا من الحياة، ونحلله بالتفصيل، ونوضح كيف تتجنب الوقوع في المشاكل. هيا بنا!

\n\n

كيف يدخل السيرفلت إلى الذاكرة: إنشاء الكائن ودورة الحياة

\n\n

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

\n\n

إليك كيف تبدو دورة الحياة النموذجية:

\n\n
    \n
  • init() — تُستدعى مرة واحدة عند تحميل السيرفلت. هنا تقوم بتهيئة اتصالات قاعدة البيانات، الإعدادات، وغيرها.
  • \n
  • service() — تُستدعى لكل طلب وارد. هنا تتم المعالجة.
  • \n
  • destroy() — تُستدعى قبل إلغاء تحميل السيرفلت. تقوم بتنظيف الموارد.
  • \n
\n\n

النقطة الأساسية: service() (ومشتقاته doGet، doPost) يتم تنفيذها في خيط منفصل لكل طلب. أي أن نفس نسخة السيرفلت تعالج مئات الطلبات في وقت واحد. وهنا يبدأ الجزء المثير للاهتمام.

\n\n

مثال: سيرفلت واحد، خيوط متعددة

\n\n
@WebServlet("/counter")\npublic class CounterServlet extends HttpServlet {\n    private int counter = 0;  // متغير مشترك!\n\n    @Override\n    protected void doGet(HttpServletRequest req, HttpServletResponse resp) \n            throws ServletException, IOException {\n        counter++;\n        resp.getWriter().write("Counter: " + counter);\n    }\n}
\n\n

إذا قمت بتشغيل هذا الكود وأرسلت 10 طلبات متوازية، لن تحصل على الأرقام من 1 إلى 10 بالترتيب. بدلاً من ذلك، سترى شيئًا مثل: 2، 5، 3، 8، 1... أو قد يفوت counter بعض القيم تمامًا. لماذا؟ لأن counter++ ليست عملية ذرية. يمكن لخيطين قراءة نفس القيمة، زيادتها، وكتابتها مرة أخرى، مما يؤدي إلى فقدان زيادة واحدة.

\n\n

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

\n\n

الجلسات: كيف لا تخلط بين المستخدمين

\n\n

الآن تخيل: تريد تخزين بيانات فريدة لكل مستخدم (سلة التسوق، تسجيل الدخول، الإعدادات). لهذا الغرض، يوجد في Java Servlets HttpSession. يتم إنشاء جلسة لكل مستخدم جديد وتعيش حتى يغلق المتصفح أو تنتهي المهلة.

\n\n

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

\n\n

مثال: العمل مع الجلسة

\n\n
@WebServlet("/session-demo")\npublic class SessionServlet extends HttpServlet {\n    @Override\n    protected void doGet(HttpServletRequest req, HttpServletResponse resp) \n            throws ServletException, IOException {\n        HttpSession session = req.getSession();\n        Integer visitCount = (Integer) session.getAttribute("visitCount");\n        if (visitCount == null) {\n            visitCount = 0;\n                

المدونات

توصيات الكتب