Почему основной поток ожидает после запуска первого потока?

Я пытался понять использование CountDownLatch, вот код, который я использую здесь,

DecrementRunnable.java

    package com.nirjhar.java.countdownlatchexample;

    import java.util.concurrent.CountDownLatch;

    public class DecrementRunnable implements Runnable {

    private String name;
    private CountDownLatch cdl;

    public DecrementRunnable(String name, CountDownLatch cdl) {
    this.name=name;
    this.cdl=cdl;
    }

    @Override
    public void run() {
        System.out.println("in run method");
    for (int i = 0; i < 6; i++) {
        cdl.countDown();
        System.out.println("counting down "+cdl.getCount());
    }
    //cdl.notifyAll();
    System.out.println("Notified to all");
    }
}

CountDownDemoThread.java

package com.nirjhar.java.countdownlatchexample;

import java.util.concurrent.CountDownLatch;

public class CountDownDemoThread extends Thread {

    private CountDownLatch cdl;

    public CountDownDemoThread(String name, CountDownLatch cdl) {
        this.cdl=cdl;
        setName(name);
    }

    @Override
    public synchronized void start() {

        System.out.println("Task completed ... waiting for other thread to complete their task");
        try {
            cdl.await();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }   
        System.out.println("Continuing my tasks ..");
    }
}

Основная программа,

package com.nirjhar.java.countdownlatchexample;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    public static void main(String[] args)
    {
        CountDownLatch cdl=new CountDownLatch(6);
        Thread tDecrementThread=new Thread(new DecrementRunnable("Runnable", cdl));
        CountDownDemoThread cddt=new CountDownDemoThread("thread", cdl);        
        cddt.start();   
        System.out.println("thread 1 started");     
        tDecrementThread.start();
    }
}

В этой основной программе я ожидал, что эта строка "поток 1 запущен" должен быть напечатан после запуска потока, но здесь основной поток заблокирован из-за оператора ожидания в операторе cdl.await() в потоке cddt. Просто хотел узнать, в чем причина этого?

2 ответа

В вашем CountDownThread класс, я думаю, вы хотели переопределить run() метод вместо start

Вместо

public синхронизированный void start() {

попробуй с

public void run() {


Причина, почему

оператор ожидания в операторе cdl.await() в потоке cddt. Просто хотел узнать, в чем причина этого?

В этом случае вы переопределили start метод Thread и поэтому он больше не создает поток для выполнения кода, как обычно.

Так что нет никакой нити, которая порождается и влияет на main нить вызывает cd1.await() вместо cd1 нить (что ты хотел). И именно поэтому main Тема заблокирована.

Я думаю, что вы должны пройти через концепцию синхронизации потоков и приоритетов потоков.

Приоритет 1.Thread:

Каждый поток в java имеет некоторый приоритет, это может быть приоритет по умолчанию, генерируемый jvm, или настраиваемый приоритет, предоставляемый программистом. Допустимый диапазон приоритета потока - от 1 до 10, где 1 - минимальный приоритет, а 10 - максимальный приоритет. Класс потока определяет следующие константы для представления некоторых приоритеты стандартов:

1.Thread.MIN_PRIORITY --------------- 1

2.Thread.NORM_PRIORITY ------------ 5

3.Thread.MAX_PRIORITY ------------- 10

Планировщик потока будет использовать приоритет при выделении процессору, поток, имеющий наивысший приоритет, получит шанс первым. Если два потока имеют одинаковый приоритет, мы не можем ожидать точного порядка выполнения, это зависит от планировщика потока

Класс потока определяет следующий метод для получения и установки приоритета потока:

1.public final int getPriority ()

2. Public Public Void SetPriority(int p)

Примечание: допустимые значения варьируются от 1 до 10, в противном случае мы получим исключение времени выполнения IllegalArgumentException

Пример:t.setPriority(7): действительный

t.setPriority (17): IllegalArgumentException

---------------------------------------- Приоритет по умолчанию -------- ------------------------------

Приоритет по умолчанию только для основного потока равен 5, но для всех остальных потоков приоритет по умолчанию будет наследоваться от родительского к дочернему, то есть любой приоритет родительского потока с таким же приоритетом будет для дочернего потока.

-------------------------- Индивидуальный приоритет ---------------------- ---------------------------

   class Mythread extends Thread
      {
        public void run()
        {
          for(int i=0;i<=5;i++)
          {
           System.out.println("child thread........");
           }
       }

     }
  public class ThreadDemo
   {
  public static void main(String arg[])
  {
      Mythread t=new Mythread();
     //   t.setPriority(10);----------(1)
      t.start();

       for(int i=0;i<=5;i++)
        {
           System.out.println("main thread........");
        }

     }
  }

Если мы комментируем первую строку, то и основной, и дочерний потоки имеют одинаковый приоритет 5, и, следовательно, мы не можем ожидать порядок выполнения и точный вывод. Если не комментируем строку 1, то главный поток имеет приоритет 5, а дочерний поток имеет приоритет, следовательно, дочерний поток сначала получит шанс, а затем основной поток, в этом случае вывод:

    Child thread……….

    Child thread……….

    Child thread……….

    Child thread……….

    Child thread……….

    Main thread……….

    Main thread……….

    Main thread……….

    Main thread……….

    Main thread……….

   [NOTE: some platforms won’t provide proper support for thread priority]

2.Synchronization:

Synchronized - это модификатор, применимый только для метода и блоков, но не для классов и переменных.

Если несколько потоков пытаются одновременно работать с одним и тем же java-объектом, то может возникнуть проблема несовместимости данных.

Чтобы преодолеть эту проблему, мы должны использовать ключевое слово synchronized, если метод или блок объявлен как синхронизированный, тогда только один поток может одновременно выполнять этот метод или блок для данного объекта, так что проблема несогласованности данных будет решена.

Основным преимуществом синхронизированного ключевого слова является то, что мы можем решить проблемы несоответствия данных, но главный недостаток синхронизированного ключевого слова заключается в том, что оно увеличивает время ожидания потоков и создает проблему производительности, поэтому, если нет особых требований, то не рекомендуется использовать синхронизированное ключевое слово.

Концепция внутренней синхронизации реализована с помощью блокировки, каждый объект в Java имеет уникальную блокировку.

Всякий раз, когда мы используем синхронизированное ключевое слово, в концепцию входит только концепция блокировки.

Если поток хочет сначала выполнить синхронизированный метод для данного объекта, он должен получить блокировку этого объекта.

как только поток получил блокировку, ему разрешается выполнять любой синхронизированный метод для этого объекта.

Как только выполнение метода завершается автоматически, поток снимает блокировку. Приобретение и снятие блокировки внутренне выполняется jvm и программистом, не ответственным за это действие.

В то время как поток, выполняющий синхронизированный метод для данного объекта, оставшимся потокам не разрешается выполнять какой-либо синхронизированный метод одновременно для одного и того же объекта, но оставшимся потокам разрешается выполнять несинхронизированные методы одновременно

Пример:

Class X
{
  synchronized void m1() {}
  synchronized void m2() {}
   public void m3(){}


  }

В приведенном выше примере предположим, что поток T1 начинает выполнение m1(), поскольку блокировка доступа T1 сначала выполняется из jvm, если одновременно поток T2 начал выполнять метод m1(), а поток T3 стал выполнять метод m2(), тогда оба потока T2 и T3 будут в состоянии ожидания, пока поток T1 не освободит блокировку.

если поток T4 пришел выполнить метод m3(), он будет выполнять метод m3() напрямую, потому что это не синхронизированный метод. Концепция блокировки реализована на основе объекта, но не на основе метода

ПРИМЕЧАНИЕ: помните, что для каждого объекта есть две области:

1. Синхронизированная зона.

2. Несинхронизированная зона.

 Class X 

{

   Synchronized();

{

   Where ever we are performing update operation           
  ( Add/remove/delete/replace).

   That is  Where state of object changing.

}

 Non-synchronized()

{

  Where ever state of object won’t be changed, like only read    
 operation is performed.


  }



 }

Пример: Программный подход:

  class Display{

    public synchronized void wish(String name){

        for(int i=1;i<=5;i++){
           System.out.print("Good Morning:");

            try{
                 Thread.sleep(2000);
              }

            catch(InterruptedException e){
                 System.out.println("i got Interrupted");

              }

                  System.out.println(name);
          }

      }

}


  class ThreadDemo extends Thread
   {

     Display d;
     String name;
  public ThreadDemo(Display d,String name){

           this.d=d;
           this.name=name;

        }
  public void run(){
         d.wish(name);
      }


}


public class SynchronizedDemo

{

    public static void main(String arg[]){

         Display d=new Display();
         ThreadDemo t1=new ThreadDemo(d,"Avadhoot");
         ThreadDemo t2=new ThreadDemo(d,"Abhishek");
         ThreadDemo t3=new ThreadDemo(d,"Rinkesh");
         ThreadDemo t4=new ThreadDemo(d,"Kushal");
         t1.start();
         t2.start();
         t3.start();
         t4.start();
    }


}

[ПРИМЕЧАНИЕ: Если мы не объявляем метод wish (имя строки) как синхронизированный, тогда весь поток будет выполняться одновременно, и, следовательно, мы получим неправильный вывод.

Если мы объявим метод wish(String name) как синхронизированный, то одновременно только одному потоку будет разрешено выполнять метод wish(String name) для данного объекта Display(class), поэтому мы получим регулярный вывод.]

ПРИМЕР:

 public class SynchronizedDemo

 {

     public static void main(String arg[])

     {        
            Display d1=new Display();
            Display d2=new Display();
            Display d3=new Display();
            Display d4=new Display();
         ThreadDemo t1=new ThreadDemo(d1,"Avadhoot");
         ThreadDemo t2=new ThreadDemo(d2,"Abhishek");
         ThreadDemo t3=new ThreadDemo(d3,"Rinkesh");
         ThreadDemo t4=new ThreadDemo(d4,"Kushal");
         t1.start();
         t2.start();
         t3.start();
         t4.start();

     }

  }

Несмотря на то, что метод wish(String name) синхронизирован, мы получим неправильный вывод, потому что потоки работают с различными объектами Java.

Заключение:

Если несколько потоков работают на одном и том же Java-объекте, то требуется синхронизация.

Если несколько потоков работают с несколькими объектами Java, синхронизация не требуется.


* Блокировка уровня класса:

Каждый класс в Java имеет уникальную блокировку, которая является ничем иным, как блокировкой на уровне класса.

Если поток хочет выполнить статический синхронизированный метод, то поток требует блокировки на уровне класса.

Как только поток получил блокировку уровня класса, ему разрешается выполнять любой статический синхронизированный метод этого класса.

Как только выполнение метода завершается автоматически, поток снимает блокировку. В то время как поток, выполняющий статический синхронизированный метод, оставшимся потокам не разрешается одновременно выполнять какой-либо статический синхронизированный метод этого класса, но остальным потокам разрешается выполнять следующие методы одновременно:

  1. Нормальный статический метод.

  2. Синхронизированный метод экземпляра.

  3. Нормальный метод экземпляра.

Например:

   Class X
    {
     static synchronized m1(){}
     static synchronized m2(){}
     static m3(){}
     synchronized m4(){}
    m5(){}

   }
Другие вопросы по тегам