Информация об изменениях

Сообщение Re[3]: Тестовое задание ... от 15.06.2015 19:27

Изменено 15.06.2015 19:31 GreenTea

Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, GreenTea, Вы писали:


GT>>Если кто шарит в java, прошу поревьювить. Я сам с ручной многопоточностью редко работал, так что могут быть косяки..


EP>Представь ситуацию, когда в первой половине tasks у нас задачи от первого клиента, а во второй — от второго, и всего один рабочий поток. Он будет выполнять все задания по списку, то есть сначала все от первого клиента, хотя по заданию должен чередовать.


Согласен.. Поправил.

package net.sf.brunneng;


import java.util.*;

public class ThreadPool {

   private class ClientTask {
      final int clientId;
      final Runnable runnable;

      public ClientTask(int clientId, Runnable runnable) {
         this.clientId = clientId;
         this.runnable = runnable;
      }
   }

   private boolean closed;
   private final List<ClientTask> tasks = Collections.synchronizedList(new ArrayList<ClientTask>());

   private List<Thread> threads = new ArrayList<Thread>();
   private Set<Integer> executingClients = Collections.synchronizedSet(new HashSet<Integer>());

   public List<ClientTask> getTasks() {
      return tasks;
   }

   public ThreadPool(int threadsCount) {
      for (int i = 0; i < threadsCount; ++i) {
         Thread thread = createThread();
         threads.add(thread);
         thread.start();
      }
   }

   private ClientTask getNextClientTask() {
      ClientTask res = null;
      synchronized (tasks) {
         for (int i = 0; i < tasks.size(); i++) {
            ClientTask task = tasks.get(i);
            if (!executingClients.contains(task.clientId)) {
               res = task;
               executingClients.add(task.clientId);
               tasks.remove(i);
               break;
            }
         }
      }

      return res;
   }

   private Thread createThread() {
      return new Thread(new Runnable() {
         @Override
         public void run() {
            boolean finish = false;

            while (!finish && !closed) {
               try {
                  ClientTask task = getNextClientTask();

                  if (task == null) {
                     synchronized (tasks) {
                        tasks.wait();
                     }
                     continue;
                  }

                  task.runnable.run();
                  executingClients.remove(task.clientId);

               } catch (InterruptedException e) {
                  e.printStackTrace();
                  finish = true;
               }
            }
         }
      });
   }

   public void addTask(int clientId, Runnable task) {
      synchronized (tasks) {
         int nextIndex = findBestIndexToInsert(clientId);
         tasks.add(nextIndex, new ClientTask(clientId, task));

         if (!executingClients.contains(clientId)) {
            tasks.notify();
         }
      }
   }

   private int findBestIndexToInsert(int clientId) {
      int minIndex = 0;

      for (int i = 0; i < tasks.size(); ++i) {
         ClientTask task = tasks.get(i);
         if (task.clientId == clientId) {
            minIndex = i + 1;
         }
      }

      int bestIndex = minIndex;

      for (int i = minIndex; i < tasks.size(); ++i) {
         ClientTask task = tasks.get(i);
         if (clientId != task.clientId) {
            bestIndex = i + 1;
            break;
         }
      }

      return bestIndex;
   }

   public void close() {
      closed = true;

      synchronized (tasks) {
         tasks.notifyAll();
      }

      for (Thread thread : threads) {
         try {
            thread.join();
         } catch (InterruptedException ignored) {

         }
      }
   }
}


Идея метода findBestIndexToInsert такая: вначале ищем minIndex — индекс таски следующей за последней таской с заданным clientId. Так обеспечиваем что все таски от одного клиента идут подряд. Далее ищем первую таску, у которой клиент будет отличаться от clientId. Вставлять новую таску будем сразу за ней..
Re[3]: Тестовое задание ...
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, GreenTea, Вы писали:


GT>>Если кто шарит в java, прошу поревьювить. Я сам с ручной многопоточностью редко работал, так что могут быть косяки..


EP>Представь ситуацию, когда в первой половине tasks у нас задачи от первого клиента, а во второй — от второго, и всего один рабочий поток. Он будет выполнять все задания по списку, то есть сначала все от первого клиента, хотя по заданию должен чередовать.


Согласен.. Поправил.

package net.sf.brunneng;


import java.util.*;

public class ThreadPool {

   private class ClientTask {
      final int clientId;
      final Runnable runnable;

      public ClientTask(int clientId, Runnable runnable) {
         this.clientId = clientId;
         this.runnable = runnable;
      }
   }

   private boolean closed;
   private final List<ClientTask> tasks = Collections.synchronizedList(new ArrayList<ClientTask>());

   private List<Thread> threads = new ArrayList<Thread>();
   private Set<Integer> executingClients = Collections.synchronizedSet(new HashSet<Integer>());

   public List<ClientTask> getTasks() {
      return tasks;
   }

   public ThreadPool(int threadsCount) {
      for (int i = 0; i < threadsCount; ++i) {
         Thread thread = createThread();
         threads.add(thread);
         thread.start();
      }
   }

   private ClientTask getNextClientTask() {
      ClientTask res = null;
      synchronized (tasks) {
         for (int i = 0; i < tasks.size(); i++) {
            ClientTask task = tasks.get(i);
            if (!executingClients.contains(task.clientId)) {
               res = task;
               executingClients.add(task.clientId);
               tasks.remove(i);
               break;
            }
         }
      }

      return res;
   }

   private Thread createThread() {
      return new Thread(new Runnable() {
         @Override
         public void run() {
            boolean finish = false;

            while (!finish && !closed) {
               try {
                  ClientTask task = getNextClientTask();

                  if (task == null) {
                     synchronized (tasks) {
                        tasks.wait();
                     }
                     continue;
                  }

                  task.runnable.run();
                  executingClients.remove(task.clientId);

               } catch (InterruptedException e) {
                  e.printStackTrace();
                  finish = true;
               }
            }
         }
      });
   }

   public void addTask(int clientId, Runnable task) {
      synchronized (tasks) {
         int nextIndex = findBestIndexToInsert(clientId);
         tasks.add(nextIndex, new ClientTask(clientId, task));

         if (!executingClients.contains(clientId)) {
            tasks.notify();
         }
      }
   }

   private int findBestIndexToInsert(int clientId) {
      int minIndex = 0;

      for (int i = 0; i < tasks.size(); ++i) {
         ClientTask task = tasks.get(i);
         if (task.clientId == clientId) {
            minIndex = i + 1;
         }
      }

      return minIndex < tasks.size() ? minIndex + 1 : minIndex;
   }

   public void close() {
      closed = true;

      synchronized (tasks) {
         tasks.notifyAll();
      }

      for (Thread thread : threads) {
         try {
            thread.join();
         } catch (InterruptedException ignored) {

         }
      }
   }
}