Синхронизированный блок не работает

Это упражнение прямо из SCJP от Kathy Seirra и Bert Bates

Синхронизация блока кода

В этом упражнении мы попытаемся синхронизировать блок кода. Внутри этого блока кода мы получим блокировку объекта, чтобы другие потоки не могли его модифицировать, пока выполняется блок кода. Мы создадим три потока, которые будут пытаться манипулировать одним и тем же объектом. Каждый поток будет выводить одну букву 100 раз, а затем увеличивать эту букву на единицу. Объект, который мы будем использовать, - StringBuffer.

Мы могли бы синхронизировать объект String, но строки не могут быть изменены один раз они создаются, поэтому мы не сможем увеличивать букву без создания нового объекта String. Конечный результат должен содержать 100 As, 100 Bs и 100 Cs в непрерывных строках.

  • Создайте класс и расширьте класс Thread.
  • Переопределить метод run() Thread. Здесь синхронизируется блок кода будет идти.
  • Чтобы наши три объекта потока могли совместно использовать один и тот же объект, нам нужно будет создать конструктор, который принимает объект StringBuffer в аргументе.
  • Синхронизированный блок кода получит блокировку на StringBuffer объект из шага 3.
  • Внутри блока выводить StringBuffer 100 раз, а затем увеличивать письмо в StringBuffer. Вы можете проверить главу 6 для StringBuffer методы, которые помогут с этим.
  • Наконец, в методе main() создайте один объект StringBuffer, используя буквы A, затем создайте три экземпляра нашего класса и запустите все три из них.

Я написал следующий класс для вышеуказанного упражнения (вместо 100 я печатаю 10 символов)

class MySyncBlockTest extends Thread {

    StringBuffer sb;

    MySyncBlockTest(StringBuffer sb) {
        this.sb=sb;
    }

    public static void main (String args[]) {
        StringBuffer sb = new StringBuffer("A");
        MySyncBlockTest t1 = new MySyncBlockTest(sb);
        MySyncBlockTest t2 = new MySyncBlockTest(sb);
        MySyncBlockTest t3 = new MySyncBlockTest(sb);
        t1.start();
        t2.start();
        t3.start();
    }

    public void run() {
        synchronized(this) {
            for (int i=0; i<10; i++) {
                System.out.print(sb);
            }
            System.out.println("");
            if (sb.charAt(0)=='A')
                sb.setCharAt(0, 'B');
            else
                sb.setCharAt(0, 'C');
        }
    }
}

Я ожидал, что вывод будет похож на следующий (10 As, 10 Bs и 10 Cs), но не получил его.

AAAAAAAAAA
BBBBBBBBBB
CCCCCCCCCC

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

AAAAAAAAAAAAAAAAAA
ABB
ACCCCCCCC

Мой вопрос: почему синхронизированный блок в методе запуска не работает?

Ответ 1

4. Синхронизированный блок кода получит блокировку объекта StringBuffer с шага 3.

Ну, ты этого не делаешь?

synchronized(this) {

Вы получаете блокировку экземпляра MySyncBlockTest, на который вызывается этот метод run(). Это... ничего не собирается делать. Нет никаких претензий к этому ресурсу; каждый Thread имеет свой экземпляр MySyncBlockTest.

Ответ 2

Вы должны заблокировать объект StringBuffer

 synchronized(sb) {
            for (int i=0; i<10; i++) {
                System.out.print(sb);
            }

Ответ 3

Я тоже смутился. Ответ, предоставленный Брайаном, правильный

synchronized (this){

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

Я написал следующую программу, чтобы продемонстрировать это:

package com.threads.chapter9;

public class TestSunchronizedBlocksUsingRunnable implements Runnable {
StringBuffer s;

@Override
public void run() {
    synchronized (this) {
        for (int i = 1; i <= 100; i++) {
            System.out.println(i);
        }
        char c = s.charAt(0);
        c++;
        s.setCharAt(0, c);
    }
}

TestSunchronizedBlocksUsingRunnable(StringBuffer s) {
    this.s = s;
}

public static void main(String[] args) {
    StringBuffer s = new StringBuffer("A");
    TestSunchronizedBlocksUsingRunnable instance1 = new TestSunchronizedBlocksUsingRunnable(s);
    Thread thread1 = new Thread(instance1);
    Thread thread2 = new Thread(instance1);
    Thread thread3 = new Thread(instance1);
    thread1.start();
    thread2.start();
    thread3.start();
}

}

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

Ответ 4

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

public class MultiThreading implements Runnable {
public static void main(String [] arg)
{
MultiThreading a=new MultiThreading(20);
Thread t0=new Thread(a);   //
Thread t1=new Thread(a);   // Multiple Threads of single object
Thread t2=new Thread(a);   // 
t0.start();
t1.start();
t2.start();
}
private int count;
MultiThreading(int a)
{this.count=a;
}
public void run()
{
synchronized(this){   
String t_name=new String("");
t_name=Thread.currentThread().getName().toString();
    for(int i=0;i<count;i++)
    if(t_name.equals("Thread-0".toString())) // mean t0
        System.out.print("A");

    else if(t_name.equals("Thread-1".toString())) // mean t1
        System.out.print("B");

    else if(t_name.equals("Thread-2".toString())) // mean t1
        System.out.print("C");
System.out.print("\n");
                  }
} // end of run
}

Ответ 5

 EXERCISE 9-2 from SCJP:
Try this For Synchronozing on stringBuffer Object.
It is giving required output.




class letterThread extends Thread
{
StringBuffer putLetter;

letterThread(StringBuffer str)
{
    this.putLetter=str;
}

public void run()
{

    synchronized (putLetter) {

        if(Thread.currentThread().getName().equals("th2"))
        {
            this.putLetter=new StringBuffer("B");
        }
        else if(Thread.currentThread().getName().equals("th3"))
        {
            this.putLetter=new StringBuffer("C");
        }

        for(int i=1;i<11;i++)
        {
            System.out.print(putLetter+"");
        }
        System.out.println();
    }
}   
}

public class Manager
{
public static void main(String args[])
{
    StringBuffer str=new StringBuffer("A");
    letterThread th1=new letterThread(str);
    letterThread th2=new letterThread(str);
    letterThread th3=new letterThread(str);

    th1.setName("th1");
    th2.setName("th2");
    th3.setName("th3");

    th1.start();
    th2.start();
    th3.start();

}
 }

Ответ 6

УПРАЖНЕНИЕ 13-2 из SCJP7

public class ThreadSyncronization extends Thread {

    StringBuffer sBuffer;
    public ThreadSyncronization(StringBuffer s,String name){
        this.sBuffer=s;
        this.setName(name);
    }
    public ThreadSyncronization(){
    }
    /**
     * @param args
     */
    public static void main(String[] args) {
        StringBuffer ch = new StringBuffer("A");
        Thread t1 = new ThreadSyncronization(ch,"first");
        Thread t2 = new ThreadSyncronization(ch,"second");
        Thread t3 = new ThreadSyncronization(ch,"third");
        t1.start();
        t2.start();
        t3.start();
    }

    public void run(){
        synchronized (sBuffer) {
            System.out.println(this.getName());
            for(int i=0;i<10;i++) {
                System.out.print(sBuffer+":"+i+" ");
                try{Thread.sleep(500);} catch(InterruptedException e) {System.out.println(e);}
            }           
            System.out.println();
            // increment char
           char c = this.sBuffer.charAt(0);
           this.sBuffer.setCharAt(0, ++c);
        }

    }

}

Ответ 7

Вы можете заменить

        if (sb.charAt(0)=='A')
            sb.setCharAt(0, 'B');
        else
            sb.setCharAt(0, 'C');

с

sb.setCharAt(0, (char) (sb.charAt(0) + 1));

Ответ 8

пакет com.practice.ThreadPackage;

класс ThreadParent расширяет поток {

StringBuffer data;

public void run() {
    synchronized (this.data) {

        System.out.println(this.getName());

        for (int i = 0; i < 10; i++) {

            System.out.print(this.data.toString());
        }

        System.out.println();
        this.data.setCharAt(0, ((char) (this.data.charAt(0) + 1)));
    }
}

ThreadParent(StringBuffer obj) {
    this.data = obj;
}

}

public class ThreadClass {   public static void main (String args []) {

    StringBuffer str = new StringBuffer("A");
    ThreadParent obj = new ThreadParent(str);
    ThreadParent obj1 = new ThreadParent(str);
    ThreadParent obj2 = new ThreadParent(str);
    obj.setName("Thread1");
    obj1.setName("Thread2");
    obj2.setName("Thread3");
    obj.start();
    obj1.start();
    obj2.start();

}

}