В чем разница между Thread.join и Synchronized?

Я смущен, когда следует использовать Thread.join() и когда использовать synchronization в приложении с несколькими потоками.

По моим словам, они блокируют или ждут выполнения какого-либо другого потока.
Этот пример должен вывести 10 A, 10 B и 10 C в последовательном шаблоне один за другим, например:

1  : A
2  : A
3  : A
4  : A
5  : A
6  : A
7  : A
8  : A
9  : A
10 : A
1  : B
2  : B
3  : B
4  : B
5  : B
6  : B
7  : B
8  : B
9  : B
10 : B
1  : C
2  : C
3  : C
4  : C
5  : C
6  : C
7  : C
8  : C
9  : C
10 : C
----ProGraM ENDS----

Пример начинается здесь

class SyncTest extends Thread 
{   
    StringBuffer sb;

    public SyncTest(StringBuffer sb) 
    {
        this.sb = sb;   
    }

    public void run()
    {
        synchronized(sb) 
        {
            for(int i=1;i<=10;i++){
                System.out.println(i+" : "+sb.charAt(0));
            }
            sb.setCharAt(0, (char) (sb.charAt(0)+1));
        }
    }

    public static void main(String [] args) throws InterruptedException
    {
        StringBuffer sb = new StringBuffer("A");
        Thread t1=new SyncTest(sb);
        Thread t2=new SyncTest(sb);
        Thread t3=new SyncTest(sb);

        t1.start();

        t2.start();

        t3.start();

        Thread.sleep(1000);

        System.out.println("----ProGraM ENDS----");
    }
}

Здесь вывод получается 10 A, за которым следует 10 B, а затем 10 C в последовательном порядке. Но я могу использовать Thread.join вместо блока synchronized, чтобы получить тот же вывод, что и этот:

public void run()
    {
        //removed synchronized statement...

            for(int i=1;i<=10;i++){
                System.out.println(i+" : "+sb.charAt(0));
            }
            sb.setCharAt(0, (char) (sb.charAt(0)+1));

    }

    public static void main(String [] args) throws InterruptedException
    {
        StringBuffer sb = new StringBuffer("A");
        Thread t1=new SyncTest(sb);
        Thread t2=new SyncTest(sb);
        Thread t3=new SyncTest(sb);

        t1.start();
        t1.join();
        t2.start(); // wait for t1 to complete
        t2.join();
        t3.start(); // wait for t2 to complete
        t3.join(); 

                     // wait for t3 to complete
        System.out.println("----ProGraM ENDS----");
    }

Может ли кто-нибудь очистить мое замешательство в использовании этих двух методов, то есть когда использовать Thread.join и когда использовать synchronization в многопоточности на Java.

Ответ 1

Thread.join() ожидает полного завершения потока, тогда как блок synchronized может использоваться для предотвращения одновременного выполнения двумя потоками одного и того же фрагмента кода.

Трудно посоветовать, когда использовать один над другим в целом, поскольку они служат различным целям. Редко можно найти пример, например, ваш код, где разница между ними минимальна.

Как говорится, в вашем первом примере нет гарантии, что результат будет алфавитным. Вы не можете быть уверены, какой поток сначала попадет в блок synchronized. Поэтому в этом конкретном случае наиболее подходящим является join().

Ответ 2

thread.join() останавливает выполнение текущего потока до тех пор, пока объединенный поток не завершится.. Вы правильно комментировали..:)

Синхронизация предотвращает выполнение несколькими потоками синхронизированной части кода в одном экземпляре.

Ответ 3

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

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

enter image description here

Источник

Ответ 4

Без join() поток работает параллельно и зависит от временного среза ОС (для начала). С помощью join() поток выполняется последовательно. Например: Предположим, у вас есть два потока с вызовом метода join()

MyThread t1 = new MyThread();
MyThread t2 = new MyThread();

t1.join(); // this will be start first.
t2.join(); // this will be start only after above.

Теперь без join() можно начинать с любого из t1 и t2. Нет никакой гарантии.

synchronized statement/keyword используется для контроля потока, так что только один поток может получить доступ к этому синхронизированному методу/переменной за раз. Неважно, используете ли вы join() или нет.

Если вы используете synchronized с join(), у вас есть гарантия, что поток t1 может получить доступ только первым. Без synchronized поток t1 и t2 может получить доступ в любое время, но эти потоки начинают и умирают. последовательно из-за join().

Ответ 5

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

Кроме того, в синхронизированных блоках концепция общего блока данных более подчеркнута, чем объединение.

Ответ 6

Простой пример:

У вас есть статическая таблица строк, в которой некоторые потоки будут помещать некоторые значения. Таблица инициализируется "пустой". к таблице обращается статический индекс. Если поток помещает значение, он увеличит статический индекс.

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

если вы используете соединение по потокам, только первый связанный поток будет иметь возможность поместить значения в таблицу. Поскольку другой будет ждать, но зная, что индекс будет увеличен, они не смогут получить доступ к таблице (null исключение указателя). Таким образом, Join сделал другой поток бесполезным.

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