البرمجة

تنفيذ مشكلة المنتج والمستهلك باستخدام خيوط في جافا

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

أولاً وقبل كل شيء، يجب عليك تعريف المصفوفة كمتغير عام (global variable)، بحيث يمكن لكل من المنتجين والمستهلكين الوصول إليها. يمكنك تحقيق ذلك عبر تعريف المصفوفة خارج الكلاس الرئيسي وعلامة static للتأكيد على أنها مشتركة بين جميع الكائنات. على سبيل المثال:

java
public class Main { // المصفوفة المشتركة بين المنتجين والمستهلكين static int[] buffer; public static void main(String[] args) { // ... رمز لتحديد قيم m و n و k // تهيئة المصفوفة بحجم k buffer = new int[k]; // ... إنشاء وتشغيل المنتجين والمستهلكين } }

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

java
import java.util.Random; class Producer implements Runnable { private int id; private Random random = new Random(); public Producer(int id) { this.id = id; } @Override public void run() { // ... رمز لتكوين المنتج try { while (true) { // ... رمز لإضافة عنصر إلى المصفوفة int value = random.nextInt(10); putInBuffer(value); System.out.println("Producer #" + id + " put: " + value); // ... رمز للنوم لفترة عشوائية Thread.sleep(random.nextInt(1000)); } } catch (InterruptedException e) { e.printStackTrace(); } } private synchronized void putInBuffer(int value) throws InterruptedException { // ... رمز لإضافة عنصر إلى المصفوفة بشكل آمن } } class Consumer implements Runnable { private int id; private Random random = new Random(); public Consumer(int id) { this.id = id; } @Override public void run() { // ... رمز لتكوين المستهلك try { while (true) { // ... رمز لاستخراج عنصر من المصفوفة int value = getFromBuffer(); System.out.println("Consumer #" + id + " got: " + value); // ... رمز للنوم لفترة عشوائية Thread.sleep(random.nextInt(1000)); } } catch (InterruptedException e) { e.printStackTrace(); } } private synchronized int getFromBuffer() throws InterruptedException { // ... رمز لاستخراج عنصر من المصفوفة بشكل آمن } }

يرجى ملاحظة أنه يجب عليك تحديد الكود المتعلق بإضافة واستخراج العناصر من المصفوفة بشكل آمن باستخدام المزامنة (synchronization) لتجنب مشاكل التنافس. كما يجب عليك تكملة الرموز المشار إليها بتناسق مع احتياجات برنامجك.

المزيد من المعلومات

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

للقيام بذلك، يُستخدم الكلمة الرئيسية synchronized لتزامن الوصول إلى المصفوفة في الكلاسين Producer و Consumer. تمثل كل عملية إضافة (putInBuffer) واستخراج (getFromBuffer) عملية حساسة للتزامن، مما يضمن أنها لن تتعرض للتنافس عند الوصول إلى المصفوفة المشتركة.

علاوة على ذلك، يمكنك استخدام طابور (Queue) لتبسيط التعامل مع المصفوفة وتجنب الحاجة إلى تنفيذ خطط خاصة للإضافة والاستهلاك. في هذا السياق، يمكنك استخدام LinkedList كهيكل للطابور.

هنا مثال يُظهر كيفية تحسين الكود باستخدام الطابور:

java
import java.util.LinkedList; import java.util.Queue; import java.util.Random; class Producer implements Runnable { private int id; private Random random = new Random(); private Queue buffer; private int maxSize; public Producer(int id, Queue buffer, int maxSize) { this.id = id; this.buffer = buffer; this.maxSize = maxSize; } @Override public void run() { try { while (true) { int value = random.nextInt(10); putInBuffer(value); System.out.println("Producer #" + id + " put: " + value); Thread.sleep(random.nextInt(1000)); } } catch (InterruptedException e) { e.printStackTrace(); } } private synchronized void putInBuffer(int value) throws InterruptedException { while (buffer.size() == maxSize) { wait(); // انتظار حتى يتاح المزيد من المساحة في المصفوفة } buffer.offer(value); notifyAll(); // إعلام المستهلكين بأن هناك عنصرًا جديدًا في المصفوفة } } class Consumer implements Runnable { private int id; private Random random = new Random(); private Queue buffer; public Consumer(int id, Queue buffer) { this.id = id; this.buffer = buffer; } @Override public void run() { try { while (true) { int value = getFromBuffer(); System.out.println("Consumer #" + id + " got: " + value); Thread.sleep(random.nextInt(1000)); } } catch (InterruptedException e) { e.printStackTrace(); } } private synchronized int getFromBuffer() throws InterruptedException { while (buffer.isEmpty()) { wait(); // انتظار حتى يتوفر عنصر في المصفوفة } int value = buffer.poll(); notifyAll(); // إعلام المنتجين بأن هناك مساحة في المصفوفة return value; } } public class Main { public static void main(String[] args) { int m = 2; // عدد المنتجين int n = 3; // عدد المستهلكين int k = 5; // حجم المصفوفة Queue buffer = new LinkedList<>(); // المصفوفة المشتركة // إنشاء وتشغيل المنتجين والمستهلكين for (int i = 1; i <= m; i++) { new Thread(new Producer(i, buffer, k)).start(); } for (int i = 1; i <= n; i++) { new Thread(new Consumer(i, buffer)).start(); } } }

هذا الكود يُظهر استخدام الطابور (Queue) لتبسيط تنظيم المصفوفة، ويستخدم wait و notifyAll للتحكم في الوصول المتزامن للمصفوفة. يتيح هذا الحلا للمنتجين والمستهلكين التفاعل بشكل آمن مع المصفوفة المشتركة.

مقالات ذات صلة

زر الذهاب إلى الأعلى