同步块不起作用

13

这个练习来自Kathy Seirra和Bert Bates的SCJP教材。

同步一个代码块:我们将尝试同步一段代码块。在该代码块中,我们将锁定一个对象,以便在执行代码块时其他线程无法修改它。我们将创建三个线程,它们都会尝试操作同一个对象。每个线程将输出一个字母100次,然后将该字母加1。我们将使用StringBuffer作为对象。

我们可以同步一个String对象,但是一旦创建了字符串,就无法修改它们,因此如果不生成新的String对象,我们将无法增加字母。最终输出应该是连续的100个A,100个B和100个C。

  1. 创建一个类并扩展Thread类。
  2. 覆盖Thread的run()方法。这是同步代码块的位置。
  3. 为了使我们的三个线程对象共享同一个对象,我们需要创建一个构造函数,该构造函数接受一个StringBuffer对象作为参数。
  4. 同步代码块将从第3步获取StringBuffer对象上的锁。
  5. 在该块内,将StringBuffer输出100次,然后增加StringBuffer中的字母。您可以查看第6章有关StringBuffer方法的帮助。
  6. 最后,在main()方法中,使用字母A创建一个StringBuffer对象,然后创建三个我们的类实例并启动它们所有三个。

我已经为上述练习编写了以下类(我将打印10个字符而不是100个)。

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个A,10个B和10个C),但实际上并没有得到。

AAAAAAAAAA
BBBBBBBBBB
CCCCCCCCCC

相反,我得到了不同的输出,因为在其他线程完成之前,这三个线程有机会进入循环。

AAAAAAAAAAAAAAAAAA
ABB
ACCCCCCCC

我的问题是为什么run方法中的同步块没有起作用?


请降级的人发布说明原因。 - Maxim Shoustin
为什么我得到了-1分?这是一个真诚的问题! - atsurti
请在可以使用StringBuilder时不要使用StringBuffer。StringBuffer已经是一个遗留类近10年了。 - Peter Lawrey
1
谢谢Peter,我知道,只是想遵循这个练习。 - atsurti
8个回答

22

4. 同步代码块会在步骤3中获取StringBuffer对象的锁。

好吧,你没有这样做,对吧?

synchronized(this) {

您正在获取对 MySyncBlockTest 实例的锁,该锁在调用 run()方法的实例上。那...不会有任何作用。这个资源没有争用;每个Thread 都有自己的 MySyncBlockTest 实例。


2
知道了!我已经把它改成了synchronized(sb),现在它可以工作了! - atsurti
不确定为什么这样做可以,但是拥有一个全局类变量就不需要了。 synchronized(lock) { ...``` - Alex
@Alex 这是因为你的变量是全局实例变量,你必须将锁定静态化,否则它与使用 (this) 相同。 - CausingUnderflowsEverywhere

4

你应该锁定StringBuffer对象。

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

2
我也感到困惑。Brian提供的答案是正确的。
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();
}

}

上述代码将显示相同的输出,但场景完全不同。因此,在同步块中使用的内容非常关键。


1
您想要的输出,可以通过单个对象的多个线程实现,尝试使用此方法。
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
}

-1

SCJP7中的练习13-2

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);
        }

    }

}

-1
 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();

}
 }

-2
你可以替换

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

使用

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

-4

包 com.practice.ThreadPackage;

class ThreadParent extends Thread {

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;
}

}

公共类线程类 { 公共静态无返回值主函数(字符串[] 参数) {

    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();

}

}


请解释你的答案,而不仅仅是倾倒代码。为什么这是一个好答案?它如何修复问题? - Gray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接