مقدمة في التزامن وتعدد الخيوط في Java
تتطلب التطبيقات الحديثة بشكل متزايد تنفيذ مهام متعددة في وقت واحد. سواء كان ذلك معالجة الطلبات على الخادم، أو تحميل الملفات، أو العمليات الحسابية المعقدة — لا يمكن الاستغناء عن آليات التزامن و تعدد الخيوط. في Java، تم تضمين هذه المفاهيم على مستوى اللغة والمكتبة القياسية، مما يجعلها واحدة من أفضل الأدوات لإنشاء تطبيقات عالية الأداء.
في هذه المقالة، سنشرح كل شيء من الأساسيات إلى الأدوات المتقدمة: كيفية إنشاء الخيوط، ومزامنة الوصول إلى البيانات، واستخدام الأدوات المساعدة الحديثة من حزمة java.util.concurrent. ستكون المادة مفيدة لكل من المبتدئين والمطورين الذين يرغبون في تنظيم معرفتهم.
ما هو الخيط (Thread) ولماذا هو مطلوب؟
الخيط هو عملية خفيفة يتم تنفيذها داخل التطبيق. كل برنامج Java لديه خيط واحد على الأقل (main). يسمح تعدد الخيوط بتنفيذ عدة خيوط في وقت واحد، وهو أمر مهم بشكل خاص على المعالجات متعددة النوى.
المزايا الرئيسية لتعدد الخيوط:
- زيادة الأداء من خلال التنفيذ المتوازي للمهام.
- تحسين استجابة واجهة المستخدم.
- الاستخدام الفعال لموارد النظام.
1. إنشاء الخيوط في Java: الطرق التقليدية
توجد طريقتان أساسيتان لإنشاء خيط في Java: الوراثة من فئة Thread وتنفيذ واجهة Runnable. دعنا ننظر في كليهما.
الوراثة من فئة Thread
أبسط طريقة هي إنشاء فئة فرعية من Thread وإعادة تعريف طريقة run().
public class MyThread extends Thread { @Override public void run() { System.out.println("الخيط " + getName() + " تم تشغيله."); }
public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // تشغيل الخيط }}هام: استدعاء start()، وليس run(). run() سينفذ الكود في الخيط الحالي، بينما start() ينشئ خيطًا جديدًا.
تنفيذ واجهة Runnable
النهج الأكثر مرونة هو تنفيذ Runnable وتمريره إلى مُنشئ Thread.
public class Task implements Runnable { @Override public void run() { System.out.println("المهمة تُنفذ في الخيط: " + Thread.currentThread().getName()); }
public static void main(String[] args) { Task task = new Task(); Thread thread = new Thread(task); thread.start(); }}هذه الطريقة مفضلة لأنها لا تتطلب الوراثة وتسمح للفئة بتوسيع فئات أخرى.
2. المزامنة والذرية: كيفية تجنب سباق البيانات
عندما تصل عدة خيوط في وقت واحد إلى بيانات مشتركة، ينشأ حالة سباق (race condition). لتجنب ذلك، توفر Java آليات المزامنة.
الكلمة المفتاحية synchronized
باستخدام synchronized يمكن قفل طريقة أو كتلة كود لخيط واحد.
public class Counter { private int count = 0;
public synchronized void increment() { count++; }
public int getCount() { return count; }}في هذا المثال، يمكن لخيط واحد فقط تنفيذ increment() في لحظة معينة. ومع ذلك، للعمليات البسيطة مثل الزيادة، من الأفضل استخدام الفئات الذرية.
الفئات الذرية (Atomic)
تحتوي حزمة java.util.concurrent.atomic على فئات توفر عمليات ذرية بدون أقفال.
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicCounter { private A